pydocket 0.1.2__py3-none-any.whl → 0.1.4__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 pydocket might be problematic. Click here for more details.

docket/cli.py CHANGED
@@ -228,6 +228,14 @@ def worker(
228
228
  envvar="DOCKET_WORKER_RECONNECTION_DELAY",
229
229
  ),
230
230
  ] = timedelta(seconds=5),
231
+ minimum_check_interval: Annotated[
232
+ timedelta,
233
+ typer.Option(
234
+ parser=duration,
235
+ help="The minimum interval to check for tasks",
236
+ envvar="DOCKET_WORKER_MINIMUM_CHECK_INTERVAL",
237
+ ),
238
+ ] = timedelta(milliseconds=100),
231
239
  until_finished: Annotated[
232
240
  bool,
233
241
  typer.Option(
@@ -244,6 +252,7 @@ def worker(
244
252
  concurrency=concurrency,
245
253
  redelivery_timeout=redelivery_timeout,
246
254
  reconnection_delay=reconnection_delay,
255
+ minimum_check_interval=minimum_check_interval,
247
256
  until_finished=until_finished,
248
257
  tasks=tasks,
249
258
  )
docket/docket.py CHANGED
@@ -147,17 +147,17 @@ class Docket:
147
147
  self._monitor_strikes_task = asyncio.create_task(self._monitor_strikes())
148
148
 
149
149
  # Ensure that the stream and worker group exist
150
- async with self.redis() as r:
151
- try:
150
+ try:
151
+ async with self.redis() as r:
152
152
  await r.xgroup_create(
153
153
  groupname=self.worker_group_name,
154
154
  name=self.stream_key,
155
155
  id="0-0",
156
156
  mkstream=True,
157
157
  )
158
- except redis.exceptions.RedisError as e:
159
- if "BUSYGROUP" not in repr(e):
160
- raise
158
+ except redis.exceptions.RedisError as e:
159
+ if "BUSYGROUP" not in repr(e):
160
+ raise
161
161
 
162
162
  return self
163
163
 
@@ -178,8 +178,21 @@ class Docket:
178
178
 
179
179
  @asynccontextmanager
180
180
  async def redis(self) -> AsyncGenerator[Redis, None]:
181
- async with Redis.from_url(self.url) as redis: # type: ignore
182
- yield redis
181
+ redis: Redis | None = None
182
+ try:
183
+ redis = await Redis.from_url(
184
+ self.url,
185
+ single_connection_client=True,
186
+ )
187
+ await redis.__aenter__()
188
+ try:
189
+ yield redis
190
+ finally:
191
+ await asyncio.shield(redis.__aexit__(None, None, None))
192
+ finally:
193
+ # redis 4.6.0 doesn't automatically disconnect and leaves connections open
194
+ if redis:
195
+ await asyncio.shield(redis.connection_pool.disconnect())
183
196
 
184
197
  def register(self, function: Callable[..., Awaitable[Any]]) -> None:
185
198
  from .dependencies import validate_dependencies
@@ -513,7 +526,7 @@ class Docket:
513
526
  )
514
527
  running.append(RunningExecution(execution, worker_name, started))
515
528
  else:
516
- future.append(execution)
529
+ future.append(execution) # pragma: no cover
517
530
 
518
531
  for message in queued_messages:
519
532
  function = self.tasks[message[b"function"].decode()]
docket/worker.py CHANGED
@@ -67,7 +67,7 @@ class Worker:
67
67
  concurrency: int = 10,
68
68
  redelivery_timeout: timedelta = timedelta(minutes=5),
69
69
  reconnection_delay: timedelta = timedelta(seconds=5),
70
- minimum_check_interval: timedelta = timedelta(milliseconds=10),
70
+ minimum_check_interval: timedelta = timedelta(milliseconds=100),
71
71
  ) -> None:
72
72
  self.docket = docket
73
73
  self.name = name or f"worker:{uuid4()}"
@@ -110,6 +110,7 @@ class Worker:
110
110
  concurrency: int = 10,
111
111
  redelivery_timeout: timedelta = timedelta(minutes=5),
112
112
  reconnection_delay: timedelta = timedelta(seconds=5),
113
+ minimum_check_interval: timedelta = timedelta(milliseconds=100),
113
114
  until_finished: bool = False,
114
115
  tasks: list[str] = ["docket.tasks:standard_tasks"],
115
116
  ) -> None:
@@ -123,6 +124,7 @@ class Worker:
123
124
  concurrency=concurrency,
124
125
  redelivery_timeout=redelivery_timeout,
125
126
  reconnection_delay=reconnection_delay,
127
+ minimum_check_interval=minimum_check_interval,
126
128
  ) as worker:
127
129
  if until_finished:
128
130
  await worker.run_until_finished()
