This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import time
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection, transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from finance.exporting import process_formula_export
|
||||
from finance.importing import process_dataset
|
||||
from finance.models import Dataset, FormulaExport
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Process durable import and formula-export jobs"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--once", action="store_true")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
postgres = connection.vendor == "postgresql"
|
||||
if postgres:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT pg_try_advisory_lock(782146291)")
|
||||
if not cursor.fetchone()[0]:
|
||||
self.stdout.write("Another worker owns the database lock")
|
||||
return
|
||||
try:
|
||||
self.run_jobs(options, postgres)
|
||||
finally:
|
||||
if postgres:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT pg_advisory_unlock(782146291)")
|
||||
|
||||
def run_jobs(self, options, recover):
|
||||
while True:
|
||||
processed = self._next_import(recover)
|
||||
if not processed:
|
||||
processed = self._next_export(recover)
|
||||
if options["once"]:
|
||||
return
|
||||
if not processed:
|
||||
time.sleep(2)
|
||||
|
||||
def _claim(self, model, states):
|
||||
with transaction.atomic():
|
||||
qs = model.objects.filter(status__in=states).order_by("created_at")
|
||||
if connection.features.has_select_for_update_skip_locked:
|
||||
qs = qs.select_for_update(skip_locked=True)
|
||||
item = qs.first()
|
||||
if not item:
|
||||
return None
|
||||
updated = model.objects.filter(pk=item.pk, status__in=states).update(status="processing")
|
||||
return item if updated else None
|
||||
|
||||
def _next_import(self, recover):
|
||||
states = ["queued", "processing"] if recover else ["queued"]
|
||||
item = self._claim(Dataset, states)
|
||||
if not item:
|
||||
return False
|
||||
Dataset.objects.filter(pk=item.pk).update(started_at=timezone.now())
|
||||
result = process_dataset(item.pk)
|
||||
self.stdout.write(f"{result.pk}: {result.status}, {result.row_count} rows")
|
||||
return True
|
||||
|
||||
def _next_export(self, recover):
|
||||
states = ["queued", "processing"] if recover else ["queued"]
|
||||
item = self._claim(FormulaExport, states)
|
||||
if not item:
|
||||
return False
|
||||
try:
|
||||
stats = process_formula_export(item.pk)
|
||||
self.stdout.write(f"{item.pk}: formula export ready, {stats['rows']} rows")
|
||||
except Exception as exc:
|
||||
self.stderr.write(f"Formula export {item.pk} failed: {exc}")
|
||||
return True
|
||||
Reference in New Issue
Block a user