Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stovepipe/core/messagequeue/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ go_library(

go_test(
name = "go_default_test",
srcs = ["process_test.go"],
srcs = ["messagequeue_test.go"],
embed = [":go_default_library"],
deps = [
"@com_github_stretchr_testify//assert:go_default_library",
Expand Down
2 changes: 2 additions & 0 deletions stovepipe/core/messagequeue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ Payloads are defined in proto3 (`proto/`, generated into `protopb/`) and seriali
## Stages

- **process** (`TopicKeyProcess`, `ProcessRequest`) — ingest publishes the minted request id here once it accepts a new head; the process controller reloads the `Request` from storage and decides the build strategy. Only the id travels: producer and consumer share the store, so messages stay small and redelivery is idempotent.
- **build** (`TopicKeyBuild`, `BuildRequest`) — process/analyze publishes the request id here once its build scope (`BuildStrategy`/`BaseURI`) is decided; the build controller reloads the `Request`, triggers the build, and persists the resulting `Build`. Partitioned by request id.
- **buildsignal** (`TopicKeyBuildSignal`, `BuildSignal`) — build publishes the build id here after triggering; buildsignal re-publishes to itself between polls until the build reaches a terminal status. Partitioned by build id, so each build's poll loop is an independent partition. See [doc/rfc/stovepipe/steps/build.md](../../../doc/rfc/stovepipe/steps/build.md) and [buildsignal.md](../../../doc/rfc/stovepipe/steps/buildsignal.md).

See [doc/rfc/messagequeue-contract.md](../../../doc/rfc/messagequeue-contract.md) for the contract conventions and `api/runway/messagequeue` for the external reference example.
8 changes: 6 additions & 2 deletions stovepipe/core/messagequeue/messagequeue.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ type (
// minted request id to validate.
ProcessRequest = protopb.ProcessRequest

// BuildRequest is the payload process publishes to the build stage: the
// admitted request id whose persisted strategy and baseline build reloads.
// BuildRequest is the payload process/analyze publishes to the build stage:
// the request id to build.
BuildRequest = protopb.BuildRequest

// BuildSignal is the payload build publishes to the buildsignal stage, and
// buildsignal re-publishes to itself while polling: the build id to poll.
BuildSignal = protopb.BuildSignal
)

// marshalOpts keeps the JSON field names identical to the proto field names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ func TestBuildRequestRoundTrip(t *testing.T) {
assert.True(t, proto.Equal(req, got), "round-tripped BuildRequest should equal the original")
}

func TestBuildSignalRoundTrip(t *testing.T) {
sig := &BuildSignal{Id: "bk-1001"}

data, err := Marshal(sig)
require.NoError(t, err)

got := &BuildSignal{}
require.NoError(t, Unmarshal(data, got))
assert.True(t, proto.Equal(sig, got), "round-tripped BuildSignal should equal the original")
}

// TestWireFormat locks the protojson encoding decision the contract relies on:
// snake_case field names (UseProtoNames).
func TestWireFormat(t *testing.T) {
Expand All @@ -58,7 +69,7 @@ func TestWireFormat(t *testing.T) {
// no topic_keys option names an unknown key.
func TestTopicKeysBindEveryTopicKey(t *testing.T) {
bound := map[string]int{}
for _, m := range []proto.Message{&ProcessRequest{}, &BuildRequest{}} {
for _, m := range []proto.Message{&ProcessRequest{}, &BuildRequest{}, &BuildSignal{}} {
keys := TopicKeys(m)
require.NotEmpty(t, keys, "message must declare a non-empty topic_keys option")
for _, key := range keys {
Expand All @@ -69,6 +80,7 @@ func TestTopicKeysBindEveryTopicKey(t *testing.T) {
keys := []TopicKey{
TopicKeyProcess,
TopicKeyBuild,
TopicKeyBuildSignal,
}

valid := map[string]bool{}
Expand Down
1 change: 1 addition & 0 deletions stovepipe/core/messagequeue/proto/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
exports_files(
[
"build.proto",
"buildsignal.proto",
"process.proto",
],
visibility = ["//tool/proto:__pkg__"],
Expand Down
10 changes: 6 additions & 4 deletions stovepipe/core/messagequeue/proto/build.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ option java_multiple_files = true;
option java_outer_classname = "BuildProto";
option java_package = "com.uber.submitqueue.stovepipe.messagequeue";

// BuildRequest is the payload process publishes after admitting a request. Only
// the request id travels; build reloads the request from storage and uses the
// strategy and baseline process persisted at admission.
// BuildRequest is the payload process/analyze publishes to the build stage once
// the request's build scope (BuildStrategy/BaseURI) has been decided: only the
// request id travels on the queue. build reloads the full Request from storage
// by this id (producer and consumer share the store, so the id is enough and
// redelivery stays idempotent). See doc/rfc/stovepipe/steps/build.md.
message BuildRequest {
option (uber.base.messagequeue.topic_keys) = "build";

// id is the admitted request id to build. Format: "request/<queue>/<counter>".
// id is the request id to build. Format: "request/<queue>/<counter>".
string id = 1;
}
37 changes: 37 additions & 0 deletions stovepipe/core/messagequeue/proto/buildsignal.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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.

syntax = "proto3";

package uber.stovepipe.messagequeue;

import "api/base/messagequeue/proto/messagequeue.proto";

option go_package = "github.com/uber/submitqueue/stovepipe/core/messagequeue/protopb";
option java_multiple_files = true;
option java_outer_classname = "BuildSignalProto";
option java_package = "com.uber.submitqueue.stovepipe.messagequeue";

// BuildSignal is the payload build publishes to the buildsignal stage, and
// buildsignal re-publishes to itself while polling: only the build id travels
// on the queue. buildsignal reloads the full Build from storage by this id
// (producer and consumer share the store, so the id is enough and redelivery
// stays idempotent). See doc/rfc/stovepipe/steps/buildsignal.md.
message BuildSignal {
option (uber.base.messagequeue.topic_keys) = "buildsignal";

// id is the build id to poll: the runner-assigned id minted by
// BuildRunner.Trigger (entity.Build.ID / entity.BuildID.ID).
string id = 1;
}
1 change: 1 addition & 0 deletions stovepipe/core/messagequeue/protopb/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"build.pb.go",
"buildsignal.pb.go",
"process.pb.go",
],
importpath = "github.com/uber/submitqueue/stovepipe/core/messagequeue/protopb",
Expand Down
10 changes: 6 additions & 4 deletions stovepipe/core/messagequeue/protopb/build.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

146 changes: 146 additions & 0 deletions stovepipe/core/messagequeue/protopb/buildsignal.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions stovepipe/core/messagequeue/topics.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@ const (
// controller consumes it, reloads the Request, and decides the build strategy.
TopicKeyProcess TopicKey = "process"

// TopicKeyBuild carries admitted requests from process to build. process
// publishes a BuildRequest (the request id) after it persists the strategy
// and baseline; build reloads the Request from storage.
// TopicKeyBuild carries requests whose build scope has been decided from
// process/analyze to the build stage. The producer publishes a BuildRequest
// (the request id) here; the build controller consumes it, reloads the
// Request, and triggers the build. Partitioned by request id.
TopicKeyBuild TopicKey = "build"

// TopicKeyBuildSignal carries builds to poll from build to the buildsignal
// stage, and from buildsignal back to itself between polls. Producers
// publish a BuildSignal (the build id) here; the buildsignal controller
// consumes it, polls the build runner, and records terminal status.
// Partitioned by build id, so each build's poll loop is an independent
// partition.
TopicKeyBuildSignal TopicKey = "buildsignal"
)
1 change: 1 addition & 0 deletions tool/proto/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ go_proto_generated_files(
name = "stovepipe_core_messagequeue",
srcs = [
"//stovepipe/core/messagequeue/proto:build.proto",
"//stovepipe/core/messagequeue/proto:buildsignal.proto",
"//stovepipe/core/messagequeue/proto:process.proto",
],
gen_services = False,
Expand Down
Loading