@@ -170,28 +172,31 @@ class Worker:
170
172
  """
171
173
  local total_work = redis.call('ZCARD', KEYS[1])
172
174
  local due_work = 0
173
- local tasks = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1])
174
175
 
175
- for i, key in ipairs(tasks) do
176
- local hash_key = ARGV[2] .. ":" .. key
177
- local task_data = redis.call('HGETALL', hash_key)
178
-
179
- if #task_data > 0 then
180
- local task = {}
181
- for j = 1, #task_data, 2 do
182
- task[task_data[j]] = task_data[j+1]
176
+ if total_work > 0 then
177
+ local tasks = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1])
178
+
179
+ for i, key in ipairs(tasks) do
180
+ local hash_key = ARGV[2] .. ":" .. key
181
+ local task_data = redis.call('HGETALL', hash_key)
182
+
183
+ if #task_data > 0 then
184
+ local task = {}
185
+ for j = 1, #task_data, 2 do
186
+ task[task_data[j]] = task_data[j+1]
187
+ end
188
+
189
+ redis.call('XADD', KEYS[2], '*',
190
+ 'key', task['key'],
191
+ 'when', task['when'],
192
+ 'function', task['function'],
193
+ 'args', task['args'],
194
+ 'kwargs', task['kwargs'],
195
+ 'attempt', task['attempt']
196
+ )
197
+ redis.call('DEL', hash_key)
198
+ due_work = due_work + 1
183
199
  end
184
-
185
- redis.call('XADD', KEYS[2], '*',
186
- 'key', task['key'],
187
- 'when', task['when'],
188
- 'function', task['function'],
189
- 'args', task['args'],
190
- 'kwargs', task['kwargs'],
191
- 'attempt', task['attempt']
192
- )
193
- redis.call('DEL', hash_key)
194
- due_work = due_work + 1
195
200
  end
196
201
  end
197
202
 
@@ -275,8 +280,6 @@ class Worker:
275
280
 
276
281
  for message_id, message in redeliveries:
277
282
  start_task(message_id, message)
278
- if available_slots <= 0:
279
- break
280
283
 
281
284
  if available_slots <= 0:
282
285
  continue
@@ -295,8 +298,7 @@ class Worker:
295
298
  for _, messages in new_deliveries:
296
299
  for message_id, message in messages:
297
300
  start_task(message_id, message)
298
- if available_slots <= 0:
299
- break
301
+
300
302
  except asyncio.CancelledError:
301
303
  if active_tasks: # pragma: no cover
302
304
  logger.info(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydocket
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A distributed background task system for Python functions
5
5
  Project-URL: Homepage, https://github.com/chrisguidry/docket
6
6
  Project-URL: Bug Tracker, https://github.com/chrisguidry/docket/issues
@@ -1,16 +1,16 @@
1
1
  docket/__init__.py,sha256=GoJYpyuO6QFeBB8GNaxGGvMMuai55Eaw_8u-o1PM3hk,743
2
2
  docket/__main__.py,sha256=Vkuh7aJ-Bl7QVpVbbkUksAd_hn05FiLmWbc-8kbhZQ4,34
3
3
  docket/annotations.py,sha256=GZwOPtPXyeIhnsLh3TQMBnXrjtTtSmF4Ratv4vjPx8U,950
4
- docket/cli.py,sha256=6dSD94wIa0aJNigeAgPR0hXE4a6szLYAs8h5F8V0CGs,18853
4
+ docket/cli.py,sha256=N0vp1zO5Wau4nBDMJOU34hYn11HR3PaYY3Ybk1gS8XY,19188
5
5
  docket/dependencies.py,sha256=Vht3qKbik-HQ7jsAU5k-eig4_yuru56-ZewjBVVu4yM,4325
6
- docket/docket.py,sha256=4bXMwDfOXbubBY1NiVyO7k598KvTDbbPzXjnN1dVGJU,19659
6
+ docket/docket.py,sha256=TWeZ63NfN6Eq4lFzKoQTJz88ECZsH3-gqYszhQl-bXs,20124
7
7
  docket/execution.py,sha256=rHsQ60BbNREzcpUC_RvbGUctdLaprYp1x46sT6jTrdc,11416
8
8
  docket/instrumentation.py,sha256=USo8ptCFcwQj_YaUpJvsUHPb0QfQr50i9dF4tYgYde4,2992
9
9
  docket/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  docket/tasks.py,sha256=K1f_W1z4m9RVz1GJ1ymWY5ZaRmqHO1SebNBVENlkelU,1471
11
- docket/worker.py,sha256=nlp4sJCI2zvAhxivl5igFbKDe3z1aOOiAEz30XelAGY,18371
12
- pydocket-0.1.2.dist-info/METADATA,sha256=_Mpg9K1AsgbagCDZWY0DF29awyi-Ox9OMAN17bf6wKE,13092
13
- pydocket-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- pydocket-0.1.2.dist-info/entry_points.txt,sha256=4WOk1nUlBsUT5O3RyMci2ImuC5XFswuopElYcLHtD5k,47
15
- pydocket-0.1.2.dist-info/licenses/LICENSE,sha256=YuVWU_ZXO0K_k2FG8xWKe5RGxV24AhJKTvQmKfqXuyk,1087
16
- pydocket-0.1.2.dist-info/RECORD,,
11
+ docket/worker.py,sha256=8wnWxHj7ctHPxEGSRxPTsHksZ6OWRoG5dKpSkvIZP88,18479
12
+ pydocket-0.1.4.dist-info/METADATA,sha256=y8PTR9Xwh8MeMr7ZhPJzUQGtQUjQXN3QRpYTvxtKfv0,13092
13
+ pydocket-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ pydocket-0.1.4.dist-info/entry_points.txt,sha256=4WOk1nUlBsUT5O3RyMci2ImuC5XFswuopElYcLHtD5k,47
15
+ pydocket-0.1.4.dist-info/licenses/LICENSE,sha256=YuVWU_ZXO0K_k2FG8xWKe5RGxV24AhJKTvQmKfqXuyk,1087
16
+ pydocket-0.1.4.dist-info/RECORD,,