serverish 2.0.2__tar.gz → 2.0.4__tar.gz

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.
Files changed (60) hide show
  1. {serverish-2.0.2 → serverish-2.0.4}/PKG-INFO +1 -1
  2. {serverish-2.0.2 → serverish-2.0.4}/pyproject.toml +1 -1
  3. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_callback_sub.py +9 -3
  4. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_reader.py +24 -5
  5. {serverish-2.0.2 → serverish-2.0.4}/LICENSE +0 -0
  6. {serverish-2.0.2 → serverish-2.0.4}/README.md +0 -0
  7. {serverish-2.0.2 → serverish-2.0.4}/serverish/__init__.py +0 -0
  8. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/__init__.py +0 -0
  9. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/asyncio_util_functions.py +0 -0
  10. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/collector.py +0 -0
  11. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/datetime.py +0 -0
  12. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/exceptions.py +0 -0
  13. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/fifoset.py +0 -0
  14. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/hasstatuses.py +0 -0
  15. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/idmanger.py +0 -0
  16. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/iterators.py +0 -0
  17. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/manageable.py +0 -0
  18. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/singleton.py +0 -0
  19. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/status.py +0 -0
  20. {serverish-2.0.2 → serverish-2.0.4}/serverish/base/task_manager.py +0 -0
  21. {serverish-2.0.2 → serverish-2.0.4}/serverish/connection/__init__.py +0 -0
  22. {serverish-2.0.2 → serverish-2.0.4}/serverish/connection/connection.py +0 -0
  23. {serverish-2.0.2 → serverish-2.0.4}/serverish/connection/connection_jets.py +0 -0
  24. {serverish-2.0.2 → serverish-2.0.4}/serverish/connection/connection_nats.py +0 -0
  25. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/__init__.py +0 -0
  26. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/live_document.py +0 -0
  27. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/logging.py +0 -0
  28. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/messenger.py +0 -0
  29. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_cmd_pub.py +0 -0
  30. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_cmd_sub.py +0 -0
  31. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_core_pub.py +0 -0
  32. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_core_read.py +0 -0
  33. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_core_sub.py +0 -0
  34. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_document_read.py +0 -0
  35. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_journal_pub.py +0 -0
  36. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_journal_read.py +0 -0
  37. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_progress_pub.py +0 -0
  38. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_progress_read.py +0 -0
  39. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_publisher.py +0 -0
  40. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_rpc_req.py +0 -0
  41. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_rpc_resp.py +0 -0
  42. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_single_pub.py +0 -0
  43. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msg_single_read.py +0 -0
  44. {serverish-2.0.2 → serverish-2.0.4}/serverish/messenger/msgvalidator.py +0 -0
  45. {serverish-2.0.2 → serverish-2.0.4}/serverish/monitoring/__init__.py +0 -0
  46. {serverish-2.0.2 → serverish-2.0.4}/serverish/monitoring/bridge.py +0 -0
  47. {serverish-2.0.2 → serverish-2.0.4}/serverish/monitoring/factory.py +0 -0
  48. {serverish-2.0.2 → serverish-2.0.4}/serverish/monitoring/monitored.py +0 -0
  49. {serverish-2.0.2 → serverish-2.0.4}/serverish/monitoring/monitored_nats.py +0 -0
  50. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/config.schema.json +0 -0
  51. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/default.schema.json +0 -0
  52. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/include/timestamp.schema.json +0 -0
  53. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/journal.schema.json +0 -0
  54. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/meta.schema.json +0 -0
  55. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/program_current_state.schema.json +0 -0
  56. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/program_state.schema.json +0 -0
  57. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/progress.schema.json +0 -0
  58. {serverish-2.0.2 → serverish-2.0.4}/serverish/schema/telemetry.schema.json +0 -0
  59. {serverish-2.0.2 → serverish-2.0.4}/serverish/services/TODO.txt +0 -0
  60. {serverish-2.0.2 → serverish-2.0.4}/serverish/slog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverish
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: helpers for server alike projects
5
5
  License-File: LICENSE
6
6
  Author: Mikołaj Kałuszyński
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "serverish"
3
- version = "2.0.2"
3
+ version = "2.0.4"
4
4
  description = "helpers for server alike projects"
5
5
  authors = ["Mikołaj Kałuszyński", "MMME team"]
