You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds a gateway `Cancel(sqid)` RPC that publishes to a new TopicKeyCancel
and an orchestrator cancel controller that performs the actual state
transitions. Cancellation uses a two-step pattern: first the request is
marked RequestStateCancelling (non-terminal intent), then either
transitioned directly to RequestStateCancelled (un-batched path) or
handed off via batch-cancel + conclude fan-out (batched path). The
Cancelling intent is non-terminal so a concurrent merge or failure may
still win the race and prevail with Landed or Error — conclude
reconciles to the actual terminal outcome.
Every stage controller (validate, batch, score, build, buildsignal,
merge, conclude, speculate) is now cancellation-aware. Speculate treats
cancelled deps as out-of-the-way (drop and continue) instead of
cascade-failing. Conclude maps BatchStateCancelled → RequestStateCancelled.
Cancellation is asynchronous and not guaranteed; gateway.proto comments
direct callers to check the actual outcome via the separate status API.
Infrastructure changes that fell out of building this:
- gRPC error mapping: a unary interceptor in example/server/gateway/main.go
translates any error matching controller.IsInvalidRequest into
codes.InvalidArgument so invalid-sqid (and Land's invalid inputs) no
longer surface as codes.Unknown.
- PublishLog message ID scoping: log entries are now keyed by
`<requestID>/<status>` so distinct statuses for the same request
coexist on the queue's `(topic, partition_key, id)` unique index
(start writes `started`, cancel writes `cancelling` then `cancelled`).
Same-status retries still dedupe.
Regression coverage: unit tests for every new controller and the
two-step transition, plus e2e tests for cancel-before-batch (including
idempotent re-cancel) and the InvalidArgument mapping. The idempotent
re-cancel e2e test depends on the MySQL queue `Insert` becoming
idempotent on (topic, partition_key, id) — extracted to a separate PR.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: entity/request_log.go
+10Lines changed: 10 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -78,6 +78,16 @@ const (
78
78
79
79
// RequestStatusError indicates that the request has encountered an error. It corresponds to the RequestStateError state.
80
80
RequestStatusErrorRequestStatus="error"
81
+
82
+
// RequestStatusCancelling indicates that the user has requested cancellation but the request has not yet transitioned
83
+
// to the RequestStateCancelled state. Cancellation is best-effort: a request that has already been merged or that
84
+
// races to completion before the cancel propagates through the pipeline may still land. Observers should treat this
85
+
// as intent only and rely on RequestStatusCancelled (or RequestStatusLanded) for the terminal outcome. Emitted by
86
+
// the gateway when the Cancel RPC is received.
87
+
RequestStatusCancellingRequestStatus="cancelling"
88
+
89
+
// RequestStatusCancelled indicates that the request was cancelled by the user before it could land. It corresponds to the RequestStateCancelled state.
90
+
RequestStatusCancelledRequestStatus="cancelled"
81
91
)
82
92
83
93
// RequestLog is an append-only record that captures a point-in-time snapshot of a request's status
0 commit comments