@@ -138,6 +138,49 @@ async def callee(tx: ydb.aio.QueryTxContext):
138138 msg = await wait_for (reader .receive_message (), DEFAULT_TIMEOUT )
139139 assert msg .data .decode () == "123"
140140
141+ async def test_tx_commit_after_reconnect_does_not_commit_stale_offsets (
142+ self , driver : ydb .aio .Driver , topic_with_messages , topic_consumer
143+ ):
144+ async with driver .topic_client .reader (topic_with_messages , topic_consumer ) as reader :
145+ async with ydb .aio .QuerySessionPool (driver ) as pool :
146+ async with pool .checkout () as session :
147+ tx = session .transaction ()
148+ await tx .begin ()
149+
150+ batch = await wait_for (reader .receive_batch_with_tx (tx , max_messages = 1 ), DEFAULT_TIMEOUT )
151+ assert batch .messages [0 ].data .decode () == "123"
152+
153+ reconnector = reader ._reconnector
154+ old_stream = reconnector ._stream_reader
155+
156+ with mock .patch .object (
157+ reconnector ,
158+ "_do_commit_batches_with_tx_call" ,
159+ wraps = reconnector ._do_commit_batches_with_tx_call ,
160+ ) as update_offsets_call :
161+ # Force a reconnect between receive_batch_with_tx() and commit, so the
162+ # batch belongs to a partition session that no longer exists.
163+ old_stream ._set_first_error (ydb .issues .ConnectionLost ("forced reconnect" ))
164+ for _ in range (100 ):
165+ await asyncio .sleep (0.05 )
166+ current = reconnector ._stream_reader
167+ if current is not None and current is not old_stream and current ._started :
168+ break
169+ assert reconnector ._stream_reader is not old_stream
170+
171+ # Committing the stale batch must fail loudly instead of silently
172+ # sending a gapped UpdateOffsetsInTransaction for the dead session.
173+ with pytest .raises (ydb .Error ):
174+ await tx .commit ()
175+
176+ update_offsets_call .assert_not_called ()
177+
178+ assert len (reader ._reconnector ._tx_to_batches_map ) == 0
179+
180+ # The consumer offset must not have advanced: the message is read again.
181+ msg = await wait_for (reader .receive_message (), DEFAULT_TIMEOUT )
182+ assert msg .data .decode () == "123"
183+
141184
142185class TestTopicTransactionalReaderSync :
143186 def test_commit (self , driver_sync : ydb .Driver , topic_with_messages , topic_consumer ):
0 commit comments