saq 0.24.10__py3-none-any.whl → 0.24.12__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.
saq/__init__.py CHANGED
@@ -14,4 +14,4 @@ __all__ = [
14
14
  "Worker",
15
15
  ]
16
16
 
17
- __version__ = "0.24.10"
17
+ __version__ = "0.24.12"
saq/queue/base.py CHANGED
@@ -28,12 +28,12 @@ if t.TYPE_CHECKING:
28
28
  from saq.types import (
29
29
  BeforeEnqueueType,
30
30
  CountKind,
31
- ListenCallback,
32
31
  DumpType,
32
+ ListenCallback,
33
33
  LoadType,
34
34
  QueueInfo,
35
- WorkerStats,
36
35
  WorkerInfo,
36
+ WorkerStats,
37
37
  )
38
38
 
39
39
 
@@ -370,7 +370,7 @@ class Queue(ABC):
370
370
  job_keys: sequence of job keys
371
371
  callback: callback function, if it returns truthy, break
372
372
  timeout: if timeout is truthy, wait for timeout seconds
373
- poll_interval: number of seconds in between poll attempts if needed
373
+ poll_interval: Number of seconds between checking job status (default 0.5)
374
374
  """
375
375
 
376
376
  async def listen() -> None:
@@ -391,7 +391,13 @@ class Queue(ABC):
391
391
  else:
392
392
  await listen()
393
393
 
394
- async def apply(self, job_or_func: str, timeout: float | None = None, **kwargs: t.Any) -> t.Any:
394
+ async def apply(
395
+ self,
396
+ job_or_func: str,
397
+ timeout: float | None = None,
398
+ poll_interval: float = 0.5,
399
+ **kwargs: t.Any,
400
+ ) -> t.Any:
395
401
  """
396
402
  Enqueue a job and wait for its result.
397
403
 
@@ -409,9 +415,12 @@ class Queue(ABC):
409
415
  Args:
410
416
  job_or_func: Same as Queue.enqueue
411
417
  timeout: If provided, how long to wait for result, else infinite (default None)
418
+ poll_interval: Number of seconds between checking job status (default 0.5)
412
419
  kwargs: Same as Queue.enqueue
413
420
  """
414
- results = await self.map(job_or_func, timeout=timeout, iter_kwargs=[kwargs])
421
+ results = await self.map(
422
+ job_or_func, timeout=timeout, poll_interval=poll_interval, iter_kwargs=[kwargs]
423
+ )
415
424
  if results:
416
425
  return results[0]
417
426
  return None
@@ -450,7 +459,7 @@ class Queue(ABC):
450
459
  return_exceptions: If False (default), an exception is immediately raised as soon as any jobs
451
460
  fail. Other jobs won't be cancelled and will continue to run.
452
461
  If True, exceptions are treated the same as successful results and aggregated in the result list.
453
- poll_interval: number of seconds in between poll attempts
462
+ poll_interval: Number of seconds between checking job status (default 0.5)
454
463
  kwargs: Default kwargs for all jobs. These will be overridden by those in iter_kwargs.
455
464
  """
