Store event_id in OrderSyncQueue, always fill not_before, log unhandled exceptions

This commit is contained in:
Mira Weller
2025-05-07 15:41:41 +02:00
parent 114304855f
commit 922d3cd629
6 changed files with 56 additions and 35 deletions

View File

@@ -148,9 +148,10 @@ class OutboundSyncProvider:
raise TypeError('Call this method on a derived class that defines an "identifier" attribute.')
OrderSyncQueue.objects.create(
order=order,
event=order.event,
sync_provider=cls.identifier,
triggered_by=triggered_by,
not_before=not_before,
not_before=not_before or now(),
)
@classmethod
@@ -235,6 +236,11 @@ class OutboundSyncProvider:
f"Could not sync order {sq.order.code} to {type(self).__name__} (unhandled exception)"
)
sentry_sdk.capture_exception(e)
sq.order.log_action("pretix.event.order.data_sync.failed.internal", {
"provider": self.identifier,
"error": [],
"full_message": str(e),
})
sq.delete()
else:
sq.order.log_action("pretix.event.order.data_sync.success", {

View File

@@ -1,4 +1,4 @@
# Generated by Django 4.2.16 on 2025-04-01 14:52
# Generated by Django 4.2.16 on 2025-05-07 13:01
import django.db.models.deletion
from django.db import migrations, models
@@ -11,33 +11,6 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name="OrderSyncQueue",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
("sync_provider", models.CharField(max_length=128)),
("triggered_by", models.CharField(max_length=128)),
("triggered", models.DateTimeField(auto_now_add=True)),
("failed_attempts", models.PositiveIntegerField(default=0)),
("not_before", models.DateTimeField(blank=True, null=True)),
(
"order",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="queued_sync_jobs",
to="pretixbase.order",
),
),
],
options={
"unique_together": {("order", "sync_provider")},
},
),
migrations.CreateModel(
name="OrderSyncResult",
fields=[
@@ -79,9 +52,45 @@ class Migration(migrations.Migration):
"indexes": [
models.Index(
fields=["order", "sync_provider"],
name="pretixbase__order_i_23d278_idx",
name="pretixbase__order_i_3e3c84_idx",
)
],
},
),
migrations.CreateModel(
name="OrderSyncQueue",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
("sync_provider", models.CharField(max_length=128)),
("triggered_by", models.CharField(max_length=128)),
("triggered", models.DateTimeField(auto_now_add=True)),
("failed_attempts", models.PositiveIntegerField(default=0)),
("not_before", models.DateTimeField(db_index=True)),
(
"event",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="queued_sync_jobs",
to="pretixbase.event",
),
),
(
"order",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="queued_sync_jobs",
to="pretixbase.order",
),
),
],
options={
"ordering": ("triggered",),
"unique_together": {("order", "sync_provider")},
},
),
]

View File

@@ -25,7 +25,7 @@ from functools import cached_property
from django.db import models
from pretix.base.models import Order, OrderPosition
from pretix.base.models import Event, Order, OrderPosition
logger = logging.getLogger(__name__)
@@ -40,11 +40,14 @@ class OrderSyncQueue(models.Model):
order = models.ForeignKey(
Order, on_delete=models.CASCADE, related_name="queued_sync_jobs"
)
event = models.ForeignKey(
Event, on_delete=models.CASCADE, related_name="queued_sync_jobs"
)
sync_provider = models.CharField(blank=False, null=False, max_length=128)
triggered_by = models.CharField(blank=False, null=False, max_length=128)
triggered = models.DateTimeField(blank=False, null=False, auto_now_add=True)
failed_attempts = models.PositiveIntegerField(default=0)
not_before = models.DateTimeField(blank=True, null=True)
not_before = models.DateTimeField(blank=False, null=False, db_index=True)
class Meta:
unique_together = (("order", "sync_provider"),)

View File

@@ -446,6 +446,7 @@ class OrderDataSyncLogEntryType(OrderLogEntryType):
"pretix.event.order.data_sync.failed.config": _("Transferring data to {provider} failed due to invalid configuration:"),
"pretix.event.order.data_sync.failed.exceeded": _("Maximum number of retries exceeded while transferring data to {provider}:"),
"pretix.event.order.data_sync.failed.permanent": _("Error while transferring data to {provider}:"),
"pretix.event.order.data_sync.failed.internal": _("Internal error while transferring data to {provider}."),
})
class OrderDataSyncErrorLogEntryType(OrderLogEntryType):
def display(self, logentry, data):

View File

@@ -14,7 +14,7 @@
<form action="{% url "control:event.order.sync_job" organizer=event.organizer.slug event=event.slug code=order.code provider=identifier %}" method="post" class="form-inline pull-right">
{% csrf_token %}
{% if pending %}
{% if pending.not_before %}
{% if pending.not_before > now %}
<button type="submit" name="run_job_now" value="{{ pending.pk }}" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Retry now" %}</button>
{% endif %}
<button type="submit" name="cancel_job" value="{{ pending.pk }}" class="btn btn-danger"><i class="fa fa-times"></i> {% trans "Cancel" %}</button>
@@ -35,7 +35,7 @@
Waiting until {{ datetime }}
{% endblocktrans %}
{% endif %}
{% elif pending.not_before %}
{% elif pending.not_before > now %}
{% blocktrans trimmed with datetime=pending.not_before|date:"SHORT_DATETIME_FORMAT" %}
Waiting until {{ datetime }}
{% endblocktrans %}

View File

@@ -27,6 +27,7 @@ from django.dispatch import receiver
from django.http import HttpResponseNotAllowed
from django.shortcuts import redirect
from django.template.loader import get_template
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from pretix.base.datasync.datasync import sync_targets
@@ -55,6 +56,7 @@ def on_control_order_info(sender: Event, request, order: Order, **kwargs):
"request": request,
"event": sender,
"providers": providers,
"now": now(),
}
return template.render(ctx, request=request)
@@ -74,7 +76,7 @@ class ControlSyncJob(OrderView):
messages.success(self.request, _('The sync job has been canceled.'))
elif self.request.POST.get("run_job_now"):
job = self.order.queued_sync_jobs.get(pk=self.request.POST.get("run_job_now"))
job.not_before = None
job.not_before = now()
job.save()
messages.success(self.request, _('The sync job has been set to run as soon as possible.'))