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: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/twmb/murmur3 v1.1.8 // indirect
github.com/uber-go/mapdecode v1.0.0 // indirect
github.com/uber/tchannel-go v1.34.4 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/dig v1.19.0 // indirect
Expand All @@ -57,5 +58,6 @@ require (
golang.org/x/text v0.37.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.4.3 // indirect
)
9 changes: 9 additions & 0 deletions service/submitqueue/orchestrator/server/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ go_library(
"//platform/extension/consumergate/noop:go_default_library",
"//platform/extension/counter:go_default_library",
"//platform/extension/counter/mysql:go_default_library",
"//platform/extension/gitworkspace:go_default_library",
"//platform/extension/gitworkspace/local:go_default_library",
"//platform/extension/hook:go_default_library",
"//platform/extension/hook/noop:go_default_library",
"//platform/extension/messagequeue/mysql:go_default_library",
Expand All @@ -56,6 +58,10 @@ go_library(
"//submitqueue/extension/conflict/fake:go_default_library",
"//submitqueue/extension/conflict/none:go_default_library",
"//submitqueue/extension/conflict/pathoverlap:go_default_library",
"//submitqueue/extension/conflict/tango:go_default_library",
"//submitqueue/extension/conflict/tango/baserevision:go_default_library",
"//submitqueue/extension/conflict/tango/mysql:go_default_library",
"//submitqueue/extension/conflict/tango/tangoclient:go_default_library",
"//submitqueue/extension/queuepause:go_default_library",
"//submitqueue/extension/speculation/allocator/sticky:go_default_library",
"//submitqueue/extension/speculation/generator/bestfirst:go_default_library",
Expand Down Expand Up @@ -142,11 +148,14 @@ go_test(
"//submitqueue/extension/buildrunner:go_default_library",
"//submitqueue/extension/changeprovider:go_default_library",
"//submitqueue/extension/conflict:go_default_library",
"//submitqueue/extension/conflict/tango:go_default_library",
"//submitqueue/extension/conflict/tango/tangoclient:go_default_library",
"//submitqueue/extension/queuepause/fake:go_default_library",
"//submitqueue/extension/speculation/scorer:go_default_library",
"//submitqueue/extension/speculation/speculator:go_default_library",
"//submitqueue/extension/validator:go_default_library",
"//submitqueue/orchestrator/extension/storage:go_default_library",
"@com_github_data_dog_go_sqlmock//:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
"@com_github_uber_go_tally//:go_default_library",
Expand Down
97 changes: 96 additions & 1 deletion service/submitqueue/orchestrator/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"time"

yamlv3 "gopkg.in/yaml.v3"

"github.com/uber/submitqueue/submitqueue/extension/conflict/tango/tangoclient"
)

// Change provider types selectable from configuration.
Expand Down Expand Up @@ -51,6 +53,7 @@ const (
analyzerTypeAll = "all"
analyzerTypeNone = "none"
analyzerTypePathOverlap = "pathoverlap"
analyzerTypeTango = "tango"
)

// Granularities a pathoverlap analyzer compares paths at.
Expand Down Expand Up @@ -117,12 +120,26 @@ const (
// closed set in the schema, so supporting a new provider is a new value here and
// a new implementation behind it — not a change to the shape of this file.
type profilesConfig struct {
// Tango is the process-level setup shared by every queue that uses the
// tango analyzer. It is required only when one does.
Tango tangoConfig `yaml:"tango"`
// Defaults applies to any queue without an override for a given extension.
Defaults queueProfileConfig `yaml:"defaults"`
// Queues holds per-queue overrides.
Queues []namedQueueProfileConfig `yaml:"queues"`
}

// tangoConfig is how this process reaches Tango and how much memory the
// signature cache may use.
type tangoConfig struct {
// Address is the host:port of the Tango service's gRPC inbound.
Address string `yaml:"address"`
// SignatureCacheBytes bounds the in-memory signature cache shared by every
// queue. It has no default, because a zero budget would silently cache
// nothing.
SignatureCacheBytes int64 `yaml:"signatureCacheBytes"`
}

// namedQueueProfileConfig is one queue's overrides. Each extension is
// independently optional: an absent block inherits that one extension from the
// defaults, so a queue that differs only in its analyzer says only that.
Expand Down Expand Up @@ -244,6 +261,18 @@ type analyzerConfig struct {
// FailAlways makes every analysis return an error, for exercising the
// analyzer failure path. Test-only.
FailAlways bool `yaml:"failAlways"`
// Tango names the repository Tango analyzes for this queue (tango only).
Tango *tangoAnalyzerConfig `yaml:"tango"`
}

// tangoAnalyzerConfig is what Tango needs to know about one queue's repository.
type tangoAnalyzerConfig struct {
// Remote identifies the repository to Tango. It must equal, exactly, a
// repository remote in Tango's own configuration.
Remote string `yaml:"remote"`
// Strategy is how Tango computes the base target graph: "unset", "shell"
// or "native". Defaults to "unset".
Strategy string `yaml:"strategy"`
}

// scorerConfig selects how a queue ranks candidate speculation paths. The
Expand Down Expand Up @@ -352,7 +381,62 @@ func (c *profilesConfig) normalizeAndValidate() error {
}
}
}
return c.validateGitRepoPaths()
if err := c.validateGitRepoPaths(); err != nil {
return err
}
return c.validateTango()
}

