119 lines
5.2 KiB
Go
119 lines
5.2 KiB
Go
package broker
|
|
|
|
import "strings"
|
|
|
|
// The pull requests' checks have work queues of their own (novox/hq ADR 0406 §3, issue 974).
|
|
//
|
|
// A delivery build goes on the build seat's own queue, `mesh.seat.node-build-agent.accept.build`. A check of a pull
|
|
// request marked ready goes on a second work-queue stream and every other check on a third. A builder takes from the
|
|
// first, then the second, then the third; the others are taken in the order asked. **Each check stream has one subject
|
|
// per pull request and keeps only the newest request** (a limit of one message a subject, the older discarded), so a
|
|
// newer request replaces one still waiting and a pull request has at most one waiting check: the stream's rule, not a
|
|
// pass of the controller that moves asks about. Nothing pauses the worker or deletes and republishes a request.
|
|
|
|
const (
|
|
// ChecksReadyStream holds the checks of pull requests marked ready; ChecksOtherStream every other check.
|
|
ChecksReadyStream = "CHECKS_READY"
|
|
ChecksOtherStream = "CHECKS_OTHER"
|
|
// ChecksReadyWorker and ChecksOtherWorker are the durable consumers the build seat's holders share on them, as
|
|
// they share the seat's worker on its own queue (novox/hq ADR 0190).
|
|
ChecksReadyWorker = "CHECKS_READY_worker"
|
|
ChecksOtherWorker = "CHECKS_OTHER_worker"
|
|
// TheBuildSeat is the role whose checks these streams queue. The retired build role's checks stay on its own queue.
|
|
TheBuildSeat = "node-build-agent"
|
|
)
|
|
|
|
// PullsBucket is mesh-delivery's key per pull request: its head, the trunk commit its current check is asked against and
|
|
// its verdict (ADR 0406 §4). A running check watches its key.
|
|
var PullsBucket = BucketName("mesh-delivery", "pulls")
|
|
|
|
// CheckSubjectPrefix is where a seat's checks of one kind are asked: `mesh.seat.<seat>.check.ready.` or `.other.`. The
|
|
// `check` token keeps the subjects out of the seat's own queue, which takes `accept.>`.
|
|
func CheckSubjectPrefix(seat string, ready bool) string {
|
|
kind := "other"
|
|
if ready {
|
|
kind = "ready"
|
|
}
|
|
return "mesh.seat." + seat + ".check." + kind + "."
|
|
}
|
|
|
|
// CheckStreams are the two check queues, in the order a builder takes from them.
|
|
func CheckStreams() []Stream {
|
|
return []Stream{
|
|
{
|
|
Name: ChecksReadyStream,
|
|
Subjects: []string{CheckSubjectPrefix(TheBuildSeat, true) + ">"},
|
|
Retention: RetentionWorkQueue,
|
|
MaxAge: 7 * 24 * 60 * 60,
|
|
MaxMsgsPerSubject: 1,
|
|
Why: "the checks of pull requests marked ready, taken after a delivery build and before any other check " +
|
|
"(novox/hq ADR 0406 §3); one subject a pull request, the newest request kept, so a newer one replaces a " +
|
|
"request still waiting",
|
|
},
|
|
{
|
|
Name: ChecksOtherStream,
|
|
Subjects: []string{CheckSubjectPrefix(TheBuildSeat, false) + ">"},
|
|
Retention: RetentionWorkQueue,
|
|
MaxAge: 7 * 24 * 60 * 60,
|
|
MaxMsgsPerSubject: 1,
|
|
Why: "every other check, taken last and in the order asked (novox/hq ADR 0406 §3); one subject a pull " +
|
|
"request, the newest request kept; one left behind for a head already judged is dropped when taken",
|
|
},
|
|
}
|
|
}
|
|
|
|
// CheckWorkers are the consumers the build seat's holders pull the check queues through: as the seat's own worker is,
|
|
// one for every holder, one ask at a time, acknowledged when the check is done.
|
|
func CheckWorkers() []Consumer {
|
|
worker := func(name, stream string, ready bool) Consumer {
|
|
return Consumer{
|
|
Name: name,
|
|
Stream: stream,
|
|
Filters: []string{CheckSubjectPrefix(TheBuildSeat, ready) + ">"},
|
|
AckWaitSeconds: 60,
|
|
MaxDeliver: 5,
|
|
Why: "every holder of " + TheBuildSeat + " pulls one check at a time from this worker and acknowledges after " +
|
|
"the check is done, so a crash mid-check redelivers rather than loses (novox/hq ADR 0406 §3)",
|
|
}
|
|
}
|
|
return []Consumer{
|
|
worker(ChecksReadyWorker, ChecksReadyStream, true),
|
|
worker(ChecksOtherWorker, ChecksOtherStream, false),
|
|
}
|
|
}
|
|
|
|
// holderCheckGrants are what a holder of the build seat is granted to take checks: ask about the two workers, pull from
|
|
// them and acknowledge, and nothing of the subjects themselves.
|
|
func holderCheckGrants() []string {
|
|
var pub []string
|
|
for _, w := range CheckWorkers() {
|
|
pub = append(pub,
|
|
"$JS.API.CONSUMER.INFO."+w.Stream+"."+w.Name,
|
|
"$JS.API.CONSUMER.MSG.NEXT."+w.Stream+"."+w.Name,
|
|
"$JS.ACK."+w.Stream+"."+w.Name+".>")
|
|
}
|
|
// And the bucket of the checks they are running: each writes the key of its own check, reads another's, and the bucket
|
|
// is a controller's own bucket the build seat's holders write by design (ADR 0406 §3).
|
|
pub = append(pub,
|
|
"$KV."+CheckRunningBucket+".>",
|
|
"$JS.API.STREAM.INFO.KV_"+CheckRunningBucket,
|
|
"$JS.API.DIRECT.GET.KV_"+CheckRunningBucket+".>")
|
|
return pub
|
|
}
|
|
|
|
// IsCheckStream says a stream is one of the check queues.
|
|
func IsCheckStream(stream string) bool {
|
|
return stream == ChecksReadyStream || stream == ChecksOtherStream
|
|
}
|
|
|
|
// isCheckAsk says a message of a check queue is an ask the controller itself makes.
|
|
func isCheckAsk(stream, subject string) bool {
|
|
switch stream {
|
|
case ChecksReadyStream:
|
|
return strings.HasPrefix(subject, CheckSubjectPrefix(TheBuildSeat, true))
|
|
case ChecksOtherStream:
|
|
return strings.HasPrefix(subject, CheckSubjectPrefix(TheBuildSeat, false))
|
|
}
|
|
return false
|
|
}
|