saq 0.24.5__py3-none-any.whl → 0.24.8__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.5"
17
+ __version__ = "0.24.8"
saq/job.py CHANGED
@@ -174,6 +174,8 @@ class Job:
174
174
 
175
175
  if "queue" in kwargs:
176
176
  kwargs["queue"] = kwargs["queue"].name
177
+ if "status" in kwargs:
178
+ kwargs["status"] = kwargs["status"].name.lower()
177
179
 
178
180
  if not kwargs.get("meta"):
179
181
  kwargs.pop("meta", None)
@@ -181,6 +183,10 @@ class Job:
181
183
  info = ", ".join(f"{k}: {v}" for k, v in kwargs.items())
182
184
  return f"Job<{info}>"
183
185
 
186
+ def __post_init__(self) -> None:
187
+ if isinstance(self.status, str):
188
+ self.status = Status[self.status.upper()]
189
+
184
190
  def __repr__(self) -> str:
185
191
  return self.info(True)
186
192
 
saq/queue/base.py CHANGED
@@ -118,7 +118,7 @@ class Queue(ABC):
118
118
  for k, v in kwargs.items():
119
119
  if hasattr(job, k):
120
120
  setattr(job, k, v)
121
- await self._update(job, **kwargs)
121
+ await self._update(self.copy(job), **kwargs)
122
122
 
123
123
  @abstractmethod
124
124
  async def _update(self, job: Job, status: Status | None = None, **kwargs: t.Any) -> None:
@@ -213,6 +213,9 @@ class Queue(ABC):
213
213
  async def connect(self) -> None:
214
214
  self._loop = asyncio.get_running_loop()
215
215
 
216
+ def copy(self, job: Job) -> Job:
217
+ return self.deserialize(job.to_dict()) # type: ignore
218
+
216
219
  def serialize(self, job: Job) -> bytes | str:
217
220
  return self._dump(job.to_dict())
218
221
 
saq/queue/postgres.py CHANGED
@@ -138,7 +138,6 @@ class PostgresQueue(Queue):
138
138
  self._waiting = 0 # Internal counter of worker tasks waiting for dequeue
139
139
  self._dequeue_conn: AsyncConnection | None = None
140
140
  self._connection_lock = asyncio.Lock()
141
- self._releasing: list[str] = []
142
141
  self._has_sweep_lock = False
143
142
  self._channel = CHANNEL.format(self.name)
144
143
  self._listener = ListenMultiplexer(self.pool, self._channel)
@@ -480,30 +479,34 @@ class PostgresQueue(Queue):
480
479
 
481
480
  job.status = status or job.status
482
481
 
483
- await conn.execute(
484
- SQL(
485
- dedent(
486
- """
487
- UPDATE {jobs_table} SET
488
- job = %(job)s
489
- ,status = %(status)s
490
- ,scheduled = %(scheduled)s
491
- {expire_at}
492
- WHERE key = %(key)s
493
- """
494
- )
495
- ).format(
496
- jobs_table=self.jobs_table,
497
- expire_at=SQL(",expire_at = %(expire_at)s" if expire_at != -1 else ""),
498
- ),
499
- {
500
- "job": self.serialize(job),
501
- "status": job.status,
502
- "key": job.key,
503
- "scheduled": job.scheduled,
504
- "expire_at": expire_at,
505
- },
506
- )
482
+ try:
483
+ await conn.execute(
484
+ SQL(
485
+ dedent(
486
+ """
487
+ UPDATE {jobs_table} SET
488
+ job = %(job)s
489
+ ,status = %(status)s
490
+ ,scheduled = %(scheduled)s
491
+ {expire_at}
492
+ WHERE key = %(key)s
493
+ """
494
+ )
495
+ ).format(
496
+ jobs_table=self.jobs_table,
497
+ expire_at=SQL(",expire_at = %(expire_at)s" if expire_at != -1 else ""),
498
+ ),
499
+ {
500
+ "job": self.serialize(job),
501
+ "status": job.status,
502
+ "key": job.key,
503
+ "scheduled": job.scheduled,
504
+ "expire_at": expire_at,
505
+ },
506
+ )
507
+ finally:
508
+ if job.status != Status.ACTIVE:
509
+ await self._release_job(job.key)
507
510
 
508
511
  async def job(self, job_key: str) -> Job | None:
509
512
  async with self.pool.connection() as conn, conn.cursor() as cursor:
@@ -643,7 +646,7 @@ class PostgresQueue(Queue):
643
646
  dedent(
644
647
  """
645
648
  WITH locked_job AS (
646
- SELECT key, lock_key
649
+ SELECT key
647
650
  FROM {jobs_table}
648
651
  WHERE status = 'queued'
649
652
  AND queue = %(queue)s
@@ -656,6 +659,7 @@ class PostgresQueue(Queue):
656
659
  AND queue = %(queue)s
657
660
  AND group_key IS NOT NULL
658
661
  )
662
+ AND pg_try_advisory_lock({job_lock_keyspace}, lock_key)
659
663
  ORDER BY priority, scheduled
660
664
  LIMIT %(limit)s
661
665
  FOR UPDATE SKIP LOCKED
@@ -663,7 +667,6 @@ class PostgresQueue(Queue):
663
667
  UPDATE {jobs_table} SET status = 'active'
664
668
  FROM locked_job
665
669
  WHERE {jobs_table}.key = locked_job.key
666
- AND pg_try_advisory_lock({job_lock_keyspace}, locked_job.lock_key)
667
670
  RETURNING job
668
671
  """
669
672
  )
