nucliadb-utils 6.1.0.post2589__py3-none-any.whl → 6.1.0.post2602__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nucliadb-utils might be problematic. Click here for more details.
- nucliadb_utils/nats.py +112 -36
- nucliadb_utils/utilities.py +6 -0
- {nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/METADATA +3 -3
- {nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/RECORD +7 -7
- {nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/WHEEL +0 -0
- {nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/top_level.txt +0 -0
- {nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/zip-safe +0 -0
nucliadb_utils/nats.py
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
|
20
20
|
import asyncio
|
21
21
|
import logging
|
22
|
+
import sys
|
22
23
|
import time
|
23
24
|
from functools import cached_property
|
24
25
|
from typing import Any, Awaitable, Callable, Optional, Union
|
@@ -126,6 +127,9 @@ class NatsConnectionManager:
|
|
126
127
|
self._lock = asyncio.Lock()
|
127
128
|
self._healthy = True
|
128
129
|
self._last_unhealthy: Optional[float] = None
|
130
|
+
self._needs_reconnection = False
|
131
|
+
self._reconnect_task: Optional[asyncio.Task] = None
|
132
|
+
self._expected_subscriptions: set[str] = set()
|
129
133
|
|
130
134
|
def healthy(self) -> bool:
|
131
135
|
if not self._healthy:
|
@@ -159,8 +163,23 @@ class NatsConnectionManager:
|
|
159
163
|
async with self._lock:
|
160
164
|
self._nc = await nats.connect(**options)
|
161
165
|
|
166
|
+
self._expected_subscription_task = asyncio.create_task(self._verify_expected_subscriptions())
|
167
|
+
|
162
168
|
async def finalize(self):
|
163
169
|
async with self._lock:
|
170
|
+
if self._reconnect_task:
|
171
|
+
self._reconnect_task.cancel()
|
172
|
+
try:
|
173
|
+
await self._reconnect_task
|
174
|
+
except asyncio.CancelledError:
|
175
|
+
pass
|
176
|
+
|
177
|
+
self._expected_subscription_task.cancel()
|
178
|
+
try:
|
179
|
+
await self._expected_subscription_task
|
180
|
+
except asyncio.CancelledError:
|
181
|
+
pass
|
182
|
+
|
164
183
|
# Finalize push subscriptions
|
165
184
|
for sub, _ in self._subscriptions:
|
166
185
|
try:
|
@@ -199,42 +218,75 @@ class NatsConnectionManager:
|
|
199
218
|
logger.warning(
|
200
219
|
f"Reconnected to NATS {self._nc.connected_url.netloc}. Attempting to re-subscribe."
|
201
220
|
)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
221
|
+
# This callback may be interrupted by NATS. In order to avoid being interrupted, we spawn our own task to do
|
222
|
+
# the reconnection. If we are interrupted we could lose subscriptions
|
223
|
+
self._needs_reconnection = True
|
224
|
+
if self._reconnect_task is None:
|
225
|
+
self._reconnect_task = asyncio.create_task(self._reconnect())
|
226
|
+
|
227
|
+
async def _reconnect(self):
|
228
|
+
tries = 0
|
229
|
+
# Loop in case we receive another reconnection request while one is ongoing
|
230
|
+
while self._needs_reconnection:
|
231
|
+
if tries > 5:
|
232
|
+
logger.error(
|
233
|
+
"Too many consecutive reconnection to NATS. Something might be wrong. Exiting."
|
234
|
+
)
|
235
|
+
sys.exit(1)
|
236
|
+
|
237
|
+
try:
|
238
|
+
self._needs_reconnection = False
|
239
|
+
|
240
|
+
async with self._lock:
|
241
|
+
existing_subs = self._subscriptions
|
242
|
+
self._subscriptions = []
|
243
|
+
for sub, recon_callback in existing_subs:
|
244
|
+
try:
|
245
|
+
await sub.drain()
|
246
|
+
await recon_callback()
|
247
|
+
except Exception:
|
248
|
+
logger.exception(
|
249
|
+
f"Error resubscribing to {sub.subject} on {self._nc.connected_url.netloc}"
|
250
|
+
)
|
251
|
+
# should force exit here to restart the service
|
252
|
+
self._healthy = False
|
253
|
+
raise
|
254
|
+
|
255
|
+
existing_pull_subs = self._pull_subscriptions
|
256
|
+
self._pull_subscriptions = []
|
257
|
+
for pull_sub, task, recon_callback, cancelled in existing_pull_subs:
|
258
|
+
cancelled.set()
|
259
|
+
task.cancel()
|
260
|
+
try:
|
261
|
+
await task
|
262
|
+
except asyncio.CancelledError:
|
263
|
+
pass
|
264
|
+
try:
|
265
|
+
await pull_sub.unsubscribe()
|
266
|
+
await recon_callback()
|
267
|
+
except Exception:
|
268
|
+
logger.exception(
|
269
|
+
f"Error resubscribing to pull subscription on {self._nc.connected_url.netloc}"
|
270
|
+
)
|
271
|
+
# should force exit here to restart the service
|
272
|
+
self._healthy = False
|
273
|
+
raise
|
274
|
+
self._healthy = True
|
275
|
+
self._last_unhealthy = None # reset the last unhealthy time
|
276
|
+
except Exception:
|
277
|
+
if self._needs_reconnection:
|
278
|
+
logger.warning("Error reconnecting to NATS, will retry")
|
279
|
+
else:
|
280
|
+
logger.exception("Error reconnecting to NATS. Exiting.")
|
281
|
+
sys.exit(1)
|
282
|
+
|
283
|
+
if self._needs_reconnection:
|
284
|
+
logger.warning(
|
285
|
+
"While reconnecting to NATS subscriptions, a reconnect request was received. Reconnecting again.",
|
286
|
+
)
|
287
|
+
tries += 1
|
288
|
+
|
289
|
+
self._reconnect_task = None
|
238
290
|
|
239
291
|
async def error_cb(self, e): # pragma: no cover
|
240
292
|
logger.error(f"There was an error on consumer: {e}", exc_info=e)
|
@@ -314,6 +366,10 @@ class NatsConnectionManager:
|
|
314
366
|
|
315
367
|
task = asyncio.create_task(consume(psub, subject), name=f"pull_subscribe_{subject}")
|
316
368
|
self._pull_subscriptions.append((psub, task, subscription_lost_cb, cancelled))
|
369
|
+
|
370
|
+
if durable:
|
371
|
+
self._expected_subscriptions.add(durable)
|
372
|
+
|
317
373
|
return psub
|
318
374
|
|
319
375
|
async def _remove_subscription(
|
@@ -340,3 +396,23 @@ class NatsConnectionManager:
|
|
340
396
|
async def unsubscribe(self, subscription: Union[Subscription, JetStreamContext.PullSubscription]):
|
341
397
|
await subscription.unsubscribe()
|
342
398
|
await self._remove_subscription(subscription)
|
399
|
+
|
400
|
+
if isinstance(subscription, JetStreamContext.PullSubscription):
|
401
|
+
self._expected_subscriptions.remove(subscription._consumer) # type: ignore
|
402
|
+
|
403
|
+
async def _verify_expected_subscriptions(self):
|
404
|
+
failures = 0
|
405
|
+
while True:
|
406
|
+
await asyncio.sleep(30)
|
407
|
+
|
408
|
+
existing_subs = set(sub._consumer for sub, _, _, _ in self._pull_subscriptions)
|
409
|
+
missing_subs = self._expected_subscriptions - existing_subs
|
410
|
+
if missing_subs:
|
411
|
+
logger.warning(f"Some NATS subscriptions are missing {missing_subs}")
|
412
|
+
failures += 1
|
413
|
+
else:
|
414
|
+
failures = 0
|
415
|
+
|
416
|
+
if failures >= 3:
|
417
|
+
logger.warning("Some NATS subscriptions are missing for too long, exiting")
|
418
|
+
sys.exit(1)
|
nucliadb_utils/utilities.py
CHANGED
@@ -92,6 +92,8 @@ def get_utility(ident: Union[Utility, str]):
|
|
92
92
|
|
93
93
|
|
94
94
|
def set_utility(ident: Union[Utility, str], util: Any):
|
95
|
+
if ident in MAIN:
|
96
|
+
logger.warning(f"Overwriting previously set utility {ident}: {MAIN[ident]} with {util}")
|
95
97
|
MAIN[ident] = util
|
96
98
|
|
97
99
|
|
@@ -237,6 +239,10 @@ def get_ingest() -> WriterStub:
|
|
237
239
|
|
238
240
|
|
239
241
|
def start_partitioning_utility() -> PartitionUtility:
|
242
|
+
util = get_utility(Utility.PARTITION)
|
243
|
+
if util is not None:
|
244
|
+
return util
|
245
|
+
|
240
246
|
util = PartitionUtility(
|
241
247
|
partitions=nuclia_settings.nuclia_partitions,
|
242
248
|
seed=nuclia_settings.nuclia_hash_seed,
|
{nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nucliadb_utils
|
3
|
-
Version: 6.1.0.
|
3
|
+
Version: 6.1.0.post2602
|
4
4
|
Home-page: https://nuclia.com
|
5
5
|
License: BSD
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
@@ -24,8 +24,8 @@ Requires-Dist: PyNaCl
|
|
24
24
|
Requires-Dist: pyjwt>=2.4.0
|
25
25
|
Requires-Dist: memorylru>=1.1.2
|
26
26
|
Requires-Dist: mrflagly>=0.2.9
|
27
|
-
Requires-Dist: nucliadb-protos>=6.1.0.
|
28
|
-
Requires-Dist: nucliadb-telemetry>=6.1.0.
|
27
|
+
Requires-Dist: nucliadb-protos>=6.1.0.post2602
|
28
|
+
Requires-Dist: nucliadb-telemetry>=6.1.0.post2602
|
29
29
|
Provides-Extra: cache
|
30
30
|
Requires-Dist: redis>=4.3.4; extra == "cache"
|
31
31
|
Requires-Dist: orjson>=3.6.7; extra == "cache"
|
@@ -8,7 +8,7 @@ nucliadb_utils/featureflagging.py,sha256=dKp8u4g52KTcupKFJkdGYk3Z2pGQdi_q7IaoY_G
|
|
8
8
|
nucliadb_utils/grpc.py,sha256=apu0uePnkGHCAT7GRQ9YZfRYyFj26kJ440i8jitbM3U,3314
|
9
9
|
nucliadb_utils/helpers.py,sha256=nPw8yod3hP-pxq80VF8QC36s7ygSg0dBUdfI-LatvCs,1600
|
10
10
|
nucliadb_utils/indexing.py,sha256=Luaqcar3CySpdYOFp6Q9Fyr8ZYwhYhaKRHQ_VGL78f8,2318
|
11
|
-
nucliadb_utils/nats.py,sha256=
|
11
|
+
nucliadb_utils/nats.py,sha256=YsSuhGKpZjyZXjVT7uAF6KjRMWvp1k7-SF9ddFXdVC0,15059
|
12
12
|
nucliadb_utils/partition.py,sha256=jBgy4Hu5Iwn4gjbPPcthSykwf-qNx-GcLAIwbzPd1d0,1157
|
13
13
|
nucliadb_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
nucliadb_utils/run.py,sha256=Es0_Bu5Yc-LWczvwL6gzWqSwC85RjDCk-0oFQAJi9g4,1827
|
@@ -16,7 +16,7 @@ nucliadb_utils/settings.py,sha256=BOuYTh08cJe7UgBgU9ALHCJm6eDezbdPsdAXM4HGEgo,81
|
|
16
16
|
nucliadb_utils/signals.py,sha256=JRNv2y9zLtBjOANBf7krGfDGfOc9qcoXZ6N1nKWS2FE,2674
|
17
17
|
nucliadb_utils/store.py,sha256=kQ35HemE0v4_Qg6xVqNIJi8vSFAYQtwI3rDtMsNy62Y,890
|
18
18
|
nucliadb_utils/transaction.py,sha256=GjM754TU9940pfQY3vxAQAYlQdBpqfwrO-_gYkSjWrI,7979
|
19
|
-
nucliadb_utils/utilities.py,sha256=
|
19
|
+
nucliadb_utils/utilities.py,sha256=_5pNsXBknULGrTvV4hgIjsOJ0sSbFq4ygseH89C1gdk,16128
|
20
20
|
nucliadb_utils/aiopynecone/__init__.py,sha256=cp15ZcFnHvpcu_5-aK2A4uUyvuZVV_MJn4bIXMa20ks,835
|
21
21
|
nucliadb_utils/aiopynecone/client.py,sha256=MPyHnDXwhukJr7U3CJh7BpsekfSuOkyM4g5b9LLtzc8,22941
|
22
22
|
nucliadb_utils/aiopynecone/exceptions.py,sha256=fUErx3ceKQK1MUbOnYcZhIzpNe8UVAptZE9JIRDLXDE,4000
|
@@ -59,8 +59,8 @@ nucliadb_utils/tests/indexing.py,sha256=YW2QhkhO9Q_8A4kKWJaWSvXvyQ_AiAwY1VylcfVQ
|
|
59
59
|
nucliadb_utils/tests/local.py,sha256=fXIBasrvdaFJM-sw2wk1_oiFzBcm9O10iCyC-OiXwY8,1914
|
60
60
|
nucliadb_utils/tests/nats.py,sha256=xqpww4jZjTKY9oPGlJdDJG67L3FIBQsa9qDHxILR8r8,7687
|
61
61
|
nucliadb_utils/tests/s3.py,sha256=pl-RJFjA4MH6iXkqhsh5g8gDuEhrYu1nPZ-laxlrMlE,3704
|
62
|
-
nucliadb_utils-6.1.0.
|
63
|
-
nucliadb_utils-6.1.0.
|
64
|
-
nucliadb_utils-6.1.0.
|
65
|
-
nucliadb_utils-6.1.0.
|
66
|
-
nucliadb_utils-6.1.0.
|
62
|
+
nucliadb_utils-6.1.0.post2602.dist-info/METADATA,sha256=7CB6_mYBRnxvaeq3fcpYyAvr62mVWpZCyhdHcT_CvLY,2055
|
63
|
+
nucliadb_utils-6.1.0.post2602.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
64
|
+
nucliadb_utils-6.1.0.post2602.dist-info/top_level.txt,sha256=fE3vJtALTfgh7bcAWcNhcfXkNPp_eVVpbKK-2IYua3E,15
|
65
|
+
nucliadb_utils-6.1.0.post2602.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
66
|
+
nucliadb_utils-6.1.0.post2602.dist-info/RECORD,,
|
File without changes
|
{nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/top_level.txt
RENAMED
File without changes
|
{nucliadb_utils-6.1.0.post2589.dist-info → nucliadb_utils-6.1.0.post2602.dist-info}/zip-safe
RENAMED
File without changes
|