-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c7b16c4
commit 612b2ce
Showing
9 changed files
with
272 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
src/hope_payment_gateway/apps/gateway/migrations/0027_asyncjob.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
# Generated by Django 5.1.4 on 2025-01-03 09:12 | ||
|
||
import concurrency.fields | ||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
("gateway", "0026_alter_paymentinstruction_status"), | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="AsyncJob", | ||
fields=[ | ||
( | ||
"id", | ||
models.AutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
( | ||
"version", | ||
concurrency.fields.AutoIncVersionField(default=0, help_text="record revision number"), | ||
), | ||
( | ||
"curr_async_result_id", | ||
models.CharField( | ||
blank=True, | ||
editable=False, | ||
help_text="Current (active) AsyncResult is", | ||
max_length=36, | ||
null=True, | ||
), | ||
), | ||
( | ||
"last_async_result_id", | ||
models.CharField( | ||
blank=True, | ||
editable=False, | ||
help_text="Latest executed AsyncResult is", | ||
max_length=36, | ||
null=True, | ||
), | ||
), | ||
( | ||
"datetime_created", | ||
models.DateTimeField(auto_now_add=True, help_text="Creation date and time"), | ||
), | ||
( | ||
"datetime_queued", | ||
models.DateTimeField( | ||
blank=True, | ||
help_text="Queueing date and time", | ||
null=True, | ||
verbose_name="Queued At", | ||
), | ||
), | ||
( | ||
"repeatable", | ||
models.BooleanField( | ||
blank=True, | ||
default=False, | ||
help_text="Indicate if the job can be repeated as-is", | ||
), | ||
), | ||
( | ||
"celery_history", | ||
models.JSONField(blank=True, default=dict, editable=False), | ||
), | ||
( | ||
"local_status", | ||
models.CharField( | ||
blank=True, | ||
default="", | ||
editable=False, | ||
max_length=100, | ||
null=True, | ||
), | ||
), | ||
( | ||
"group_key", | ||
models.CharField( | ||
blank=True, | ||
editable=False, | ||
help_text="Tasks with the same group key will not run in parallel", | ||
max_length=255, | ||
null=True, | ||
), | ||
), | ||
( | ||
"type", | ||
models.CharField( | ||
choices=[ | ||
("STANDARD_TASK", "Standard Task"), | ||
("ADMIN_ACTION", "Admin Action"), | ||
("JOB_TASK", "Job Task"), | ||
], | ||
max_length=50, | ||
), | ||
), | ||
("config", models.JSONField(blank=True, default=dict)), | ||
("action", models.CharField(blank=True, max_length=500, null=True)), | ||
( | ||
"description", | ||
models.CharField(blank=True, max_length=255, null=True), | ||
), | ||
("sentry_id", models.CharField(blank=True, max_length=255, null=True)), | ||
( | ||
"instruction", | ||
models.ForeignKey( | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="jobs", | ||
to="gateway.paymentinstruction", | ||
), | ||
), | ||
( | ||
"owner", | ||
models.ForeignKey( | ||
blank=True, | ||
null=True, | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="%(app_label)s_%(class)s_jobs", | ||
to=settings.AUTH_USER_MODEL, | ||
), | ||
), | ||
], | ||
options={ | ||
"permissions": (("debug_job", "Can debug background jobs"),), | ||
"abstract": False, | ||
}, | ||
), | ||
] |
20 changes: 20 additions & 0 deletions
20
src/hope_payment_gateway/apps/gateway/migrations/0028_alter_asyncjob_type.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Generated by Django 5.1.4 on 2025-01-07 13:06 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
("gateway", "0027_asyncjob"), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name="asyncjob", | ||
name="type", | ||
field=models.CharField( | ||
choices=[("STANDARD_TASK", "Standard Task"), ("JOB_TASK", "Job Task")], | ||
max_length=50, | ||
), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import contextlib | ||
import logging | ||
from typing import TYPE_CHECKING, Any | ||
|
||
from django.core.cache import cache | ||
|
||
import sentry_sdk | ||
from redis_lock import Lock | ||
|
||
from hope_payment_gateway.apps.gateway.models import AsyncJob | ||
from hope_payment_gateway.config.celery import app | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
if TYPE_CHECKING: | ||
from redis_lock.django_cache import RedisCache | ||
|
||
cache: RedisCache | ||
|
||
|
||
@contextlib.contextmanager | ||
def lock_job(job: AsyncJob) -> Lock: | ||
lock = None | ||
if job.group_key: | ||
lock_key = f"lock:{job.group_key}" | ||
# Get a lock with a 60-second lifetime but keep renewing it automatically | ||
# to ensure the lock is held for as long as the Python process is running. | ||
lock = cache.lock(lock_key, 60, auto_renewal=True) | ||
yield lock.__enter__() | ||
else: | ||
yield | ||
if lock: | ||
lock.release() | ||
|
||
|
||
@app.task() | ||
def sync_job_task(pk: int, version: int) -> dict[str, Any]: | ||
try: | ||
job: AsyncJob = AsyncJob.objects.select_related("owner").get(pk=pk, version=version) | ||
except AsyncJob.DoesNotExist as e: | ||
sentry_sdk.capture_exception(e) | ||
raise e | ||
|
||
with lock_job(job): | ||
try: | ||
scope = sentry_sdk.get_current_scope() | ||
if job.owner: | ||
sentry_sdk.set_user = {"id": job.owner.pk, "email": job.owner.email} | ||
return job.execute() | ||
except Exception: | ||
# error is logged in job.execute | ||
raise | ||
finally: | ||
scope.clear() | ||
|
||
|
||
@app.task() | ||
def removed_expired_jobs(**kwargs): | ||
AsyncJob.objects.filter(**kwargs).delete() |