Skip to content

Commit b54ba8b

Browse files
authored
Merge branch 'main' into fix/busy-partition-gc
2 parents d3b5968 + cfeea0d commit b54ba8b

29 files changed

Lines changed: 625 additions & 109 deletions

File tree

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ FOLDERS ?= 0
7878
FILES ?= 3
7979
CONCURRENCY ?= 5
8080
STACKED ?= false
81+
# BURST=true creates every change first, then enqueues them all at once instead
82+
# of as each is created. Independent changes only; a stack always lands as one.
83+
BURST ?= false
8184
SINCE ?= 1h
8285
LIMIT ?= 50
8386
LAND ?= true
@@ -240,7 +243,7 @@ clean-proto: ## Clean generated proto files
240243
@rm -f $(foreach p,$(PROTO_PACKAGES),$(p)/protopb/*.pb.go $(p)/protopb/*.pb.yarpc.go)
241244
@echo "Proto clean complete!"
242245

243-
demo-requests: ## Create N changes, enqueue each as it is created, and watch (PROVIDER=fake|git|github COUNT=3 FOLDERS=0 FILES=3 CONCURRENCY=5)
246+
demo-requests: ## Create N changes, enqueue each as it is created, and watch (PROVIDER=fake|git|github COUNT=3 FOLDERS=0 FILES=3 CONCURRENCY=5 BURST=false)
244247
@set -e; $(resolve_gateway_addr); $(resolve_provider); \
245248
$(BAZEL) run //service/submitqueue/demo/requests -- \
246249
-provider $$provider \
@@ -251,6 +254,7 @@ demo-requests: ## Create N changes, enqueue each as it is created, and watch (PR
251254
-files $(FILES) \
252255
-concurrency $(CONCURRENCY) \
253256
-stacked=$(STACKED) \
257+
-burst=$(BURST) \
254258
-addr $$addr \
255259
-queue $(QUEUE) \
256260
-strategy $(STRATEGY) \

platform/publish/publish.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ import (
4747
// which is what makes redelivery safe, while a new cause about the same entity
4848
// can never be swallowed by an older row.
4949
func Message(ctx context.Context, registry consumer.TopicRegistry, key consumer.TopicKey, msgID string, payload []byte, partitionKey string) error {
50+
return MessageWithMetadata(ctx, registry, key, msgID, payload, partitionKey, nil)
51+
}
52+
53+
// MessageWithMetadata is Message with side-band message metadata (headers/attributes)
54+
// attached to the delivery. Use it to carry diagnostic context that is not part of
55+
// the payload — the backend persists and redelivers metadata alongside the message.
56+
func MessageWithMetadata(ctx context.Context, registry consumer.TopicRegistry, key consumer.TopicKey, msgID string, payload []byte, partitionKey string, metadata map[string]string) error {
5057
q, ok := registry.Queue(key)
5158
if !ok {
5259
return fmt.Errorf("no queue registered for topic key %s", key)
@@ -56,7 +63,7 @@ func Message(ctx context.Context, registry consumer.TopicRegistry, key consumer.
5663
return fmt.Errorf("no topic name registered for topic key %s", key)
5764
}
5865

59-
msg := entityqueue.NewMessage(msgID, payload, partitionKey, nil)
66+
msg := entityqueue.NewMessage(msgID, payload, partitionKey, metadata)
6067
return q.Publisher().Publish(ctx, topicName, msg)
6168
}
6269

service/runway/server/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ go_library(
2828
"//platform/extension/consumergate/noop:go_default_library",
2929
"//platform/extension/messagequeue:go_default_library",
3030
"//platform/extension/messagequeue/mysql:go_default_library",
31+
"//platform/git/exec:go_default_library",
3132
"//runway/controller:go_default_library",
3233
"//runway/controller/dlq:go_default_library",
3334
"//runway/controller/merge:go_default_library",

service/runway/server/checkout.go

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"go.uber.org/zap"
2828

29+
gitexec "github.com/uber/submitqueue/platform/git/exec"
2930
gitmerger "github.com/uber/submitqueue/runway/extension/merger/git"
3031
)
3132

@@ -190,8 +191,9 @@ func setLocalConfig(checkoutPath, key, value string) error {
190191
}
191192

192193
// runGit invokes the pinned git in dir with an environment scrubbed of ambient
193-
// configuration but retaining what is needed to reach a remote — the same split
194-
// the merger draws, so provisioning and merging authenticate identically.
194+
// configuration but retaining what is needed to reach a remote. It composes that
195+
// environment through gitexec.Env, the same source the merger uses, so
196+
// provisioning and merging authenticate — and behave — identically.
195197
func runGit(ctx context.Context, runtime gitmerger.GitRuntime, dir string, args ...string) ([]byte, error) {
196198
full := append([]string{
197199
"--exec-path=" + runtime.ExecPath,
@@ -200,27 +202,17 @@ func runGit(ctx context.Context, runtime gitmerger.GitRuntime, dir string, args
200202

201203
cmd := exec.CommandContext(ctx, runtime.Executable, full...)
202204
cmd.Dir = dir
203-
cmd.Env = []string{
204-
"HOME=" + filepath.Join(dir, ".submitqueue-git-home"),
205-
"GIT_CONFIG_NOSYSTEM=1",
206-
"GIT_CONFIG_GLOBAL=" + os.DevNull,
207-
"GIT_TERMINAL_PROMPT=0",
208-
"GIT_EXEC_PATH=" + runtime.ExecPath,
209-
"GIT_TEMPLATE_DIR=" + runtime.TemplateDir,
210-
"LC_ALL=C",
211-
"LANG=C",
212-
}
213-
for _, name := range []string{
214-
"PATH", "SSH_AUTH_SOCK", "SSH_AGENT_PID",
215-
"GIT_SSH", "GIT_SSH_COMMAND", "GIT_SSH_VARIANT",
216-
"GIT_SSL_CAINFO", "GIT_SSL_CAPATH", "SSL_CERT_DIR", "SSL_CERT_FILE",
217-
"HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
218-
"http_proxy", "https_proxy", "no_proxy",
219-
} {
220-
if v, ok := os.LookupEnv(name); ok {
221-
cmd.Env = append(cmd.Env, name+"="+v)
222-
}
223-
}
205+
cmd.Env = gitexec.Env(gitexec.EnvOptions{
206+
Transport: true,
207+
Passthrough: runtime.PassthroughEnv,
208+
Literal: []string{
209+
"HOME=" + filepath.Join(dir, ".submitqueue-git-home"),
210+
"GIT_EXEC_PATH=" + runtime.ExecPath,
211+
"GIT_TEMPLATE_DIR=" + runtime.TemplateDir,
212+
"LC_ALL=C",
213+
"LANG=C",
214+
},
215+
})
224216

225217
var stdout, stderr bytes.Buffer
226218
cmd.Stdout = &stdout

service/stovepipe/server/main.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,15 @@ func run() error {
282282
// Each factory is constructed once and threaded through every consumer of
283283
// it, so a real (stateful) backend introduced later is shared rather than
284284
// silently duplicated across controllers.
285-
scf := fakeSourceControlFactory{}
285+
sourceControl := fakeSourceControlFactory{}
286286
brf := fakeBuildRunnerFactory{}
287287

288288
storageFty := storageFactory{backend: store}
289-
primaryCount, err := registerPrimaryControllers(primaryConsumer, logger.Sugar(), scope, storageFty, registry, scf, brf)
289+
primaryCount, err := registerPrimaryControllers(primaryConsumer, logger.Sugar(), scope, storageFty, registry, sourceControl, brf)
290290
if err != nil {
291291
return err
292292
}
293-
dlqCount, err := registerDLQControllers(dlqConsumer, logger.Sugar(), scope, storageFty, registry)
293+
dlqCount, err := registerDLQControllers(dlqConsumer, logger.Sugar(), scope, storageFty, registry, sourceControl)
294294
if err != nil {
295295
return err
296296
}
@@ -318,7 +318,7 @@ func run() error {
318318
logger.Sugar(),
319319
scope,
320320
newInMemoryCounterFactory(),
321-
scf,
321+
sourceControl,
322322
storageFty,
323323
registry,
324324
)
@@ -398,7 +398,7 @@ func registerPrimaryControllers(
398398
scope tally.Scope,
399399
store storage.Factory,
400400
registry consumer.TopicRegistry,
401-
scf sourcecontrol.Factory,
401+
sourceControl sourcecontrol.Factory,
402402
brf buildrunner.Factory,
403403
) (int, error) {
404404
var count int
@@ -408,7 +408,7 @@ func registerPrimaryControllers(
408408
scope,
409409
store,
410410
queueconfigdefault.NewStore(),
411-
scf,
411+
sourceControl,
412412
registry,
413413
stovepipemq.TopicKeyProcess,
414414
"stovepipe-process",
@@ -430,7 +430,7 @@ func registerPrimaryControllers(
430430
}
431431
count++
432432

433-
recordController := record.NewController(logger, scope, store, scf, stovepipemq.TopicKeyRecord, "stovepipe-record")
433+
recordController := record.NewController(logger, scope, store, sourceControl, stovepipemq.TopicKeyRecord, "stovepipe-record")
434434
if err := c.Register(recordController); err != nil {
435435
return count, fmt.Errorf("failed to register record controller: %w", err)
436436
}
@@ -447,21 +447,34 @@ func registerDLQControllers(
447447
scope tally.Scope,
448448
store storage.Factory,
449449
registry consumer.TopicRegistry,
450+
sourceControl sourcecontrol.Factory,
450451
) (int, error) {
451452
var count int
452453

453-
processDLQController := dlq.NewController(logger, scope, store, dlq.TopicKey(stovepipemq.TopicKeyProcess), "stovepipe-process-dlq")
454+
processDLQController := dlq.NewDLQRequestController(logger, scope, store, dlq.TopicKey(stovepipemq.TopicKeyProcess), "stovepipe-process-dlq")
454455
if err := c.Register(processDLQController); err != nil {
455456
return count, fmt.Errorf("failed to register process dlq controller: %w", err)
456457
}
457458
count++
458459

459-
buildSignalDLQController := dlq.NewBuildSignalController(logger, scope, store, dlq.TopicKey(stovepipemq.TopicKeyBuildSignal), "stovepipe-buildsignal-dlq")
460+
buildDLQController := dlq.NewDLQBuildController(logger, scope, store, dlq.TopicKey(stovepipemq.TopicKeyBuild), "stovepipe-build-dlq")
461+
if err := c.Register(buildDLQController); err != nil {
462+
return count, fmt.Errorf("failed to register build dlq controller: %w", err)
463+
}
464+
count++
465+
466+
buildSignalDLQController := dlq.NewDLQBuildSignalController(logger, scope, store, dlq.TopicKey(stovepipemq.TopicKeyBuildSignal), "stovepipe-buildsignal-dlq")
460467
if err := c.Register(buildSignalDLQController); err != nil {
461468
return count, fmt.Errorf("failed to register buildsignal dlq controller: %w", err)
462469
}
463470
count++
464471

472+
recordDLQController := record.NewController(logger, scope, store, sourceControl, dlq.TopicKey(stovepipemq.TopicKeyRecord), "stovepipe-record-dlq")
473+
if err := c.Register(recordDLQController); err != nil {
474+
return count, fmt.Errorf("failed to register record dlq controller: %w", err)
475+
}
476+
count++
477+
465478
return count, nil
466479
}
467480

@@ -511,12 +524,24 @@ func newTopicRegistry(q extqueue.Queue, subscriberName string) (consumer.TopicRe
511524
Queue: q,
512525
Subscription: extqueue.DLQSubscriptionConfig(subscriberName, "stovepipe-process-dlq"),
513526
},
527+
{
528+
Key: dlq.TopicKey(stovepipemq.TopicKeyBuild),
529+
Name: "build_dlq",
530+
Queue: q,
531+
Subscription: extqueue.DLQSubscriptionConfig(subscriberName, "stovepipe-build-dlq"),
532+
},
514533
{
515534
Key: dlq.TopicKey(stovepipemq.TopicKeyBuildSignal),
516535
Name: "buildsignal_dlq",
517536
Queue: q,
518537
Subscription: extqueue.DLQSubscriptionConfig(subscriberName, "stovepipe-buildsignal-dlq"),
519538
},
539+
{
540+
Key: dlq.TopicKey(stovepipemq.TopicKeyRecord),
541+
Name: "record_dlq",
542+
Queue: q,
543+
Subscription: extqueue.DLQSubscriptionConfig(subscriberName, "stovepipe-record-dlq"),
544+
},
520545
})
521546
}
522547

service/submitqueue/demo/requests/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ go_test(
4646
"SUBMITQUEUE_TEST_GIT": "$(location @git//:git)",
4747
},
4848
deps = [
49+
"//api/base/mergestrategy/protopb:go_default_library",
4950
"//platform/base/change/git:go_default_library",
5051
"//platform/fakemarker:go_default_library",
5152
"//platform/git/exec:go_default_library",
5253
"//platform/git/exectest:go_default_library",
54+
"//submitqueue/client:go_default_library",
5355
"@com_github_stretchr_testify//assert:go_default_library",
5456
"@com_github_stretchr_testify//require:go_default_library",
5557
],

service/submitqueue/demo/requests/main.go

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ type config struct {
9292
files int
9393
concurrency int
9494
stacked bool
95+
burst bool
9596
prefix string
9697
land bool
9798
watch bool
@@ -120,6 +121,8 @@ func parseFlags() config {
120121
flag.IntVar(&c.concurrency, "concurrency", 5,
121122
"how many changes to create at once; a stack ignores it, being sequential by nature, and -provider git serializes its git commands")
122123
flag.BoolVar(&c.stacked, "stacked", false, "chain the changes and enqueue them as one stack")
124+
flag.BoolVar(&c.burst, "burst", false,
125+
"create every change first, then enqueue them all at once instead of as each is created; independent changes only")
123126
flag.StringVar(&c.prefix, "prefix", "demo", "branch name prefix")
124127
flag.BoolVar(&c.land, "land", true, "enqueue each change as it is created")
125128
flag.BoolVar(&c.watch, "watch", true, "watch the requests until they all settle")
@@ -242,6 +245,9 @@ func shape(cfg config) string {
242245
if cfg.stacked {
243246
return "stacked, enqueued as one request once the chain exists"
244247
}
248+
if cfg.burst && cfg.land {
249+
return fmt.Sprintf("independent, created %d at a time, then all enqueued at once", cfg.concurrency)
250+
}
245251
if cfg.concurrency > 1 {
246252
return fmt.Sprintf("independent, %d at a time, each enqueued as soon as it is created", cfg.concurrency)
247253
}
@@ -432,6 +438,12 @@ func changeFileCount(tag string, change, min int) int {
432438
return min + int(sum[0]%4)
433439
}
434440

441+
// lander enqueues a request. *client.Client is the real one; a test supplies
442+
// its own to observe when each change is enqueued relative to when it is created.
443+
type lander interface {
444+
Land(ctx context.Context, queue string, uris []string, strategy mergestrategypb.Strategy) (string, error)
445+
}
446+
435447
// createAndEnqueue creates the changes and puts them on the queue, filling
436448
// in the tracker's rows as it goes and reporting each step beneath the table.
437449
//
@@ -451,7 +463,7 @@ func changeFileCount(tag string, change, min int) int {
451463
func createAndEnqueue(
452464
ctx context.Context,
453465
src changeSource,
454-
sq *client.Client,
466+
sq lander,
455467
cfg config,
456468
strategy mergestrategypb.Strategy,
457469
tag, baseSHA string,
@@ -480,12 +492,16 @@ func createAndEnqueue(
480492
func createIndependent(
481493
ctx context.Context,
482494
src changeSource,
483-
sq *client.Client,
495+
sq lander,
484496
cfg config,
485497
strategy mergestrategypb.Strategy,
486498
tag, baseSHA string,
487499
t *client.Tracker,
488500
) ([]change, error) {
501+
if cfg.burst && cfg.land {
502+
return createBurst(ctx, src, sq, cfg, strategy, tag, baseSHA, t)
503+
}
504+
489505
rows := t.Rows()
490506
// Indexed rather than appended: the workers finish in whatever order the
491507
// provider answers them, and the caller still wants the run's own order.
@@ -521,6 +537,62 @@ func createIndependent(
521537
return created, nil
522538
}
523539

540+
// createBurst creates every change first and only then enqueues them, firing
541+
// all the Land calls together so the requests arrive at the queue in one burst.
542+
//
543+
// The default path lands each change the moment it exists, so the queue starts
544+
// working while later changes are still being created. Burst trades that early
545+
// overlap for a simultaneous arrival: useful for watching the queue admit a
546+
// large batch at once. It does not make creation faster — with -provider git the
547+
// creation phase is still serialized on a single work tree — it only separates
548+
// creation from enqueuing so the enqueues are not spread across it.
549+
func createBurst(
550+
ctx context.Context,
551+
src changeSource,
552+
sq lander,
553+
cfg config,
554+
strategy mergestrategypb.Strategy,
555+
tag, baseSHA string,
556+
t *client.Tracker,
557+
) ([]change, error) {
558+
rows := t.Rows()
559+
created := make([]change, cfg.count)
560+
561+
create, createCtx := errgroup.WithContext(ctx)
562+
create.SetLimit(cfg.concurrency)
563+
for i := 1; i <= cfg.count; i++ {
564+
create.Go(func() error {
565+
c, err := createOne(createCtx, src, cfg, tag, baseSHA, cfg.base, i, t, rows[i-1])
566+
if err != nil {
567+
return err
568+
}
569+
created[i-1] = c
570+
return nil
571+
})
572+
}
573+
if err := create.Wait(); err != nil {
574+
return nil, err
575+
}
576+
577+
t.Note("enqueuing %d changes at once", cfg.count)
578+
land, landCtx := errgroup.WithContext(ctx)
579+
land.SetLimit(cfg.concurrency)
580+
for i := 1; i <= cfg.count; i++ {
581+
land.Go(func() error {
582+
sqid, err := sq.Land(landCtx, cfg.queue, urisOf([]change{created[i-1]}), strategy)
583+
if err != nil {
584+
return err
585+
}
586+
t.Update(func() { rows[i-1].SQID, rows[i-1].Submitted = sqid, time.Now() })
587+
return nil
588+
})
589+
}
590+
if err := land.Wait(); err != nil {
591+
return nil, err
592+
}
593+
return created, nil
594+
}
595+
524596
// createStack creates the changes one after another, each based on the one
525597
// before it, and submits the whole chain as a single request.
526598
//
@@ -530,7 +602,7 @@ func createIndependent(
530602
func createStack(
531603
ctx context.Context,
532604
src changeSource,
533-
sq *client.Client,
605+
sq lander,
534606
cfg config,
535607
strategy mergestrategypb.Strategy,
536608
tag, baseSHA string,

0 commit comments

Comments
 (0)