serverish 2.0.1__tar.gz → 2.0.3__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.1 → serverish-2.0.3}/PKG-INFO +1 -1
  2. {serverish-2.0.1 → serverish-2.0.3}/pyproject.toml +1 -1
  3. {serverish-2.0.1 → serverish-2.0.3}/serverish/connection/connection.py +6 -2
  4. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_reader.py +24 -5
  5. {serverish-2.0.1 → serverish-2.0.3}/LICENSE +0 -0
  6. {serverish-2.0.1 → serverish-2.0.3}/README.md +0 -0
  7. {serverish-2.0.1 → serverish-2.0.3}/serverish/__init__.py +0 -0
  8. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/__init__.py +0 -0
  9. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/asyncio_util_functions.py +0 -0
  10. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/collector.py +0 -0
  11. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/datetime.py +0 -0
  12. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/exceptions.py +0 -0
  13. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/fifoset.py +0 -0
  14. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/hasstatuses.py +0 -0
  15. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/idmanger.py +0 -0
  16. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/iterators.py +0 -0
  17. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/manageable.py +0 -0
  18. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/singleton.py +0 -0
  19. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/status.py +0 -0
  20. {serverish-2.0.1 → serverish-2.0.3}/serverish/base/task_manager.py +0 -0
  21. {serverish-2.0.1 → serverish-2.0.3}/serverish/connection/__init__.py +0 -0
  22. {serverish-2.0.1 → serverish-2.0.3}/serverish/connection/connection_jets.py +0 -0
  23. {serverish-2.0.1 → serverish-2.0.3}/serverish/connection/connection_nats.py +0 -0
  24. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/__init__.py +0 -0
  25. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/live_document.py +0 -0
  26. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/logging.py +0 -0
  27. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/messenger.py +0 -0
  28. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_callback_sub.py +0 -0
  29. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_cmd_pub.py +0 -0
  30. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_cmd_sub.py +0 -0
  31. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_core_pub.py +0 -0
  32. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_core_read.py +0 -0
  33. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_core_sub.py +0 -0
  34. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_document_read.py +0 -0
  35. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_journal_pub.py +0 -0
  36. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_journal_read.py +0 -0
  37. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_progress_pub.py +0 -0
  38. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_progress_read.py +0 -0
  39. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_publisher.py +0 -0
  40. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_rpc_req.py +0 -0
  41. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_rpc_resp.py +0 -0
  42. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_single_pub.py +0 -0
  43. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msg_single_read.py +0 -0
  44. {serverish-2.0.1 → serverish-2.0.3}/serverish/messenger/msgvalidator.py +0 -0
  45. {serverish-2.0.1 → serverish-2.0.3}/serverish/monitoring/__init__.py +0 -0
  46. {serverish-2.0.1 → serverish-2.0.3}/serverish/monitoring/bridge.py +0 -0
  47. {serverish-2.0.1 → serverish-2.0.3}/serverish/monitoring/factory.py +0 -0
  48. {serverish-2.0.1 → serverish-2.0.3}/serverish/monitoring/monitored.py +0 -0
  49. {serverish-2.0.1 → serverish-2.0.3}/serverish/monitoring/monitored_nats.py +0 -0
  50. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/config.schema.json +0 -0
  51. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/default.schema.json +0 -0
  52. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/include/timestamp.schema.json +0 -0
  53. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/journal.schema.json +0 -0
  54. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/meta.schema.json +0 -0
  55. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/program_current_state.schema.json +0 -0
  56. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/program_state.schema.json +0 -0
  57. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/progress.schema.json +0 -0
  58. {serverish-2.0.1 → serverish-2.0.3}/serverish/schema/telemetry.schema.json +0 -0
  59. {serverish-2.0.1 → serverish-2.0.3}/serverish/services/TODO.txt +0 -0
  60. {serverish-2.0.1 → serverish-2.0.3}/serverish/slog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverish
3
- Version: 2.0.1
3
+ Version: 2.0.3
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.1"
3
+ version = "2.0.3"
4
4
  description = "helpers for server alike projects"
5
5
  authors = ["Mikołaj Kałuszyński", "MMME team"]
6
6
  readme = "README.md"
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
  import asyncio
3
3
  import logging
4
4
  import re
5
+ import shutil
5
6
  import socket
6
7
  from typing import Iterable
7
8
 
@@ -108,14 +109,17 @@ class Connection(HasStatuses):
108
109
  Returns:
109
110
  Status: Status object, named 'ping'
110
111
  """
112
+ ping_exec = shutil.which('ping')
113
+ if ping_exec is None:
114
+ return StatusReport.unknown(msg="'ping' command not found in PATH, skipping ping check")
111
115
 
112
116
  async def _ping_host(host):
113
117
  proc = await asyncio.create_subprocess_exec(
114
- 'ping', '-c', '1', host,
118
+ ping_exec, '-c', '1', host,
115
119
  stdout=asyncio.subprocess.PIPE,
116
120
  stderr=asyncio.subprocess.PIPE)
117
121
  _stdout, _stderr = await proc.communicate()
118
- return (host, proc.returncode)
122
+ return host, proc.returncode
119
123
 
120
124
  if len(self.host) == 0:
121
125
  return StatusReport.unknown(msg='No hosts to ping')
@@ -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