Skip to content

Commit 5d2bc1e

Browse files
authored
refactor(conflict): Move target signature and dictionary contracts under tango
1 parent 2b01526 commit 5d2bc1e

13 files changed

Lines changed: 285 additions & 232 deletions

‎doc/rfc/service-scoped-extensions.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Deciding which service of a multi-service domain an extension belongs to, and mo
44

55
## Status
66

7-
Implemented for SubmitQueue storage and for the core packages that served one service. `submitqueue/gateway/extension/storage` and `submitqueue/orchestrator/extension/storage` each declare a `Factory`, a `Storage` aggregate, and a MySQL schema. The fifteen store contracts, their mocks, and the error vocabulary remain at `submitqueue/extension/storage`. There is no `submitqueue/extension/storage/mysql` package. `submitqueue/extension/storage/BUILD.bazel` filegroups the two service schemas for deployments that colocate them.
7+
Implemented for SubmitQueue storage and for the core packages that served one service. `submitqueue/gateway/extension/storage` and `submitqueue/orchestrator/extension/storage` each declare a `Factory`, a `Storage` aggregate, and a MySQL schema. The thirteen store contracts, their mocks, and the error vocabulary remain at `submitqueue/extension/storage`. There is no `submitqueue/extension/storage/mysql` package. `submitqueue/extension/storage/BUILD.bazel` filegroups the two service schemas for deployments that colocate them.
88

99
`submitqueue/core/batch` and `submitqueue/core/request` are not on disk. Batch helpers live in `submitqueue/orchestrator/core/batch`. Request-log publish and terminate live in `submitqueue/orchestrator/core/request`. The gateway materializer lives in `submitqueue/gateway/core/request`. `submitqueue/core` holds `changeset`, `messagequeue`, and `topickey`. `changeset` still declares `Stores` and `Resolve`, because `buildrunner`, `conflict`, and `speculation` are still domain-level packages that depend on it. Stovepipe keeps `stovepipe/extension/storage/mysql` because that domain is the service; that package is not a leftover SubmitQueue backend.
1010

@@ -26,7 +26,7 @@ Separating the services onto their own databases already worked — the orchestr
2626
2. An extension shared between a domain's services stays wholly at `{domain}/extension/{ext}/` — including one used by a single service today but expected in both. `submitqueue/extension/queueconfig/` is gateway-only now and stays where it is on that basis. A single-service domain is unaffected: its domain root is its service root, so nothing moves.
2727
3. Storage is the extension this split applied to. `changeprovider`, `validator`, `conflict`, `buildrunner` and `speculation` are orchestrator-only and would qualify under Decision 1, but none has a schema and none was the reason for this change; they stay at `{domain}/extension/` until a later RFC takes them. The trigger for moving storage was a schema that a separate-database deployment must not over-provision.
2828
4. SubmitQueue's storage aggregate splits in two: `submitqueue/gateway/extension/storage/` declares a `Factory` and a four-accessor `Storage`, `submitqueue/orchestrator/extension/storage/` a `Factory` and a nine-accessor `Storage`. Neither can resolve the other's stores.
29-
5. `submitqueue/extension/storage/` keeps the fifteen store contracts, their mocks, the error vocabulary, `Config`, and the package README, which documents the contracts. Its import path does not change, so callers that name only a contract or an error are untouched.
29+
5. `submitqueue/extension/storage/` keeps the thirteen store contracts, their mocks, the error vocabulary, `Config`, and the package README, which documents the contracts. Its import path does not change, so callers that name only a contract or an error are untouched.
3030
6. The contract package is not promoted to `platform/`. It is not cross-domain — `stovepipe/extension/storage/storage.go` already declares its own verbatim copy of the same symbols, so a per-domain error vocabulary is the existing pattern rather than something this RFC introduces.
3131
7. Each half owns its own MySQL schema directory, so the tables a service creates are the tables it reads. A union target over both stays available for deployments that colocate the services — the end-to-end tests and the local compose stack take it and name no service directory. Split schemas are the opt-in, not the default.
3232

