fix: handle concurrent idempotency key conflicts
This commit is contained in:
+11
-1
@@ -18,6 +18,7 @@ from fastapi import Depends, FastAPI, File, Header, HTTPException, Query, Reques
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy import delete, func, select, text
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from .activity import activity_rows
|
||||
@@ -514,7 +515,16 @@ def create_catch_report(
|
||||
if idempotency_key:
|
||||
key_hash = hmac.new(settings.rate_limit_secret.encode(), idempotency_key.encode(), hashlib.sha256).hexdigest()
|
||||
db.add(SubmissionAttempt(client_hash="", idempotency_key=key_hash, catch_report_id=report.id, payload_hash=payload_hash, created_at=datetime.now(timezone.utc)))
|
||||
db.commit()
|
||||
try:
|
||||
db.commit()
|
||||
except IntegrityError:
|
||||
# Another request won the same idempotency key race.
|
||||
db.rollback()
|
||||
winner = db.scalar(select(SubmissionAttempt).where(SubmissionAttempt.idempotency_key == key_hash))
|
||||
if winner and winner.catch_report:
|
||||
replay_token = hmac.new(settings.rate_limit_secret.encode(), (key_hash + ":upload").encode(), hashlib.sha256).hexdigest()
|
||||
return JSONResponse(status_code=200, content={"id": str(winner.catch_report.id), "moderation_status": winner.catch_report.moderation_status.value, "screenshot_upload_token": replay_token, "idempotent": True})
|
||||
raise
|
||||
logger.info("idempotency key stored", extra={"idempotency_key": idempotency_key[:8]})
|
||||
return CatchReportAccepted(id=report.id, moderation_status=report.moderation_status.value, screenshot_upload_token=upload_token, idempotent=False)
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class SubmissionAttempt(Base):
|
||||
__tablename__ = "submission_attempt"
|
||||
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
|
||||
client_hash: Mapped[str] = mapped_column(String(64), index=True)
|
||||
idempotency_key: Mapped[str | None] = mapped_column(String(128), index=True)
|
||||
idempotency_key: Mapped[str | None] = mapped_column(String(128), unique=True, index=True)
|
||||
catch_report_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("catch_report.id"), nullable=True)
|
||||
payload_hash: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
|
||||
|
||||
Reference in New Issue
Block a user