6
6
  readme = "README.md"
@@ -42,7 +42,9 @@ class MsgCallbackSubscriber(MsgReader):
42
42
  Args:
43
43
  callback: a callback function to call on each message, may be asynchronous
44
44
  callback is called with two arguments: message dict and metadata dict,
45
- and should return True to continue reading messages, False to stop
45
+ and may return False to stop reading messages. Any other return value
46
+ (including None — i.e. callbacks with no explicit return — and True)
47
+ keeps the subscription running.
46
48
  """
47
49
  self.callback = callback
48
50
  if asyncio.iscoroutinefunction(callback):
@@ -59,7 +61,7 @@ class MsgCallbackSubscriber(MsgReader):
59
61
  assert scb is not None or acb is not None
60
62
  assert not (scb is not None and acb is not None)
61
63
  cb = scb or acb
62
- cont = True
64
+ cont: object = True
63
65
  log.debug(f"Entering sync interation{self}")
64
66
  async for data, meta in self:
65
67
  try:
@@ -74,7 +76,11 @@ class MsgCallbackSubscriber(MsgReader):
74
76
  break
75
77
  except Exception as e:
76
78
  log.exception(f'Error in callback {cb} for message {meta}{str(data):20}: {e}')
77
- if not cont or self._stop_event.is_set() :
79
+ # Stop only on explicit False — None (the implicit "no return" value
80
+ # of a Python function) and any truthy value keep the subscription
81
+ # alive. Treating None as "stop" would silently kill any callback
82
+ # that doesn't bother returning anything, which is the common case.
83
+ if cont is False or self._stop_event.is_set():
78
84
  break
79
85
  log.debug(f"Exiting sync interation{self}")
80
86
 
@@ -292,10 +292,13 @@ class MsgReader(MsgDriver):
292
292
  log.debug(self.fmt(f"No messages available, finishing due to nowait"))
293
293
  raise self.EndIterationException('nowait')
294
294
 
295
- # Non-nowait mode: wait for NEW messages to arrive
296
- # Break into shorter intervals to allow health checks and reconnection handling
297
- blocking_interval = 10.0 # Check every 10 seconds
298
- max_wait_cycles = 10 # Total wait: 10 * 10s = 100s
295
+ # Non-nowait mode: wait for NEW messages to arrive.
296
+ # Break into shorter intervals so the reconnect-needed
297
+ # check at the top of the loop runs often enough that
298
+ # low-rate subjects don't sit on a stale subscription
299
+ # for tens of seconds after a NATS reconnect.
300
+ blocking_interval = 2.0
301
+ max_wait_cycles = 50 # Total wait: 50 * 2s = 100s (unchanged)
299
302
 
300
303
  for cycle in range(max_wait_cycles):
301
304
  # Check for reconnection before each wait cycle
@@ -621,12 +624,28 @@ class MsgReader(MsgDriver):
621
624
  max_retries = 3
622
625
  for attempt in range(max_retries):
623
626
  try:
624
- # Clean up existing subscription
627
+ # Clean up existing subscription. `unsubscribe()` only detaches the
628
+ # local handle; the JetStream consumer must be deleted explicitly
629
+ # or it leaks server-side across every reconnect (one orphan per
630
+ # reopen, accumulating until the stream's consumer cap or the
631
+ # process restarts).
625
632
  if self.pull_subscription is not None:
633
+ try:
634
+ ci = await self.pull_subscription.consumer_info()
635
+ except Exception as e:
636
+ log.debug(f"Error fetching consumer_info during reopen attempt {attempt + 1}: {e}")
637
+ ci = None
626
638
  try:
627
639
  await self.pull_subscription.unsubscribe()
628
640
  except Exception as e:
629
641
  log.debug(f"Error unsubscribing during reopen attempt {attempt + 1}: {e}")
642
+ if ci is not None:
643
+ try:
644
+ await self.connection.js.delete_consumer(stream=ci.stream_name, consumer=ci.name)
645
+ except nats.js.errors.NotFoundError:
646
+ pass # already gone, nothing to delete
647
+ except Exception as e:
648
+ log.debug(f"Error deleting old consumer during reopen attempt {attempt + 1}: {e}")
630
649
 
631
650
  # Create new consumer configuration
632
651
  consumer_conf = await self._create_consumer_cfg()
File without changes
File without changes
File without changes