type namedProfile struct {
where string
profile queueProfileConfig
}

// namedProfiles lists the defaults followed by every queue's resolved profile,
// in config order.
func (c profilesConfig) namedProfiles() []namedProfile {
profiles := []namedProfile{{where: "defaults", profile: c.Defaults}}
for _, q := range c.Queues {
profiles = append(profiles, namedProfile{where: fmt.Sprintf("queue %q", q.Name), profile: c.resolve(q)})
}
return profiles
}

// usesTango reports whether any profile selects the tango analyzer.
func (c profilesConfig) usesTango() bool {
for _, p := range c.namedProfiles() {
if p.profile.Analyzer.Type == analyzerTypeTango {
return true
}
}
return false
}

// validateTango rejects a tango analyzer on a queue that has no repository
// clone to read the base revision from, and a tango analyzer without the
// process-level setup it needs. The defaults count as a queue, since queues
// without an override inherit them.
func (c profilesConfig) validateTango() error {
used := false
for _, p := range c.namedProfiles() {
if p.profile.Analyzer.Type != analyzerTypeTango {
continue
}
used = true
if p.profile.ChangeProvider.Type != changeProviderTypeGit {
return fmt.Errorf("%s: the tango analyzer needs the git change provider, got %q", p.where, p.profile.ChangeProvider.Type)
}
}
if !used {
return nil
}
if c.Tango.Address == "" {
return fmt.Errorf("a queue uses the tango analyzer, so tango.address is required")
}
if c.Tango.SignatureCacheBytes <= 0 {
return fmt.Errorf("a queue uses the tango analyzer, so tango.signatureCacheBytes must be positive")
}
return nil
}

// validateGitRepoPaths rejects two queues keeping their copies in one directory
Expand Down Expand Up @@ -614,6 +698,17 @@ func (a *analyzerConfig) normalizeAndValidate(where string) error {
return fmt.Errorf("%s: unknown pathoverlap granularity %q", where, a.By)
}
return nil
case analyzerTypeTango:
if a.Tango == nil || a.Tango.Remote == "" {
return fmt.Errorf("%s: tango analyzer needs a tango block with a remote", where)
}
if a.Tango.Strategy == "" {
a.Tango.Strategy = "unset"
}
if !tangoclient.ValidStrategy(a.Tango.Strategy) {
return fmt.Errorf("%s: unknown tango strategy %q", where, a.Tango.Strategy)
}
return nil
default:
return fmt.Errorf("%s: unknown analyzer type %q", where, a.Type)
}
Expand Down
Loading
Loading