feat: bind job events to command leases
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Source-of-truth protobuf contract for Archive Control clients and the Titan
|
Source-of-truth protobuf contract for Archive Control clients and the Titan
|
||||||
control daemon. Implementations exchange canonical protobuf JSON as one text
|
control daemon. Implementations exchange canonical protobuf JSON as one text
|
||||||
message per WebSocket frame.
|
message per WebSocket frame. The current incompatible protocol major is `2`.
|
||||||
|
|
||||||
Upstream: `cabbage/archive-control-proto`
|
Upstream: `cabbage/archive-control-proto`
|
||||||
|
|
||||||
@@ -29,14 +29,16 @@ Upstream: `cabbage/archive-control-proto`
|
|||||||
3. A server command is delivered at least once. Clients durably deduplicate
|
3. A server command is delivered at least once. Clients durably deduplicate
|
||||||
`command_id` and return `DUPLICATE` for a replayed accepted command.
|
`command_id` and return `DUPLICATE` for a replayed accepted command.
|
||||||
4. `CommandAck` means durable acceptance, not operation completion.
|
4. `CommandAck` means durable acceptance, not operation completion.
|
||||||
5. Job events have monotonically increasing global per-job sequences. A command
|
5. Job events have monotonically increasing global per-job sequences and name
|
||||||
carries the accepted sequence base and grants one client the event-writer
|
the durable command that owns them. Control verifies the reporting client,
|
||||||
lease; a gap requires `JobSnapshot` reconciliation before further
|
command cursor, and permitted state transition. A gap or conflict requires
|
||||||
destructive transitions.
|
authoritative `ReconcileJobCommand` processing before further destructive
|
||||||
|
transitions.
|
||||||
6. UUIDs use lowercase canonical text. Info hashes use validated lowercase hex.
|
6. UUIDs use lowercase canonical text. Info hashes use validated lowercase hex.
|
||||||
7. `SelectionSet.ranges` are inclusive, sorted, non-overlapping, and coalesced.
|
7. `SelectionSet.ranges` are inclusive, sorted, non-overlapping, and coalesced.
|
||||||
8. Receivers ignore unknown JSON fields for compatible minor versions. They
|
8. Receivers reject a different protocol major. Within major `2`, they ignore
|
||||||
reject an unknown required command/job operation.
|
unknown JSON fields only for compatible minor versions and reject an unknown
|
||||||
|
required command/job operation.
|
||||||
9. Published field numbers and enum values are never reused. Removed values are
|
9. Published field numbers and enum values are never reused. Removed values are
|
||||||
reserved in the change that removes them.
|
reserved in the change that removes them.
|
||||||
10. Implementations cap an envelope at the negotiated maximum and keep streamed
|
10. Implementations cap an envelope at the negotiated maximum and keep streamed
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "2fc6a524-84cd-47ab-bd73-30d463b7c43f",
|
"messageId": "2fc6a524-84cd-47ab-bd73-30d463b7c43f",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "9c62fcda-5f83-4286-80cf-a628001e44d3",
|
"messageId": "9c62fcda-5f83-4286-80cf-a628001e44d3",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "7c4a3534-c754-4c4d-a12e-1a1047e3f96e",
|
"messageId": "7c4a3534-c754-4c4d-a12e-1a1047e3f96e",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "56aae470-d1c9-402a-bf14-d7a28ff8998f",
|
"messageId": "56aae470-d1c9-402a-bf14-d7a28ff8998f",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "830626f3-dc76-46f0-a9b1-a6fb61b3ba73",
|
"messageId": "830626f3-dc76-46f0-a9b1-a6fb61b3ba73",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "d00def27-cde1-43b7-a6af-ee3582d0c7c1",
|
"messageId": "d00def27-cde1-43b7-a6af-ee3582d0c7c1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "f337dc9c-84b8-42c6-a2ae-52f8f91258a8",
|
"messageId": "f337dc9c-84b8-42c6-a2ae-52f8f91258a8",
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"sentAt": "2026-07-22T12:08:00Z",
|
"sentAt": "2026-07-22T12:08:00Z",
|
||||||
"jobEvent": {
|
"jobEvent": {
|
||||||
"eventId": "ecfbd79e-e0fe-41da-ab77-012cde0ed53f",
|
"eventId": "ecfbd79e-e0fe-41da-ab77-012cde0ed53f",
|
||||||
|
"commandId": "6bfccbf4-866f-46fe-bf67-d8943c1ece91",
|
||||||
"jobId": "fd6844d0-ed76-475e-8375-2f98d8eea470",
|
"jobId": "fd6844d0-ed76-475e-8375-2f98d8eea470",
|
||||||
"sequence": "23",
|
"sequence": "23",
|
||||||
"jobRevision": "9",
|
"jobRevision": "9",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "44b64096-cd58-4f49-bae4-d829f50f787c",
|
"messageId": "44b64096-cd58-4f49-bae4-d829f50f787c",
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"sentAt": "2026-07-22T12:04:00Z",
|
"sentAt": "2026-07-22T12:04:00Z",
|
||||||
"jobEvent": {
|
"jobEvent": {
|
||||||
"eventId": "18cf6a34-b0a5-429e-b56a-9228b2dca640",
|
"eventId": "18cf6a34-b0a5-429e-b56a-9228b2dca640",
|
||||||
|
"commandId": "6bfccbf4-866f-46fe-bf67-d8943c1ece91",
|
||||||
"jobId": "fd6844d0-ed76-475e-8375-2f98d8eea470",
|
"jobId": "fd6844d0-ed76-475e-8375-2f98d8eea470",
|
||||||
"sequence": "17",
|
"sequence": "17",
|
||||||
"jobRevision": "6",
|
"jobRevision": "6",
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"protocolVersion": {
|
||||||
|
"major": 2
|
||||||
|
},
|
||||||
|
"messageId": "4be5df55-3d95-4f60-8f6b-1cfd0b64cfbb",
|
||||||
|
"sentAt": "2026-08-13T06:00:00Z",
|
||||||
|
"command": {
|
||||||
|
"commandId": "94b4f73f-31a8-426b-8511-12b91987f1b4",
|
||||||
|
"createdAt": "2026-08-13T06:00:00Z",
|
||||||
|
"reconcileJob": {
|
||||||
|
"authoritativeJob": {
|
||||||
|
"definition": {
|
||||||
|
"jobId": "3173ae0d-ecc4-4113-a659-7897aac6c66f",
|
||||||
|
"idempotencyKey": "55b6d968-e20d-444b-8fae-17914d15fdf5",
|
||||||
|
"operation": "JOB_OPERATION_ARCHIVE",
|
||||||
|
"resourceDisplayName": "example",
|
||||||
|
"createdAt": "2026-08-13T05:00:00Z",
|
||||||
|
"transfer": {
|
||||||
|
"sourceClientId": "cache-1",
|
||||||
|
"targetClientId": "archive-1",
|
||||||
|
"routeId": "route-example"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state": "JOB_STATE_RUNNING",
|
||||||
|
"revision": "12"
|
||||||
|
},
|
||||||
|
"authoritativeLastEventSequence": "12",
|
||||||
|
"supersededCommandIds": [
|
||||||
|
"6d4ab7f8-2ff0-47d8-8c7c-45a67dce8535"
|
||||||
|
],
|
||||||
|
"reason": "control cursor is authoritative after stale event replay"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"messageId": "0d6d31f8-1242-4a40-a261-433897102fe4",
|
"messageId": "0d6d31f8-1242-4a40-a261-433897102fe4",
|
||||||
"sentAt": "2026-07-22T12:00:00Z",
|
"sentAt": "2026-07-22T12:00:00Z",
|
||||||
"registerRequest": {
|
"registerRequest": {
|
||||||
"protocolVersion": {
|
"protocolVersion": {
|
||||||
"major": 1,
|
"major": 2,
|
||||||
"minor": 0
|
"minor": 0
|
||||||
},
|
},
|
||||||
"client": {
|
"client": {
|
||||||
|
|||||||
@@ -38,6 +38,17 @@ message RequestJobSnapshotCommand {
|
|||||||
repeated string job_ids = 1;
|
repeated string job_ids = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReconcileJobCommand makes control's durable cursor authoritative after a
|
||||||
|
// reconnect conflict, local database restore, or rejected stale event. The
|
||||||
|
// client must durably adopt this cursor before acknowledging the command and
|
||||||
|
// must never emit output for superseded_command_ids afterwards.
|
||||||
|
message ReconcileJobCommand {
|
||||||
|
JobRecord authoritative_job = 1;
|
||||||
|
uint64 authoritative_last_event_sequence = 2;
|
||||||
|
repeated string superseded_command_ids = 3;
|
||||||
|
string reason = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message Command {
|
message Command {
|
||||||
string command_id = 1;
|
string command_id = 1;
|
||||||
google.protobuf.Timestamp created_at = 2;
|
google.protobuf.Timestamp created_at = 2;
|
||||||
@@ -48,6 +59,7 @@ message Command {
|
|||||||
EnsureRouteCommand ensure_route = 13;
|
EnsureRouteCommand ensure_route = 13;
|
||||||
InventoryQuery inventory_query = 14;
|
InventoryQuery inventory_query = 14;
|
||||||
RequestJobSnapshotCommand request_job_snapshot = 15;
|
RequestJobSnapshotCommand request_job_snapshot = 15;
|
||||||
|
ReconcileJobCommand reconcile_job = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +109,10 @@ message JobEvent {
|
|||||||
ResourceStateFingerprint observed_resource = 10;
|
ResourceStateFingerprint observed_resource = 10;
|
||||||
Placement observed_placement = 11;
|
Placement observed_placement = 11;
|
||||||
google.protobuf.Timestamp occurred_at = 12;
|
google.protobuf.Timestamp occurred_at = 12;
|
||||||
|
// The durable ExecuteStepCommand or CancelJobCommand that owns this event.
|
||||||
|
// Control verifies this ID, the reporting client, the command's expected
|
||||||
|
// cursor, and the permitted step/state transition before appending it.
|
||||||
|
string command_id = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message JobSnapshot {
|
message JobSnapshot {
|
||||||
|
|||||||
Reference in New Issue
Block a user