You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(buildrunner): drop per-call queueName, add Factory, wire per-queue
## Summary
### Why?
`BuildRunner.Trigger` took a `queueName` argument that selected the
runner-specific job configuration on every call. That put queue routing
on the hot path and on a single verb, leaving `Status` and `Cancel` to
rediscover the queue from the build ID. The queue belongs at construction
time, not per call.
### What?
- Drop `queueName` from `BuildRunner.Trigger`; the verbs speak only in
builds and changes.
- Add a `Factory` interface (`New(cfg Config) (BuildRunner, error)`) and
a `Config` struct carrying `QueueID`. A runner is bound to its Config at
construction; backends extend `Config` with their own settings.
- noop: add `NewFactory()`; keep `New()`.
- Wire the factory end to end: the build and buildsignal controllers hold
a `buildrunner.Factory` and build a runner per queue via
`New(Config{QueueID: batch.Queue})`. buildsignal loads the batch to
resolve the queue (TODO: denormalize queue onto the Build). The example
orchestrator wires `buildnoop.NewFactory()`.
- Docs: BuildRunner is no longer described as a "singleton" but must stay
safe for concurrent use. Updated the RFC (Construction section) and
README.
Caching of runners per queue is intentionally omitted for now.
## Test Plan
✅ `make mocks`, `make gazelle`, `make tidy`, `make fmt`
✅ `bazel build //...`
✅ `make test` (unit), incl. buildrunner + build + buildsignal
Copy file name to clipboardExpand all lines: doc/rfc/build-runner.md
+17-2Lines changed: 17 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -38,12 +38,27 @@ The build stage needs a vendor-agnostic abstraction for talking to a Build Runne
38
38
39
39
`BuildRunner` exposes three verbs, all keyed by a build identifier (`entity.BuildID`):
40
40
41
-
-**`Trigger`** — submit a build for a queue, given the ordered `base` and `head` change sets plus a free-form metadata map; returns the new build's ID. Runner-side work is asynchronous.
41
+
-**`Trigger`** — submit a build given the ordered `base` and `head` change sets plus a free-form metadata map; returns the new build's ID. Runner-side work is asynchronous.
42
42
-**`Status`** — fetch the current `BuildStatus` and runner-defined metadata for a build; MAY round-trip to the runner.
43
43
-**`Cancel`** — request cancellation; returns once the request reaches the runner, not once the build stops.
44
44
45
45
See `extension/buildrunner/build_runner.go` for the exact Go signatures. The sections below record why the contract is shaped this way.
46
46
47
+
### Construction: a Factory, queue bound at build time
48
+
49
+
A `BuildRunner` does not take a queue selector on any verb. The queue whose job configuration a runner uses is fixed when the runner is constructed, and runners are constructed by a `Factory`.
50
+
51
+
-**`Factory`** — produces `BuildRunner` instances from a `Config`. A controller that drives builds for several queues holds one `Factory` and obtains one `BuildRunner` per queue.
52
+
-**`Config`** — the configuration the factory binds in: a `QueueID` selecting the queue whose job definition the runner builds against, plus any backend-specific settings (endpoints, credentials, defaults) a concrete implementation adds.
53
+
54
+
Why bind the queue at construction rather than pass it per call:
55
+
56
+
- A runner's connection pool, caches, and job defaults are all keyed to one queue's configuration. Passing the queue per call would force every implementation to re-resolve that configuration on the hot path, or to maintain an internal queue→config map the factory already expresses cleanly.
57
+
- It keeps the per-call verbs (`Trigger`, `Status`, `Cancel`) free of routing concerns — they speak only in builds and changes.
58
+
- It matches the rest of the extension family, whose implementations are bound to their configuration at construction.
59
+
60
+
Rejected: a `queueName` argument on `Trigger`. It put routing on the hot path and on a single verb, leaving `Status` and `Cancel` to rediscover the queue from the build ID. Carrying the selection in `Config` keeps each runner bound to a single queue.
61
+
47
62
### Trigger: base + head
48
63
49
64
`Trigger` takes two ordered lists of changes and a free-form metadata map:
@@ -125,7 +140,7 @@ Rejected: long-polling on `Status`. Not every backend supports efficient server-
125
140
126
141
### Lifecycle
127
142
128
-
Implementations are long-lived singletons bound to provider config at construction. Every method is concurrent-safe; connection pools and caches live inside the manager; anything that must survive a restart belongs in persistent storage, not the manager.
143
+
Implementations are constructed by a `Factory` and bound to one queue's provider config at construction (see *Construction* above). They may be shared and called concurrently, so every method must be concurrent-safe; connection pools and caches live inside the manager; anything that must survive a restart belongs in persistent storage, not the manager.
Copy file name to clipboardExpand all lines: extension/buildrunner/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ See [`doc/rfc/build-runner.md`](../../doc/rfc/build-runner.md) for the contract
6
6
7
7
## Adding a new backend
8
8
9
-
1. Create `extension/buildrunner/{backend}/` with a `BuildRunner` implementation bound to its runner configuration at construction.
9
+
1. Create `extension/buildrunner/{backend}/` with a `Factory` whose `New` returns a `BuildRunner`bound to one queue's job configuration. The runner verbs carry no queue selector — that selection lives in the `Config` passed to the factory.
10
10
2. Map the `base` and `head` change slices onto the backend's build primitives (apply `base`, apply `head`, validate the result).
11
11
3. Map the runner's lifecycle states down to the `BuildStatus` values: `Accepted` (accepted for execution), `Running` (executing), and the terminal `Succeeded` / `Failed` / `Cancelled`.
12
12
4. Implement internal reconnect / retry so transient failures surface as plain errors without blocking the caller.
0 commit comments