Skip to content

Commit 6f14ba8

Browse files
committed
feat(storage): add request read-model schema
Summary: Add gateway request summary entities and additive SQL tables keyed for sqid, queue receipt history, and change URI lookup. The tables are initially unused and may be deployed empty before any runtime writer is enabled. Test Plan: make fmt make gazelle make lint-license ./tool/bazel test //submitqueue/entity:go_default_test //test/integration/submitqueue/extension/storage/mysql:go_default_test --test_output=errors Revert Plan: Revert this commit. Empty additive tables may remain without affecting existing behavior. API Changes: None. Monitoring and Alerts: N/A
1 parent 593cda9 commit 6f14ba8

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)