11import asyncio
22from asyncio import wait_for
33import pytest
4+ from unittest import mock
45import ydb
56
7+ DEFAULT_TIMEOUT = 0.1
8+ DEFAULT_RETRY_SETTINGS = ydb .RetrySettings (max_retries = 1 )
9+
610
7- @pytest .mark .skip ("Not implemented yet." )
811@pytest .mark .asyncio
912class TestTopicTransactionalReader :
1013 async def test_commit (self , driver : ydb .aio .Driver , topic_with_messages , topic_consumer ):
11- async with driver . topic_client . reader ( topic_with_messages , topic_consumer ) as reader :
12- async with ydb . aio . QuerySessionPool ( driver ) as pool :
14+ async with ydb . aio . QuerySessionPool ( driver ) as pool :
15+ async with driver . topic_client . reader ( topic_with_messages , topic_consumer ) as reader :
1316
1417 async def callee (tx : ydb .aio .QueryTxContext ):
15- batch = await wait_for (reader .receive_batch_with_tx (tx , max_messages = 1 ), 1 )
16- assert len (batch ) == 1
17- assert batch [0 ].data .decode () == "123"
18+ batch = await wait_for (reader .receive_batch_with_tx (tx , max_messages = 1 ), DEFAULT_TIMEOUT )
19+ assert len (batch . messages ) == 1
20+ assert batch . messages [0 ].data .decode () == "123"
1821
19- await pool .retry_tx_async (callee )
22+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
2023
21- msg = await wait_for (reader .receive_message (), 1 )
24+ async with driver .topic_client .reader (topic_with_messages , topic_consumer ) as reader :
25+ msg = await wait_for (reader .receive_message (), DEFAULT_TIMEOUT )
2226 assert msg .data .decode () == "456"
2327
2428 async def test_rollback (self , driver : ydb .aio .Driver , topic_with_messages , topic_consumer ):
2529 async with driver .topic_client .reader (topic_with_messages , topic_consumer ) as reader :
2630 async with ydb .aio .QuerySessionPool (driver ) as pool :
2731
2832 async def callee (tx : ydb .aio .QueryTxContext ):
29- batch = await wait_for (reader .receive_batch_with_tx (tx , max_messages = 1 ), 1 )
30- assert len (batch ) == 1
31- assert batch [0 ].data .decode () == "123"
33+ batch = await wait_for (reader .receive_batch_with_tx (tx , max_messages = 1 ), DEFAULT_TIMEOUT )
34+ assert len (batch . messages ) == 1
35+ assert batch . messages [0 ].data .decode () == "123"
3236
3337 await tx .rollback ()
3438
35- await pool .retry_tx_async (callee )
39+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
3640
37- msg = await wait_for (reader .receive_message (), 1 )
41+ msg = await wait_for (reader .receive_message (), DEFAULT_TIMEOUT )
3842 assert msg .data .decode () == "123"
3943
44+ async def test_tx_failed_if_update_offsets_call_failed (
45+ self , driver : ydb .aio .Driver , topic_with_messages , topic_consumer
46+ ):
47+ async with driver .topic_client .reader (topic_with_messages , topic_consumer ) as reader :
48+ async with ydb .aio .QuerySessionPool (driver ) as pool :
49+ with mock .patch .object (
50+ reader ._reconnector ,
51+ "_do_commit_batches_with_tx_call" ,
52+ side_effect = ydb .Error ("Update offsets in tx failed" ),
53+ ):
54+
55+ async def callee (tx : ydb .aio .QueryTxContext ):
56+ batch = await wait_for (reader .receive_batch_with_tx (tx , max_messages = 1 ), DEFAULT_TIMEOUT )
57+ assert len (batch .messages ) == 1
58+ assert batch .messages [0 ].data .decode () == "123"
59+
60+ with pytest .raises (ydb .Error ):
61+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
62+
63+ msg = await wait_for (reader .receive_message (), DEFAULT_TIMEOUT )
64+ assert msg .data .decode () == "123"
65+
4066
41- # @pytest.mark.skip("Not implemented yet.")
4267class TestTopicTransactionalWriter :
4368 async def test_commit (self , driver : ydb .aio .Driver , topic_path , topic_reader : ydb .TopicReaderAsyncIO ):
4469 async with ydb .aio .QuerySessionPool (driver ) as pool :
@@ -47,7 +72,7 @@ async def callee(tx: ydb.aio.QueryTxContext):
4772 tx_writer = driver .topic_client .tx_writer (tx , topic_path )
4873 await tx_writer .write (ydb .TopicWriterMessage (data = "123" .encode ()))
4974
50- await pool .retry_tx_async (callee )
75+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
5176
5277 msg = await wait_for (topic_reader .receive_message (), 0.1 )
5378 assert msg .data .decode () == "123"
@@ -61,7 +86,92 @@ async def callee(tx: ydb.aio.QueryTxContext):
6186
6287 await tx .rollback ()
6388
64- await pool .retry_tx_async (callee )
89+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
90+
91+ with pytest .raises (asyncio .TimeoutError ):
92+ await wait_for (topic_reader .receive_message (), 0.1 )
93+
94+ async def test_no_msg_written_in_error_case (
95+ self , driver : ydb .aio .Driver , topic_path , topic_reader : ydb .TopicReaderAsyncIO
96+ ):
97+ async with ydb .aio .QuerySessionPool (driver ) as pool :
98+
99+ async def callee (tx : ydb .aio .QueryTxContext ):
100+ tx_writer = driver .topic_client .tx_writer (tx , topic_path )
101+ await tx_writer .write (ydb .TopicWriterMessage (data = "123" .encode ()))
102+
103+ raise BaseException ("error" )
104+
105+ with pytest .raises (BaseException ):
106+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
65107
66108 with pytest .raises (asyncio .TimeoutError ):
67109 await wait_for (topic_reader .receive_message (), 0.1 )
110+
111+ async def test_msg_written_exactly_once_with_retries (
112+ self , driver : ydb .aio .Driver , topic_path , topic_reader : ydb .TopicReaderAsyncIO
113+ ):
114+ error_raised = False
115+ async with ydb .aio .QuerySessionPool (driver ) as pool :
116+
117+ async def callee (tx : ydb .aio .QueryTxContext ):
118+ nonlocal error_raised
119+ tx_writer = driver .topic_client .tx_writer (tx , topic_path )
120+ await tx_writer .write (ydb .TopicWriterMessage (data = "123" .encode ()))
121+
122+ if not error_raised :
123+ error_raised = True
124+ raise ydb .issues .Unavailable ("some retriable error" )
125+
126+ await pool .retry_tx_async (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
127+
128+ msg = await wait_for (topic_reader .receive_message (), 0.1 )
129+ assert msg .data .decode () == "123"
130+
131+ with pytest .raises (asyncio .TimeoutError ):
132+ await wait_for (topic_reader .receive_message (), 0.1 )
133+
134+
135+ class TestTopicTransactionalWriterSync :
136+ def test_commit (self , driver_sync : ydb .Driver , topic_path , topic_reader_sync : ydb .TopicReader ):
137+ with ydb .QuerySessionPool (driver_sync ) as pool :
138+
139+ def callee (tx : ydb .QueryTxContext ):
140+ tx_writer = driver_sync .topic_client .tx_writer (tx , topic_path )
141+ tx_writer .write (ydb .TopicWriterMessage (data = "123" .encode ()))
142+
143+ pool .retry_tx_sync (callee , retry_settings = ydb .RetrySettings (max_retries = 1 ))
144+
145+ msg = topic_reader_sync .receive_message (timeout = 0.1 )
146+ assert msg .data .decode () == "123"
147+
148+ def test_rollback (self , driver_sync : ydb .aio .Driver , topic_path , topic_reader_sync : ydb .TopicReader ):
149+ with ydb .QuerySessionPool (driver_sync ) as pool :
150+
151+ def callee (tx : ydb .QueryTxContext ):
152+ tx_writer = driver_sync .topic_client .tx_writer (tx , topic_path )
153+ tx_writer .write (ydb .TopicWriterMessage (data = "123" .encode ()))
154+
155+ tx .rollback ()
156+
157+ pool .retry_tx_sync (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
158+
159+ with pytest .raises (TimeoutError ):
160+ topic_reader_sync .receive_message (timeout = 0.1 )
161+
162+ def test_no_msg_written_in_error_case (
163+ self , driver_sync : ydb .Driver , topic_path , topic_reader_sync : ydb .TopicReaderAsyncIO
164+ ):
165+ with ydb .QuerySessionPool (driver_sync ) as pool :
166+
167+ def callee (tx : ydb .QueryTxContext ):
168+ tx_writer = driver_sync .topic_client .tx_writer (tx , topic_path )
169+ tx_writer .write (ydb .TopicWriterMessage (data = "123" .encode ()))
170+
171+ raise BaseException ("error" )
172+
173+ with pytest .raises (BaseException ):
174+ pool .retry_tx_sync (callee , retry_settings = DEFAULT_RETRY_SETTINGS )
175+
176+ with pytest .raises (TimeoutError ):
177+ topic_reader_sync .receive_message (timeout = 0.1 )
0 commit comments