Skip to content

scheduling: release and cancel retries resolve the current policy before replaying a stored receipt #1250

Description

@jeremi

Found during review of #1092.

Ordering

SchedulingService::release_hold and SchedulingService::cancel_appointment both
resolve the offering from the current in-memory policy before anything consults
the stored attempt receipt:

  • release_hold loads the claim, checks its kind, then
    self.policy.offering(&hold.offering), failing with
    ServiceError::internal("a committed hold names no offering in the policy").
  • cancel_appointment loads the booking, then
    self.policy.offering(&appointment.offering), failing with
    ServiceError::internal("a committed appointment names no offering in the policy").

ServiceError::internal returns ProblemCode::ServiceUnavailable. The idempotency
replay lives further down, inside the store call, so it is never reached.

Why the offering can be gone

apply_policy fences only offerings with active claims:

WHERE state='active'
  AND (kind='booking' OR (kind='hold' AND hold_expires_at > $1))

A released hold and a cancelled appointment are both non-active, so an offering
whose commitments have all been closed may be removed from the policy, and the
publication is accepted.

PostgresStore::claim selects on claim_id with no state filter, and
SchedulingService::booking filters on kind only. So the released hold and the
cancelled appointment are both still found by a retry.

Result

For the remainder of retention.attempt_receipt_days, a retry of the very same
release or cancellation request, which is the retry the receipt exists to serve,
returns service.unavailable instead of replaying its recorded success. The
caller cannot distinguish that from a transient outage and will keep retrying.

Options

  • Consult the stored receipt before resolving the current policy, and replay a
    terminal recorded answer without needing the offering.
  • Retain in the receipt whatever authorization terms the replay needs, so
    require_permission does not depend on a live offering.
  • Fence offering removal while replayable receipts remain, which trades the
    problem for a longer publication fence.

The first two keep apply_policy as permissive as it is now, which seems
preferable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions