Skip to content

Commit b3b129f

Browse files
authored
[2/N Status & List APIs] feat(storage): add request read-model schema (#321)
## Summary Add gateway request summary entities and SQL tables keyed for sqid, queue receipt history, and change URI lookup. ## Test Plan ✅ `make fmt && make build && make test && make check-mocks && make e2e-test` ## Issues ## Stack 1. #314 1. @ #321 1. #322 1. #327
1 parent 2cee7c9 commit b3b129f

6 files changed

Lines changed: 120 additions & 0 deletions

File tree

submitqueue/entity/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ go_library(
1717
"queue_config.go",
1818
"request.go",
1919
"request_log.go",
20+
"request_summary.go",
2021
"speculation_tree.go",
2122
],
2223
importpath = "github.com/uber/submitqueue/submitqueue/entity",
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) 2025 Uber Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package entity
16+
17+
// RequestSummary is the gateway-owned materialized current view of a request.
18+
// RequestID is exposed as sqid by the gateway API.
19+
type RequestSummary struct {
20+
// RequestID is the globally unique request identifier.
21+
RequestID string
22+
// Queue is the queue supplied at receipt.
23+
Queue string
24+
// ChangeURIs are the change URIs supplied at receipt in caller order.
25+
ChangeURIs []string
26+
// ReceivedAtMs is the immutable receipt timestamp in Unix milliseconds.
27+
ReceivedAtMs int64
28+
// Status is the current customer-facing request status.
29+
Status RequestStatus
30+
// RequestVersion is the orchestrator request version carried by the winning log entry, or zero when unavailable.
31+
RequestVersion int32
32+
// StatusTimestampMs is the timestamp of the winning log entry in Unix milliseconds.
33+
StatusTimestampMs int64
34+
// Version is the optimistic-lock version of this materialized view.
35+
Version int32
36+
// LastError is the error associated with the current status, or empty when absent.
37+
LastError string
38+
// Metadata is display and debugging metadata associated with the current status.
39+
Metadata map[string]string
40+
}
41+
42+
// RequestQueueSummary is the queue-ordered projection returned by List.
43+
type RequestQueueSummary struct {
44+
// RequestID is the globally unique request identifier.
45+
RequestID string
46+
// Queue is the queue supplied at receipt.
47+
Queue string
48+
// ChangeURIs are the change URIs supplied at receipt in caller order.
49+
ChangeURIs []string
50+
// ReceivedAtMs is the immutable receipt timestamp in Unix milliseconds.
51+
ReceivedAtMs int64
52+
// Status is the current customer-facing request status.
53+
Status RequestStatus
54+
// Version is copied from the authoritative RequestSummary and guards stale projection writers.
55+
Version int32
56+
// LastError is the error associated with the current status, or empty when absent.
57+
LastError string
58+
// Metadata is display and debugging metadata associated with the current status.
59+
Metadata map[string]string
60+
}
61+
62+
// RequestURI maps one change URI to one received request.
63+
type RequestURI struct {
64+
// ChangeURI is the exact canonical URI supplied at receipt.
65+
ChangeURI string
66+
// ReceivedAtMs is the immutable receipt timestamp in Unix milliseconds.
67+
ReceivedAtMs int64
68+
// RequestID is the globally unique request identifier.
69+
RequestID string
70+
}

submitqueue/extension/storage/mysql/schema/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,22 @@ As the `batch` table grows, the secondary index will grow with it, increasing st
2222

2323
The `change` table records per-URI claims by in-flight requests. `request_id` is part of the primary key so that concurrent claims on the same URI by different requests coexist as distinct rows — a same-request retry collides on the PK and is a no-op (`INSERT IGNORE`), while a different-request claim is a new row that `GetByURI` surfaces for overlap detection. `queue` leads the key so queue-scoped lookups are primary-key-prefix scans and the table is shardable by queue.
2424

25+
## Gateway request read model
26+
27+
The gateway request read model uses three additive tables and requires no alteration of existing tables. Deployments create these tables empty and populate them only for requests received after rollout; historical request logs and orchestrator working tables are intentionally not backfilled.
28+
29+
### `request_summary`
30+
31+
`request_summary` is keyed by `request_id` and serves direct Status lookup. It stores immutable receipt context plus the current materialized request-log winner and its optimistic-lock projection version.
32+
33+
### `request_summary_by_queue`
34+
35+
`request_summary_by_queue` is keyed by `(queue, received_at_ms, request_id)`. This key covers the List predicate, descending sort, and keyset continuation for one bounded receipt-time window without a secondary index. The row duplicates the complete List response so one page is served by one range scan rather than one follow-up read per request ID.
36+
37+
### `change_uri_request_mapping`
38+
39+
`change_uri_request_mapping` is keyed by `(change_uri, received_at_ms, request_id)` and serves bounded newest-first Status lookup by change URI. The gateway reads at most 101 mappings to enforce the API maximum of 100 results without silently truncating.
40+
41+
### JSON collections
42+
43+
`change_uris` and `metadata` are non-null application values. MySQL JSON columns can contain the JSON value `null` despite `NOT NULL`, so stores normalize nil slices and maps to empty values on both write and read.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CREATE TABLE IF NOT EXISTS change_uri_request_mapping (
2+
change_uri VARCHAR(255) NOT NULL,
3+
received_at_ms BIGINT NOT NULL,
4+
request_id VARCHAR(255) NOT NULL,
5+
PRIMARY KEY (change_uri, received_at_ms, request_id)
6+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CREATE TABLE IF NOT EXISTS request_summary (
2+
request_id VARCHAR(255) NOT NULL,
3+
queue VARCHAR(255) NOT NULL,
4+
change_uris JSON NOT NULL,
5+
received_at_ms BIGINT NOT NULL,
6+
status VARCHAR(64) NOT NULL,
7+
request_version INT NOT NULL,
8+
status_timestamp_ms BIGINT NOT NULL,
9+
version INT NOT NULL,
10+
last_error TEXT NOT NULL,
11+
metadata JSON NOT NULL,
12+
PRIMARY KEY (request_id)
13+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE IF NOT EXISTS request_summary_by_queue (
2+
queue VARCHAR(255) NOT NULL,
3+
received_at_ms BIGINT NOT NULL,
4+
request_id VARCHAR(255) NOT NULL,
5+
change_uris JSON NOT NULL,
6+
status VARCHAR(64) NOT NULL,
7+
version INT NOT NULL,
8+
last_error TEXT NOT NULL,
9+
metadata JSON NOT NULL,
10+
PRIMARY KEY (queue, received_at_ms, request_id)
11+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

0 commit comments

Comments
 (0)