diff --git a/submitqueue/entity/BUILD.bazel b/submitqueue/entity/BUILD.bazel index 47f52b4d..a2514139 100644 --- a/submitqueue/entity/BUILD.bazel +++ b/submitqueue/entity/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "queue_config.go", "request.go", "request_log.go", + "request_summary.go", "speculation_tree.go", ], importpath = "github.com/uber/submitqueue/submitqueue/entity", diff --git a/submitqueue/entity/request_summary.go b/submitqueue/entity/request_summary.go new file mode 100644 index 00000000..fe9565c0 --- /dev/null +++ b/submitqueue/entity/request_summary.go @@ -0,0 +1,70 @@ +// Copyright (c) 2025 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entity + +// RequestSummary is the gateway-owned materialized current view of a request. +// RequestID is exposed as sqid by the gateway API. +type RequestSummary struct { + // RequestID is the globally unique request identifier. + RequestID string + // Queue is the queue supplied at receipt. + Queue string + // ChangeURIs are the change URIs supplied at receipt in caller order. + ChangeURIs []string + // ReceivedAtMs is the immutable receipt timestamp in Unix milliseconds. + ReceivedAtMs int64 + // Status is the current customer-facing request status. + Status RequestStatus + // RequestVersion is the orchestrator request version carried by the winning log entry, or zero when unavailable. + RequestVersion int32 + // StatusTimestampMs is the timestamp of the winning log entry in Unix milliseconds. + StatusTimestampMs int64 + // Version is the optimistic-lock version of this materialized view. + Version int32 + // LastError is the error associated with the current status, or empty when absent. + LastError string + // Metadata is display and debugging metadata associated with the current status. + Metadata map[string]string +} + +// RequestQueueSummary is the queue-ordered projection returned by List. +type RequestQueueSummary struct { + // RequestID is the globally unique request identifier. + RequestID string + // Queue is the queue supplied at receipt. + Queue string + // ChangeURIs are the change URIs supplied at receipt in caller order. + ChangeURIs []string + // ReceivedAtMs is the immutable receipt timestamp in Unix milliseconds. + ReceivedAtMs int64 + // Status is the current customer-facing request status. + Status RequestStatus + // Version is copied from the authoritative RequestSummary and guards stale projection writers. + Version int32 + // LastError is the error associated with the current status, or empty when absent. + LastError string + // Metadata is display and debugging metadata associated with the current status. + Metadata map[string]string +} + +// RequestURI maps one change URI to one received request. +type RequestURI struct { + // ChangeURI is the exact canonical URI supplied at receipt. + ChangeURI string + // ReceivedAtMs is the immutable receipt timestamp in Unix milliseconds. + ReceivedAtMs int64 + // RequestID is the globally unique request identifier. + RequestID string +} diff --git a/submitqueue/extension/storage/mysql/schema/README.md b/submitqueue/extension/storage/mysql/schema/README.md index 9e8c36bf..b1445624 100644 --- a/submitqueue/extension/storage/mysql/schema/README.md +++ b/submitqueue/extension/storage/mysql/schema/README.md @@ -22,3 +22,22 @@ As the `batch` table grows, the secondary index will grow with it, increasing st 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. +## Gateway request read model + +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. + +### `request_summary` + +`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. + +### `request_summary_by_queue` + +`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. + +### `change_uri_request_mapping` + +`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. + +### JSON collections + +`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. diff --git a/submitqueue/extension/storage/mysql/schema/change_uri_request_mapping.sql b/submitqueue/extension/storage/mysql/schema/change_uri_request_mapping.sql new file mode 100644 index 00000000..12225282 --- /dev/null +++ b/submitqueue/extension/storage/mysql/schema/change_uri_request_mapping.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS change_uri_request_mapping ( + change_uri VARCHAR(255) NOT NULL, + received_at_ms BIGINT NOT NULL, + request_id VARCHAR(255) NOT NULL, + PRIMARY KEY (change_uri, received_at_ms, request_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/submitqueue/extension/storage/mysql/schema/request_summary.sql b/submitqueue/extension/storage/mysql/schema/request_summary.sql new file mode 100644 index 00000000..d4bcce7d --- /dev/null +++ b/submitqueue/extension/storage/mysql/schema/request_summary.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS request_summary ( + request_id VARCHAR(255) NOT NULL, + queue VARCHAR(255) NOT NULL, + change_uris JSON NOT NULL, + received_at_ms BIGINT NOT NULL, + status VARCHAR(64) NOT NULL, + request_version INT NOT NULL, + status_timestamp_ms BIGINT NOT NULL, + version INT NOT NULL, + last_error TEXT NOT NULL, + metadata JSON NOT NULL, + PRIMARY KEY (request_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/submitqueue/extension/storage/mysql/schema/request_summary_by_queue.sql b/submitqueue/extension/storage/mysql/schema/request_summary_by_queue.sql new file mode 100644 index 00000000..14d3359a --- /dev/null +++ b/submitqueue/extension/storage/mysql/schema/request_summary_by_queue.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS request_summary_by_queue ( + queue VARCHAR(255) NOT NULL, + received_at_ms BIGINT NOT NULL, + request_id VARCHAR(255) NOT NULL, + change_uris JSON NOT NULL, + status VARCHAR(64) NOT NULL, + version INT NOT NULL, + last_error TEXT NOT NULL, + metadata JSON NOT NULL, + PRIMARY KEY (queue, received_at_ms, request_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;