@@ -784,7 +787,7 @@ class PostgresQueue(Queue):
784
787
  SQL(
785
788
  dedent(
786
789
  """
787
- SELECT status
790
+ SELECT UPPER(status)
788
791
  FROM {jobs_table}
789
792
  WHERE key = %(key)s
790
793
  {for_update}
@@ -800,7 +803,7 @@ class PostgresQueue(Queue):
800
803
  )
801
804
  result = await cursor.fetchone()
802
805
  if result:
803
- return result[0]
806
+ return Status[result[0]]
804
807
  return None
805
808
 
806
809
  async def _retry(self, job: Job, error: str | None) -> None:
@@ -839,7 +842,7 @@ class PostgresQueue(Queue):
839
842
  ).format(jobs_table=self.jobs_table),
840
843
  {"key": key},
841
844
  )
842
- await self._release_job(key)
845
+ await self._release_job(key)
843
846
 
844
847
  async def _notify(self, key: str, connection: AsyncConnection | None = None) -> None:
845
848
  async with self.nullcontext(connection) if connection else self.pool.connection() as conn:
@@ -871,26 +874,22 @@ class PostgresQueue(Queue):
871
874
  yield enter_result
872
875
 
873
876
  async def _release_job(self, key: str) -> None:
874
- self._releasing.append(key)
875
- if self._connection_lock.locked():
876
- return
877
- async with self._get_dequeue_conn() as conn:
877
+ async with self._get_dequeue_conn() as conn, conn:
878
878
  await conn.execute(
879
879
  SQL(
880
880
  dedent(
881
881
  """
882
882
  SELECT pg_advisory_unlock({job_lock_keyspace}, lock_key)
883
883
  FROM {jobs_table}
884
- WHERE key = ANY(%(keys)s)
884
+ WHERE key = %(key)s
885
885
  """
886
886
  )
887
887
  ).format(
888
888
  jobs_table=self.jobs_table,
889
889
  job_lock_keyspace=self.job_lock_keyspace,
890
890
  ),
891
- {"keys": self._releasing},
891
+ {"key": key},
892
892
  )
893
- self._releasing.clear()
894
893
 
895
894
  @cached_property
896
895
  def _job_queue(self) -> asyncio.Queue:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: saq
3
- Version: 0.24.5
3
+ Version: 0.24.8
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
@@ -1,16 +1,16 @@
1
- saq/__init__.py,sha256=p8_e3CZvOzoEqFNkshhh1z1vvIguuNtn21nOE90zfws,218
1
+ saq/__init__.py,sha256=8Yq6PVEa09HJzvRvQV0TUGSmOPqkdsJfNGYooR-DZ10,218
2
2
  saq/__main__.py,sha256=N4RNqnCcj7eZbM3OyYaC03_6Cot-y-SxW5Hwx6fuzKU,2440
3
3
  saq/errors.py,sha256=XPJw6J3caSAho4ZybuodIbeuGjboVabLuf3NFOEE-4Q,112
4
- saq/job.py,sha256=Pion_buhc4N-5mqnqfwfpzVjv-paP3HHqtMAKB6XIcE,11327
4
+ saq/job.py,sha256=X49OuSqZeN-RVmh4AonZRrwk2wU8MeukLSxrvjGLm7E,11552
5
5
  saq/multiplexer.py,sha256=S_mjo7kObSBQ_f8epf0pT5Tjxg-LABW3fSH4dPfZxsE,2332
6
6
  saq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  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=KaRv9r0fmlRf2CH6Q72MGHXIk9e3vpPEWo4vt5DL1RA,15460
11
+ saq/queue/base.py,sha256=vRSr4ExGETlkjc9bLrtqfi_A7Zsr_IQzCMyN_b70-UM,15572
12
12
  saq/queue/http.py,sha256=V9S26gJbUt5AUIR2ETasSQy4Q_K30eGtguBYHpfcLGU,7739
13
- saq/queue/postgres.py,sha256=J6yENeG3rx7IhmCmSDYqz-eyYTvBvWf7-nsVgkuu-Us,34667
13
+ saq/queue/postgres.py,sha256=Zb59GWPy36K6FaW3atdYOl4KBykoFVkW8S3gwX59nAc,34719
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.5.dist-info/LICENSE,sha256=p208OXrLf_dMcvuRHpcinfsJdihCqKWbqtFXpw4kyW0,1065
24
- saq-0.24.5.dist-info/METADATA,sha256=rSwLcz-zNI6vH0d619vqBNmSncV70QdQc39QaaA7WuI,7781
25
- saq-0.24.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
- saq-0.24.5.dist-info/entry_points.txt,sha256=HkKOud1K15_DV7AEltn8G5Ua10VqIgHaZ4BQit4fdOk,42
27
- saq-0.24.5.dist-info/top_level.txt,sha256=FMrrc5EiGr4sQkEDtUMHIpomnWHL9i6xT7B6lvEh8xM,4
28
- saq-0.24.5.dist-info/RECORD,,
23
+ saq-0.24.8.dist-info/LICENSE,sha256=p208OXrLf_dMcvuRHpcinfsJdihCqKWbqtFXpw4kyW0,1065
24
+ saq-0.24.8.dist-info/METADATA,sha256=SlpZba2Kq2khnXyF5ik6kjBzcY5FqwypKbn91EbrTSM,7781
25
+ saq-0.24.8.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
+ saq-0.24.8.dist-info/entry_points.txt,sha256=HkKOud1K15_DV7AEltn8G5Ua10VqIgHaZ4BQit4fdOk,42
27
+ saq-0.24.8.dist-info/top_level.txt,sha256=FMrrc5EiGr4sQkEDtUMHIpomnWHL9i6xT7B6lvEh8xM,4
28
+ saq-0.24.8.dist-info/RECORD,,
File without changes
File without changes