Skip to content

Commit 8177d7f

Browse files
committed
Fixed hungup topic reader on unknown codec
1 parent f6f591e commit 8177d7f

File tree

5 files changed

+47
-17
lines changed

5 files changed

+47
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* Fixed hungup topic reader on unknown codec
2+
13
## 3.11.1 ##
24
* fixed unexpected require requests module on import
35

tests/topics/test_topic_reader.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ def decode(b: bytes):
7474
batch = await reader.receive_batch()
7575
assert batch.messages[0].data.decode() == "123"
7676

77+
async def test_error_unknown_codec(self, driver, topic_path, topic_consumer):
78+
codec = 10001
79+
80+
def encode(b: bytes):
81+
return bytes(reversed(b))
82+
83+
async with driver.topic_client.writer(topic_path, codec=codec, encoders={codec: encode}) as writer:
84+
await writer.write("123")
85+
86+
async with driver.topic_client.reader(topic_path, topic_consumer) as reader:
87+
with pytest.raises(ydb.TopicReaderUnexpectedCodecError):
88+
await asyncio.wait_for(reader.receive_batch(), timeout=5)
89+
7790
async def test_read_from_two_topics(self, driver, topic_path, topic2_path, topic_consumer):
7891
async with driver.topic_client.writer(topic_path) as writer:
7992
await writer.write("1")

ydb/_topic_reader/topic_reader_asyncio.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from collections import deque
99
from typing import Optional, Set, Dict, Union, Callable
1010

11+
import ydb
1112
from .. import _apis, issues
1213
from .._utilities import AtomicCounter
1314
from ..aio import Driver
@@ -35,7 +36,7 @@ class TopicReaderError(YdbError):
3536
pass
3637

3738

38-
class TopicReaderUnexpectedCodec(YdbError):
39+
class PublicTopicReaderUnexpectedCodecError(YdbError):
3940
pass
4041

4142

@@ -222,9 +223,7 @@ def commit(self, batch: datatypes.ICommittable) -> datatypes.PartitionSession.Co
222223

223224
async def close(self, flush: bool):
224225
if self._stream_reader:
225-
if flush:
226-
await self.flush()
227-
await self._stream_reader.close()
226+
await self._stream_reader.close(flush)
228227
for task in self._background_tasks:
229228
task.cancel()
230229

