Skip to content

Commit e8dda39

Browse files
authored
Merge pull request #30 from taskiq-python/fix-reuse-stream-if-it-already-exists
fix: reuse stream if it already exists
2 parents 45c07e0 + b7f6351 commit e8dda39

2 files changed

Lines changed: 72 additions & 1 deletion

File tree

‎taskiq_nats/broker.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from nats.errors import TimeoutError as NatsTimeoutError
88
from nats.js import JetStreamContext
99
from nats.js.api import ConsumerConfig, StreamConfig
10+
from nats.js.errors import BadRequestError
1011
from taskiq import AckableMessage, AsyncBroker, AsyncResultBackend, BrokerMessage
1112

1213
_T = typing.TypeVar("_T") # (Too short)
@@ -152,9 +153,22 @@ async def startup(self) -> None:
152153
self.stream_config.name = self.stream_name
153154
if not self.stream_config.subjects:
154155
self.stream_config.subjects = [self.subject]
155-
await self.js.add_stream(config=self.stream_config)
156+
await self._add_or_reuse_stream()
156157
await self._startup_consumer()
157158

159+
async def _add_or_reuse_stream(self) -> None:
160+
"""Create a stream or reuse an already existing one."""
161+
try:
162+
await self.js.add_stream(config=self.stream_config)
163+
except BadRequestError as exc:
164+
if exc.err_code != 10058:
165+
raise
166+
logger.info(
167+
"Stream %s already exists with a different configuration. "
168+
"Reusing the existing stream.",
169+
self.stream_config.name,
170+
)
171+
158172
async def shutdown(self) -> None:
159173
"""Close connections to NATS."""
160174
await self.client.close()

‎tests/test_jetstream.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import asyncio
22
import uuid
3+
from unittest.mock import AsyncMock, MagicMock
34

5+
import pytest
6+
from nats.js import JetStreamContext
7+
from nats.js.api import RetentionPolicy, StreamConfig
8+
from nats.js.errors import BadRequestError
49
from taskiq import AckableMessage, BrokerMessage
510

611
from taskiq_nats import PullBasedJetStreamBroker, PushBasedJetStreamBroker
@@ -87,3 +92,55 @@ async def test_pull_based_broker_success(
8792
broker.stream_name,
8893
)
8994
await broker.shutdown()
95+
96+
97+
async def test_broker_reuses_existing_stream(
98+
nats_urls: list[str],
99+
nats_subject: str,
100+
nats_jetstream: JetStreamContext,
101+
) -> None:
102+
stream_name = uuid.uuid4().hex
103+
await nats_jetstream.add_stream(
104+
config=StreamConfig(
105+
name=stream_name,
106+
subjects=[nats_subject],
107+
retention=RetentionPolicy.WORK_QUEUE,
108+
max_msgs=1000,
109+
),
110+
)
111+
112+
broker = PullBasedJetStreamBroker(
113+
servers=nats_urls,
114+
subject=nats_subject,
115+
stream_name=stream_name,
116+
)
117+
await broker.startup()
118+
119+
stream_info = await broker.js.stream_info(stream_name)
120+
assert stream_info.config.retention == RetentionPolicy.WORK_QUEUE
121+
assert stream_info.config.max_msgs == 1000
122+
123+
await broker.js.delete_consumer(
124+
stream=stream_name,
125+
consumer=broker.durable,
126+
)
127+
await broker.js.delete_stream(stream_name)
128+
await broker.shutdown()
129+
130+
131+
async def test_broker_startup_reraises_unknown_stream_error(
132+
nats_urls: list[str],
133+
nats_subject: str,
134+
) -> None:
135+
broker = PullBasedJetStreamBroker(
136+
servers=nats_urls,
137+
subject=nats_subject,
138+
stream_name=uuid.uuid4().hex,
139+
)
140+
broker.js = MagicMock()
141+
broker.js.add_stream = AsyncMock(
142+
side_effect=BadRequestError(code=400, err_code=10052),
143+
)
144+
145+
with pytest.raises(BadRequestError):
146+
await broker._add_or_reuse_stream()

0 commit comments

Comments
 (0)