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 +1 -1
- saq/job.py +6 -0
- saq/queue/base.py +4 -1
- saq/queue/postgres.py +36 -37
- {saq-0.24.5.dist-info → saq-0.24.8.dist-info}/METADATA +1 -1
- {saq-0.24.5.dist-info → saq-0.24.8.dist-info}/RECORD +10 -10
- {saq-0.24.5.dist-info → saq-0.24.8.dist-info}/LICENSE +0 -0
- {saq-0.24.5.dist-info → saq-0.24.8.dist-info}/WHEEL +0 -0
- {saq-0.24.5.dist-info → saq-0.24.8.dist-info}/entry_points.txt +0 -0
- {saq-0.24.5.dist-info → saq-0.24.8.dist-info}/top_level.txt +0 -0
saq/__init__.py
CHANGED
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
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
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
|
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
|
-
|
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.
|
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 =
|
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
|
-
{"
|
891
|
+
{"key": key},
|
892
892
|
)
|
893
|
-
self._releasing.clear()
|
894
893
|
|
895
894
|
@cached_property
|
896
895
|
def _job_queue(self) -> asyncio.Queue:
|
@@ -1,16 +1,16 @@
|
|
1
|
-
saq/__init__.py,sha256=
|
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=
|
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=
|
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=
|
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.
|
24
|
-
saq-0.24.
|
25
|
-
saq-0.24.
|
26
|
-
saq-0.24.
|
27
|
-
saq-0.24.
|
28
|
-
saq-0.24.
|
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
|
File without changes
|
File without changes
|