@@ -339,9 +338,12 @@ async def _start(self, stream: IGrpcWrapperAsyncIO, init_message: StreamReadMess
339338
self._update_token_event.set()
340339

341340
self._background_tasks.add(asyncio.create_task(self._read_messages_loop(), name="read_messages_loop"))
342-
self._background_tasks.add(asyncio.create_task(self._decode_batches_loop()))
341+
self._background_tasks.add(asyncio.create_task(self._decode_batches_loop(), name="decode_batches"))
343342
if self._get_token_function:
344343
self._background_tasks.add(asyncio.create_task(self._update_token_loop(), name="update_token_loop"))
344+
self._background_tasks.add(
345+
asyncio.create_task(self._handle_background_errors(), name="handle_background_errors")
346+
)
345347

346348
async def wait_error(self):
347349
raise await self._first_error
@@ -411,6 +413,17 @@ def commit(self, batch: datatypes.ICommittable) -> datatypes.PartitionSession.Co
411413

412414
return waiter
413415

416+
async def _handle_background_errors(self):
417+
done, _ = await asyncio.wait(self._background_tasks, return_when=asyncio.FIRST_EXCEPTION)
418+
for f in done:
419+
f = f # type: asyncio.Future
420+
err = f.exception()
421+
if not isinstance(err, ydb.Error):
422+
old_err = err
423+
err = ydb.Error("Background process failed unexpected")
424+
err.__cause__ = old_err
425+
self._set_first_error(err)
426+
414427
async def _read_messages_loop(self):
415428
try:
416429
self._stream.write(
@@ -602,7 +615,7 @@ async def _decode_batch_inplace(self, batch):
602615
try:
603616
decode_func = self._decoders[batch._codec]
604617
except KeyError:
605-
raise TopicReaderUnexpectedCodec("Receive message with unexpected codec: %s" % batch._codec)
618+
raise PublicTopicReaderUnexpectedCodecError("Receive message with unexpected codec: %s" % batch._codec)
606619

607620
decode_data_futures = []
608621
for message in batch.messages:
@@ -628,22 +641,22 @@ def _get_first_error(self) -> Optional[YdbError]:
628641
return self._first_error.result()
629642

630643
async def flush(self):
631-
if self._closed:
632-
raise RuntimeError("Flush on closed Stream")
633-
634644
futures = []
635645
for session in self._partition_sessions.values():
636646
futures.extend(w.future for w in session._ack_waiters)
637647

638648
if futures:
639649
await asyncio.wait(futures)
640650

641-
async def close(self):
651+
async def close(self, flush: bool):
642652
if self._closed:
643653
return
644654

645655
self._closed = True
646656

657+
if flush:
658+
await self.flush()
659+
647660
self._set_first_error(TopicReaderStreamClosedError())
648661
self._state_changed.set()
649662
self._stream.close()

ydb/_topic_reader/topic_reader_asyncio_test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,14 @@ async def stream_reader(self, stream_reader_started: ReaderStream):
183183
yield stream_reader_started
184184

185185
assert stream_reader_started._get_first_error() is None
186-
await stream_reader_started.close()
186+
await stream_reader_started.close(False)
187187

188188
@pytest.fixture()
189189
async def stream_reader_finish_with_error(self, stream_reader_started: ReaderStream):
190190
yield stream_reader_started
191191

192192
assert stream_reader_started._get_first_error() is not None
193-
await stream_reader_started.close()
193+
await stream_reader_started.close(False)
194194

195195
@staticmethod
196196
def create_message(
@@ -372,7 +372,7 @@ async def test_close_ack_waiters_when_close_stream_reader(
372372
self, stream_reader_started: ReaderStream, partition_session
373373
):
374374
waiter = partition_session.add_waiter(self.partition_session_committed_offset + 1)
375-
await wait_for_fast(stream_reader_started.close())
375+
await wait_for_fast(stream_reader_started.close(False))
376376

377377
with pytest.raises(topic_reader_asyncio.PublicTopicReaderPartitionExpiredError):
378378
waiter.future.result()
@@ -402,7 +402,7 @@ async def test_flush(self, stream, stream_reader_started: ReaderStream, partitio
402402
# don't raises
403403
assert waiter.future.result() is None
404404

405-
await wait_for_fast(stream_reader_started.close())
405+
await wait_for_fast(stream_reader_started.close(False))
406406

407407
async def test_commit_ranges_for_received_messages(
408408
self, stream, stream_reader_started: ReaderStream, partition_session
@@ -422,7 +422,7 @@ async def test_commit_ranges_for_received_messages(
422422
received = stream_reader_started.receive_batch_nowait().messages
423423
assert received == [m2]
424424

425-
await stream_reader_started.close()
425+
await stream_reader_started.close(False)
426426

427427
# noinspection PyTypeChecker
428428
@pytest.mark.parametrize(
@@ -613,7 +613,7 @@ async def test_init_reader(self, stream, default_reader_settings):
613613
)
614614

615615
assert reader._session_id == "test"
616-
await reader.close()
616+
await reader.close(False)
617617

618618
async def test_start_partition(
619619
self,
@@ -1230,7 +1230,7 @@ async def test_update_token(self, stream):
12301230
got = await wait_for_fast(stream.from_client.get())
12311231
assert expected == got
12321232

1233-
await reader.close()
1233+
await reader.close(False)
12341234

12351235
async def test_read_unknown_message(self, stream, stream_reader, caplog):
12361236
class TestMessage:

ydb/topic.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"TopicReaderMessage",
1616
"TopicReaderSelector",
1717
"TopicReaderSettings",
18+
"TopicReaderUnexpectedCodecError",
1819
"TopicReaderPartitionExpiredError",
1920
"TopicStatWindow",
2021
"TopicWriteResult",
@@ -49,6 +50,7 @@
4950
from ._topic_reader.topic_reader_asyncio import (
5051
PublicAsyncIOReader as TopicReaderAsyncIO,
5152
PublicTopicReaderPartitionExpiredError as TopicReaderPartitionExpiredError,
53+
PublicTopicReaderUnexpectedCodecError as TopicReaderUnexpectedCodecError,
5254
)
5355

5456
from ._topic_writer.topic_writer import ( # noqa: F401

0 commit comments

Comments
 (0)