456
465
  iter_kwargs = [
saq/queue/postgres.py CHANGED
@@ -21,7 +21,7 @@ from saq.job import (
21
21
  from saq.multiplexer import Multiplexer
22
22
  from saq.queue.base import Queue, logger
23
23
  from saq.queue.postgres_migrations import get_migrations
24
- from saq.utils import now_seconds
24
+ from saq.utils import now, now_seconds
25
25
 
26
26
  if t.TYPE_CHECKING:
27
27
  from collections.abc import Iterable
@@ -666,7 +666,7 @@ class PostgresQueue(Queue):
666
666
  UPDATE {jobs_table} SET status = 'active'
667
667
  FROM locked_job
668
668
  WHERE {jobs_table}.key = locked_job.key
669
- RETURNING job, {jobs_table}.key, status
669
+ RETURNING job, {jobs_table}.key
670
670
  """
671
671
  )
672
672
  ).format(
@@ -680,7 +680,22 @@ class PostgresQueue(Queue):
680
680
  "phigh": self._priorities[1],
681
681
  },
682
682
  )
683
- rows = await cursor.fetchall()
683
+
684
+ jobs = []
685
+ keys = []
686
+ dequeued = now()
687
+
688
+ for payload, key in await cursor.fetchall():
689
+ # there can be a race condition where a job is swept right after it is dequeued
690
+ # but before it's updated for processing
691
+ job = self.deserialize(payload, Status.ACTIVE)
692
+ assert job
693
+ job.started = dequeued
694
+ job.touched = dequeued
695
+ await self._update(job, status=Status.ACTIVE, connection=conn)
696
+
697
+ jobs.append(job)
698
+ keys.append(key)
684
699
 
685
700
  await conn.execute(
686
701
  SQL(
@@ -695,20 +710,20 @@ class PostgresQueue(Queue):
695
710
  jobs_table=self.jobs_table,
696
711
  job_lock_keyspace=self.job_lock_keyspace,
697
712
  ),
698
- {"keys": [key for _, key, _ in rows]},
713
+ {"keys": keys},
699
714
  )
700
- lock_acquisition_results = await cursor.fetchall()
701
- for key, lock_acquired in lock_acquisition_results:
715
+
716
+ for key, lock_acquired in await cursor.fetchall():
702
717
  if not lock_acquired:
703
718
  logger.error(
704
719
  "Could not acquire lock for job %s. This may result in unexpected behavior",
705
720
  key,
706
721
  )
707
722
 
708
- for job, _, status in rows:
709
- self._job_queue.put_nowait(self.deserialize(job, status))
723
+ if jobs:
724
+ for job in jobs:
725
+ self._job_queue.put_nowait(job)
710
726
 
711
- if rows:
712
727
  await self._notify(DEQUEUE)
713
728
 
714
729
  async def _enqueue(self, job: Job) -> Job | None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: saq
3
- Version: 0.24.10
3
+ Version: 0.24.12
4
4
  Summary: Distributed Python job queue with asyncio and redis
5
5
  Home-page: https://github.com/tobymao/saq
6
6
  Author: Toby Mao
@@ -198,6 +198,9 @@ print(job.results)
198
198
  # run a job and return the result
199
199
  print(await queue.apply("test", a=2))
200
200
 
201
+ # run a job with custom polling interval to check status more frequently
202
+ print(await queue.apply("test", a=2, poll_interval=0.1))
203
+
201
204
  # Run multiple jobs concurrently and collect the results into a list
202
205
  print(await queue.map("test", [{"a": 3}, {"a": 4}]))
203
206
 
@@ -1,4 +1,4 @@
1
- saq/__init__.py,sha256=vYIFEVix-39RzyynnCqMQVZMrhIr4ns1aeJOmEXWCt0,219
1
+ saq/__init__.py,sha256=-6W6CleVyyCSWJOyF4tfXRJ9nI6u-Y4KPRgZYraOhTg,219
2
2
  saq/__main__.py,sha256=N4RNqnCcj7eZbM3OyYaC03_6Cot-y-SxW5Hwx6fuzKU,2440
3
3
  saq/errors.py,sha256=XPJw6J3caSAho4ZybuodIbeuGjboVabLuf3NFOEE-4Q,112
4
4
  saq/job.py,sha256=X49OuSqZeN-RVmh4AonZRrwk2wU8MeukLSxrvjGLm7E,11552
@@ -8,9 +8,9 @@ saq/types.py,sha256=GhIq2BIE_Z9hA-qS-NQXh_iPICNI0NZxOzjW0vcMgFU,3196
8
8
  saq/utils.py,sha256=NdOycT-03zxjhKM8A1i0vzKnkv1UQxvy_Zt4GnO0Zd8,1721
9
9
  saq/worker.py,sha256=W9rqK_u0bbvUbdb879pvf1f-suNnesKk1TjYqHQREc8,16817
10
10
  saq/queue/__init__.py,sha256=5LgBHGylCVvrLDcjMCcI2dRRgh0BPdz2TKOdc8NMs2E,87
11
- saq/queue/base.py,sha256=wej8WBWK-S9GXYEHXTfl7Wwwm-DB3o1RB-bHMvNLfdI,15682
11
+ saq/queue/base.py,sha256=3Fc91Cgrpp1KUTRT0nPL9lroRmPCz7WGFpZkxDyMQh4,15919
12
12
  saq/queue/http.py,sha256=V9S26gJbUt5AUIR2ETasSQy4Q_K30eGtguBYHpfcLGU,7739
13
- saq/queue/postgres.py,sha256=7FSljVrqISV8w82cv0yWvyejMpucbucsjpu8Ntoqee4,35648
13
+ saq/queue/postgres.py,sha256=Yqv73KKbAO7aC7URkjLGk_XR1JWQ2AQtqoGepLNPSzw,36130
14
14
  saq/queue/postgres_migrations.py,sha256=gI6j-0TzlFFSWxji3Dy9aJ-llboJBm92J4tB_YZ7qI8,2080
15
15
  saq/queue/redis.py,sha256=sa_wzUUlfPw-RZ-v_cnDEJWEFyUi3sy_3YTqG4UklOA,17754
16
16
  saq/web/__init__.py,sha256=NG9LfjgJQxNft0_iZuZ3LnX1I58SfxRwKpycjazBoGE,23
@@ -20,9 +20,9 @@ saq/web/starlette.py,sha256=i38xuNcnQvWBY3jyHHu9Uo9ILSBzOwmk5Bq06c3CQzM,4432
20
20
  saq/web/static/app.js,sha256=i6PaRvBvt96LOINBdEuKkDvVeM-GA8lJiFg4jtQ3viY,7094
21
21
  saq/web/static/pico.min.css.gz,sha256=qCxIv3wWFMQ7MkvGSHQLwxio3121VvvieOkSjw6fv6o,9263
22
22
  saq/web/static/snabbdom.js.gz,sha256=zSO3Z761TB7bYNQFFEtypD0vCuqWesqPJeE5CuV4xRg,7603
23
- saq-0.24.10.dist-info/LICENSE,sha256=p208OXrLf_dMcvuRHpcinfsJdihCqKWbqtFXpw4kyW0,1065
24
- saq-0.24.10.dist-info/METADATA,sha256=iPnYOcZctuzl-5KOEWq7UoOBSexbYKFTtBvEM_KUr40,7782
25
- saq-0.24.10.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
- saq-0.24.10.dist-info/entry_points.txt,sha256=HkKOud1K15_DV7AEltn8G5Ua10VqIgHaZ4BQit4fdOk,42
27
- saq-0.24.10.dist-info/top_level.txt,sha256=FMrrc5EiGr4sQkEDtUMHIpomnWHL9i6xT7B6lvEh8xM,4
28
- saq-0.24.10.dist-info/RECORD,,
23
+ saq-0.24.12.dist-info/LICENSE,sha256=p208OXrLf_dMcvuRHpcinfsJdihCqKWbqtFXpw4kyW0,1065
24
+ saq-0.24.12.dist-info/METADATA,sha256=g_SywCzbz3nXYipojXUVz7NQRIFK55UPXVpcPrHEuvU,7913
25
+ saq-0.24.12.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
+ saq-0.24.12.dist-info/entry_points.txt,sha256=HkKOud1K15_DV7AEltn8G5Ua10VqIgHaZ4BQit4fdOk,42
27
+ saq-0.24.12.dist-info/top_level.txt,sha256=FMrrc5EiGr4sQkEDtUMHIpomnWHL9i6xT7B6lvEh8xM,4
28
+ saq-0.24.12.dist-info/RECORD,,
File without changes
File without changes