@@ -60,11 +60,11 @@ Every store, verified against actual usage rather than intent.
6060
```
6161
submitqueue/
6262
├── extension/storage/ # contracts, shared
63-
│ ├── {15 stores}_store.go # BatchStore, RequestLogStore, …
63+
│ ├── {13 stores}_store.go # BatchStore, RequestLogStore, …
6464
│ ├── storage.go # ErrNotFound, ErrAlreadyExists, ErrVersionMismatch,
6565
│ │ # IsNotFound, WrapNotFound, Config
6666
│ ├── README.md
67-
│ └── mock/ # the 15 store mocks
67+
│ └── mock/ # the 13 store mocks
6868
├── gateway/extension/storage/
6969
│ ├── storage.go # Factory + Storage, 4 accessors
7070
│ ├── mock/ # the aggregate's mock

‎submitqueue/entity/BUILD.bazel‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ go_library(
2525
"request_summary.go",
2626
"speculation.go",
2727
"subject.go",
28-
"target_label.go",
29-
"target_signature.go",
3028
],
3129
importpath = "github.com/uber/submitqueue/submitqueue/entity",
3230
visibility = ["//visibility:public"],
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("@rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = [
6+
"mock_store.go",
7+
"store.go",
8+
"target_label.go",
9+
"target_signature.go",
10+
],
11+
importpath = "github.com/uber/submitqueue/submitqueue/extension/conflict/tango",
12+
visibility = ["//visibility:public"],
13+
deps = ["@org_uber_go_mock//gomock:go_default_library"],
14+
)

‎submitqueue/extension/conflict/tango/mock_store.go‎

Lines changed: 205 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) 2026 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 tango
16+
17+
//go:generate mockgen -source=store.go -destination=mock_store.go -package=tango
18+
19+
import "context"
20+
21+
// SignatureReader reads the target signature of a batch. It is the read-only
22+
// view of SignatureStore for consumers that must not write.
23+
type SignatureReader interface {
24+
// Get returns the signature of batchID in the bound queue. The bool is
25+
// false when no signature has been created for the batch.
26+
Get(ctx context.Context, batchID string) (TargetSignature, bool, error)
27+
}
28+
29+
// SignatureStore persists target signatures. A signature is immutable: it is
30+
// created once under (Queue, BatchID) and never updated. Reads and writes are
31+
// per-batch point operations, so any backend can implement it.
32+
type SignatureStore interface {
33+
SignatureReader
34+
35+
// Create persists signature in one write. Returns an already-exists error
36+
// if the bound queue already holds a signature for signature.BatchID, so the
37+
// first signature written stays and the caller can read it back with Get.
38+
Create(ctx context.Context, signature TargetSignature) error
39+
}
40+
41+
// DictionaryReader reads the label recorded for a target ID. It is the
42+
// read-only view of DictionaryStore.
43+
type DictionaryReader interface {
44+
// Get returns the label recorded for id in the bound queue. The bool is
45+
// false when none has been recorded.
46+
Get(ctx context.Context, id uint64) (TargetLabel, bool, error)
47+
}
48+
49+
// DictionaryStore persists the target dictionary: one TargetLabel for each
50+
// 64-bit target ID. A record is immutable: it is created once under
51+
// (Queue, ID) and never updated. Two labels can share an ID, so the first label
52+
// recorded stays and the caller can read it back with Get to detect a collision.
53+
type DictionaryStore interface {
54+
DictionaryReader
55+
56+
// Create persists label in one write. Returns an already-exists error if
57+
// the bound queue holds a label for label.ID, whether or not it is the same
58+
// label, so repeating a write is harmless.
59+
Create(ctx context.Context, label TargetLabel) error
60+
}

submitqueue/entity/target_label.go renamed to submitqueue/extension/conflict/tango/target_label.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package entity
15+
package tango
1616

1717
// TargetLabel is one build target label and the 64-bit ID it hashes to. The
1818
// whole record is immutable: (Queue, ID) is its identity and there is no update

submitqueue/entity/target_signature.go renamed to submitqueue/extension/conflict/tango/target_signature.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package entity
15+
package tango
1616

1717
// TargetSignature is the set of build targets a batch changes, as 64-bit IDs.
1818
// The whole record is immutable: (Queue, BatchID) is its identity and there is

‎submitqueue/extension/storage/BUILD.bazel‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ go_library(
1717
"request_uri_store.go",
1818
"speculation_path_set_store.go",
1919
"storage.go",
20-
"target_dictionary_store.go",
21-
"target_signature_store.go",
2220
],
2321
importpath = "github.com/uber/submitqueue/submitqueue/extension/storage",
2422
visibility = ["//visibility:public"],

‎submitqueue/extension/storage/mock/BUILD.bazel‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ go_library(
1616
"request_summary_store_mock.go",
1717
"request_uri_store_mock.go",
1818
"speculation_path_set_store_mock.go",
19-
"target_dictionary_store_mock.go",
20-
"target_signature_store_mock.go",
2119
],
2220
importpath = "github.com/uber/submitqueue/submitqueue/extension/storage/mock",
2321
visibility = ["//visibility:public"],

0 commit comments

Comments
 (0)