by-framework 0.2.2.dev15__py3-none-any.whl → 0.2.2.dev16__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.
@@ -83,6 +83,7 @@ class WorkerRunner:
83
83
  self.worker = worker
84
84
  self.group_name = group_name or self._auto_group_name()
85
85
  self.consumer_name = worker.worker_id
86
+ self.max_concurrency = max_concurrency
86
87
  self.semaphore = asyncio.Semaphore(max_concurrency)
87
88
  self.fetch_count = fetch_count
88
89
  # OTel for worker.execute is emitted via a live wrapping span (see
@@ -121,6 +122,14 @@ class WorkerRunner:
121
122
  float(self.worker.heartbeat_lease_ttl_seconds) * 2.0,
122
123
  stream_block_seconds * 3.0,
123
124
  )
125
+ # Poll interval used by _acquire_slot() while waiting for a saturated
126
+ # semaphore. Derived from _consumer_health_timeout_seconds (not a
127
+ # hardcoded constant) so it stays well under the health-check
128
+ # deadline under any configuration, with a floor to avoid busy-
129
+ # polling if the timeout is configured very small.
130
+ self._slot_wait_poll_seconds = max(
131
+ self._consumer_health_timeout_seconds / 4.0, 1.0
132
+ )
124
133
  # Set as the first step of _shutdown() - see
125
134
  # docs/architecture/worker-readiness-endpoint.md Decision 6.
126
135
  self._draining: bool = False
@@ -386,9 +395,23 @@ class WorkerRunner:
386
395
  finally:
387
396
  await self.redis.xack(stream_name, self.group_name, msg_id)
388
397
 
398
+ async def _acquire_slot(self) -> None:
399
+ """Wait for a concurrency slot, marking the consumer tick on every
400
+ poll so a saturated-but-alive loop isn't judged stalled.
401
+
402
+ Polling on `len(self._running_tasks)` instead of wrapping
403
+ `semaphore.acquire()` in `asyncio.wait_for()` avoids relying on
404
+ Semaphore cancellation semantics for a lock this runner is the only
405
+ waiter on — see design.md's "why not asyncio.wait_for" section.
406
+ """
407
+ while len(self._running_tasks) >= self.max_concurrency:
408
+ self._mark_consumer_tick()
409
+ await asyncio.sleep(self._slot_wait_poll_seconds)
410
+ await self.semaphore.acquire()
411
+
389
412
  async def _run_once(self) -> bool:
390
413
  """Fetch and start processing messages."""
391
- await self.semaphore.acquire()
414
+ await self._acquire_slot()
392
415
 
393
416
  try:
394
417
  messages = await self.fetch_messages(
@@ -401,7 +424,7 @@ class WorkerRunner:
401
424
 
402
425
  for i, (stream_name, msg_id, data_dict) in enumerate(messages):
403
426
  if i > 0:
404
- await self.semaphore.acquire()
427
+ await self._acquire_slot()
405
428
 
406
429
  task = asyncio.create_task(
407
430
  self._process_and_release(stream_name, msg_id, data_dict)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: by-framework
3
- Version: 0.2.2.dev15
3
+ Version: 0.2.2.dev16
4
4
  Summary: 分布式 Agent 调度框架
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -94,12 +94,12 @@ by_framework/worker/context.py,sha256=5As3GcnVrwIE1nCJDDYLt_Ke8-8nzh44CzQBvyncQs
94
94
  by_framework/worker/health_server.py,sha256=4Jw3ltAD7Ow4dPHhwTUU_4JjKz-iDCNG7RJzD2VzPlM,6090
95
95
  by_framework/worker/heartbeat.py,sha256=trHDS8FUALsA6jyzDPa087hpr8_2vY8ZUFn0ld1kI2o,15465
96
96
  by_framework/worker/processor.py,sha256=UgC4_p-bg2KOHVTEILeMXF44rT9VLrRP5vompX0EeiQ,16243
97
- by_framework/worker/runner.py,sha256=6_L_UUV1Kljys_4Eh_qnwMd7a-gGsikd4M_O5K879NA,51897
97
+ by_framework/worker/runner.py,sha256=nkJDpziUQaqqVEXc8OhoF4DKGiw7fthpvXudDjxYdm0,53093
98
98
  by_framework/worker/worker.py,sha256=IaPFLWCFo5U5Xo7PIQHtTt4JmtxZA1O_3nAuGl_g4N0,49527
99
99
  by_framework/worker/sandbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
100
  by_framework/worker/sandbox/hook_sandbox.py,sha256=XXSdH5YGX814LvV03vkq-zn5DVIBPYYt2NhRv1lT1Bk,2329
101
- by_framework-0.2.2.dev15.dist-info/METADATA,sha256=l050msraVm4RdyM0rIA_GXOpYu5pWyuUTdw7e2IBdd4,28018
102
- by_framework-0.2.2.dev15.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
103
- by_framework-0.2.2.dev15.dist-info/entry_points.txt,sha256=5t6kgMjkR0UgJTpOsbuQDo0fOf7zeXjQW9DhkxfAGFc,56
104
- by_framework-0.2.2.dev15.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
105
- by_framework-0.2.2.dev15.dist-info/RECORD,,
101
+ by_framework-0.2.2.dev16.dist-info/METADATA,sha256=3Sbgc5Aw5L6vYzfRQpcfyMK9B-qYs9V-TMvAJGHwrAM,28018
102
+ by_framework-0.2.2.dev16.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
103
+ by_framework-0.2.2.dev16.dist-info/entry_points.txt,sha256=5t6kgMjkR0UgJTpOsbuQDo0fOf7zeXjQW9DhkxfAGFc,56
104
+ by_framework-0.2.2.dev16.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
105
+ by_framework-0.2.2.dev16.dist-info/RECORD,,