Add at-least-once delivery + idempotent federation handler

- EventProcessor now recovers stuck "processing" activities back to
  "pending" after 5 minutes (handles process crashes)
- New ap_delivery_log table records successful inbox deliveries
- Federation delivery handler checks the log before sending, so
  retries skip already-delivered inboxes
- Together these give at-least-once + idempotent semantics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-23 16:16:55 +00:00
parent 8951a62b90
commit 9cb8cf9e1d
4 changed files with 140 additions and 12 deletions

View File

@@ -427,3 +427,27 @@ class APNotification(Base):
Index("ix_ap_notification_read", "actor_profile_id", "read"),
Index("ix_ap_notification_created", "created_at"),
)
class APDeliveryLog(Base):
"""Tracks successful deliveries of activities to remote inboxes.
Used for idempotency: the delivery handler skips inboxes that already
have a success row, so retries after a crash never send duplicates.
"""
__tablename__ = "ap_delivery_log"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
activity_id: Mapped[int] = mapped_column(
Integer, ForeignKey("ap_activities.id", ondelete="CASCADE"), nullable=False,
)
inbox_url: Mapped[str] = mapped_column(String(512), nullable=False)
status_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
delivered_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now(),
)
__table_args__ = (
UniqueConstraint("activity_id", "inbox_url", name="uq_delivery_activity_inbox"),
Index("ix_ap_delivery_activity", "activity_id"),
)