carlos / platform Public

provision: prefetch record reads a chunk at a time

Astra review of 20c5ffd5 (accept with fixes):

- P2: prefetching every body before judging held the whole record set
  in memory. The judging loops now prefetch and judge 64 keys at a time,
  in the same order as before.
- P2: ListAllRecords read every record before reporting the first one
  that failed. It now stops within a chunk of it. A cancelled context
  reads nothing, and each key reports ctx.Err() as its read failure.
- P3: the tests now bound concurrency at eight, count one read per
  record, mint a fresh record in a pass where another record's read
  fails, require the error to name the failing record, and cover the
  chunk bound and cancellation.

The loop bodies are re-indented by the added chunk loop; their
statements are unchanged apart from reading the body from the chunk.
Agent notes
Mutants checked: one unbounded chunk, no cancellation check, 64 concurrent readers. Each fails the test written for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev c54e77041abdfecef536bd0923eb52079477d265 parent f41b124
3 files changed, +301 −202
  • internal/provision/parallelreads_test.go +78 −5
  • internal/provision/reconcile.go +136 −109
  • internal/provision/sidecar_reconcile.go +87 −88

CI passed — run details

diff --git a/internal/provision/parallelreads_test.go b/internal/provision/parallelreads_test.go
index 07b98022f..199ef3793 100644
--- a/internal/provision/parallelreads_test.go
+++ b/internal/provision/parallelreads_test.go
@@ -3,6 +3,7 @@ package provision
import (
"context"
"errors"
+ "fmt"
"strings"
"sync"
"testing"
@@ -18,6 +19,7 @@ type overlapStore struct {
mu sync.Mutex
inFlight int
maxFlight int
+ reads int
failKey string
}
@@ -25,6 +27,7 @@ func (s *overlapStore) Get(ctx context.Context, key string) ([]byte, string, err
if strings.Contains(key, "/instances/") {
s.mu.Lock()
s.inFlight++
+ s.reads++
if s.inFlight > s.maxFlight {
s.maxFlight = s.inFlight
}
@@ -71,8 +74,11 @@ func TestRecordReadersReadRecordsConcurrently(t *testing.T) {
if err := c.run(s); err != nil {
t.Fatalf("%s: %v", c.name, err)
}
- if s.maxFlight < 2 {
- t.Fatalf("%s read at most %d record at a time, want several", c.name, s.maxFlight)
+ if s.maxFlight < 2 || s.maxFlight > 8 {
+ t.Fatalf("%s read up to %d records at a time, want several and never more than 8", c.name, s.maxFlight)
+ }
+ if s.reads != 12 {
+ t.Fatalf("%s made %d record reads for 12 records, want one each", c.name, s.reads)
}
}
}
@@ -90,14 +96,81 @@ func TestReconcileStillJudgesAFailedReadPerRecord(t *testing.T) {
if err := Reconcile(ctx, s, reg, cfg); err != nil {
t.Fatal(err)
}
- for _, h := range []string{"ra.go.tito.io", "rb.go.tito.io", "rc.go.tito.io", "rd.go.tito.io"} {
+ // A record created since the first pass must still be minted in a pass
+ // where another record's read fails: the failure is rb's alone.
+ fresh := acmeRecord(sqid)
+ fresh.Host, fresh.Slug = "re.go.tito.io", "re"
+ if err := CreateRecord(ctx, inner, fresh); err != nil {
+ t.Fatal(err)
+ }
+ if err := Reconcile(ctx, s, reg, cfg); err != nil {
+ t.Fatal(err)
+ }
+ for _, h := range []string{"ra.go.tito.io", "rb.go.tito.io", "rc.go.tito.io", "rd.go.tito.io", "re.go.tito.io"} {
if rt, err := reg.Get(h); err != nil || rt == nil {
- t.Fatalf("%s gone after a pass where only rb's read failed: %+v, %v", h, rt, err)
+ t.Fatalf("%s missing after a pass where only rb's read failed: %+v, %v", h, rt, err)
}
}
+ _, err := ListAllRecords(ctx, s)
+ if err == nil || !strings.Contains(err.Error(), "rb.go.tito.io") {
+ t.Fatalf("ListAllRecords with rb unreadable = %v, want an error naming rb", err)
+ }
+}
+
+// A loop that stops at its first bad read has read at most one chunk ahead,
+// and a large record set is never prefetched whole.
+func TestListAllRecordsStopsWithinAChunkOfABadRead(t *testing.T) {
+ inner, sqid := setup(t)
+ const total = 200 // more than one chunk, fixed so the test does not move with readChunk
+ for i := 0; i < total; i++ {
+ rec := acmeRecord(sqid)
+ rec.Host, rec.Slug = fmt.Sprintf("r%04d.go.tito.io", i), fmt.Sprintf("r%04d", i)
+ if err := CreateRecord(ctx, inner, rec); err != nil {
+ t.Fatal(err)
+ }
+ }
+ s := &countingGets{Store: inner, failKey: RecordKey(sqid, "tito", "r0000.go.tito.io")}
if _, err := ListAllRecords(ctx, s); err == nil {
- t.Fatal("ListAllRecords returned a set with one record unread")
+ t.Fatal("ListAllRecords read past an unreadable first record without an error")
+ }
+ if s.n > readChunk || s.n >= total {
+ t.Fatalf("read %d of %d records before stopping at the first, want at most one chunk (%d)", s.n, total, readChunk)
+ }
+}
+
+// A cancelled context reads nothing, and every key reports why.
+func TestGetEachOnACancelledContextReadsNothing(t *testing.T) {
+ inner, sqid := setup(t)
+ s := &countingGets{Store: inner}
+ cctx, cancel := context.WithCancel(ctx)
+ cancel()
+ key := RecordKey(sqid, "tito", "acme.go.tito.io")
+ got := getEach(cctx, s, []string{key})
+ if s.n != 0 {
+ t.Fatalf("made %d reads on a cancelled context", s.n)
}
+ if !errors.Is(got[key].err, context.Canceled) {
+ t.Fatalf("key reported %v, want context.Canceled", got[key].err)
+ }
+}
+
+type countingGets struct {
+ release.Store
+ mu sync.Mutex
+ n int
+ failKey string
+}
+
+func (s *countingGets) Get(ctx context.Context, key string) ([]byte, string, error) {
+ if strings.Contains(key, "/instances/") {
+ s.mu.Lock()
+ s.n++
+ s.mu.Unlock()
+ if key == s.failKey {
+ return nil, "", errors.New("read refused")
+ }
+ }
+ return s.Store.Get(ctx, key)
}
// appOverlapStore holds each app-level read (an app's instances manifest and
diff --git a/internal/provision/reconcile.go b/internal/provision/reconcile.go
index c54ceca19..0de893f51 100644
--- a/internal/provision/reconcile.go
+++ b/internal/provision/reconcile.go
@@ -742,96 +742,95 @@ func Reconcile(ctx context.Context, s release.Store, reg *registry.Registry, cfg
recordKeys = append(recordKeys, key)
}
}
- bodies := getEach(ctx, s, recordKeys)
- for _, key := range keys {
- acct, app, host, ok := ParseRecordKey(key, known)
- if !ok {
- continue
- }
- raw, err := bodies[key].body, bodies[key].err
- if err != nil {
- log.Printf("provision: skipping record %s: read failed: %v", key, err)
- // The record's absence from recs below must not read as "the
- // bucket retired it" — a transient Get failure disables removal
- // for the whole pass, same as an unreadable account manifest.
- readFailed = true
- continue
- }
- rec, err := DecodeRecord(raw)
- if err != nil {
- log.Printf("provision: skipping record %s: malformed: %v", key, err)
- protect[host] = true
- continue
- }
- if rec.Host != host || rec.Account != acct || rec.App != app {
- log.Printf("provision: skipping record %s: disagrees with its own path (host=%q account=%q app=%q)",
- key, rec.Host, rec.Account, rec.App)
- protect[host] = true
- continue
- }
- // The record IS the authorization, and this pass turns its host into
- // a socket path and a database path (DefaultSocket/DefaultDBPath)
- // and a registry primary key. The console's create door enforces
- // this same shape — but a door is not a guarantee about what is
- // already in the bucket: anything with bucket write access, and any
- // record written by a different (older, or future) writer, arrives
- // here unexamined. Both doors hold the invariant, neither trusts
- // the other. Protected like a corrupted body, and for the same
- // reason: a record that cannot be used is not a record that was
- // retired.
- if !ValidHostShape(rec.Host) {
- log.Printf("provision: skipping record %s: host %q is not a hostname", key, rec.Host)
- protect[host] = true
- continue
- }
- // ValidateShape is the console create door's policy check — but a
- // door is not a guarantee about what is already in the bucket (the
- // same reasoning as the host-shape check just above). This pass
- // materializes whatever it finds, so a record whose Kind/Addr/Unit/
- // Environment combination is malformed must be caught here too, or
- // a bad value becomes a live route. Protected like a corrupted
- // body: the record is there, it just cannot be used.
- if err := ValidateShape(rec); err != nil {
- log.Printf("provision: skipping record %s: invalid shape: %v", key, err)
- protect[host] = true
- continue
- }
- // ValidateShape deliberately does not inspect Channel (it validates
- // shape fields, and Channel is not one), but a channel-less record
- // must not become — or converge — a route pinned to no channel:
- // that is issue #130's failure mode arriving through a different
- // door than the one Task 3 closed. Protected like a corrupted body:
- // the record is there, it just cannot be used.
- if rec.Channel == "" {
- log.Printf("provision: skipping record %s: empty channel", key)
- protect[host] = true
- continue
- }
- // Scope. For a home-boxed record this is the region/pool test it
- // always was; for a static or public-addr one the region half widens
- // to every platform box in the deployment (everywhereClass, above).
- // A replica record narrows it: it belongs ONLY to the box whose
- // placement is the record's own Region — the placement the create
- // door validated as armed — so a replica record minted on the wrong
- // region would mint a route with no mirror behind it and no lease
- // speaking to it, a process holding live order data nobody
- // authorized into that region (the residence rule, #162). Every
- // other box skips it exactly like any other out-of-scope record;
- // the replica's own box sees Region == cfg.Region through the
- // ordinary test above.
- if !cfg.inScope(rec.Region, rec.Pool, everywhereClass(rec)) {
- continue // another agent's jurisdiction, not this pass's business
- }
- // Two accounts writing the same host is a collision this pass
- // cannot arbitrate silently: whichever record this listing reaches
- // FIRST wins the host, and the loser is skipped loudly rather than
- // overwriting the winner in the map with no trace of the conflict.
- if prev, dup := recs[rec.Host]; dup {
- log.Printf("provision: duplicate host %s: keeping %s, skipping %s", rec.Host,
- RecordKey(prev.Account, prev.App, prev.Host), RecordKey(rec.Account, rec.App, rec.Host))
- continue
+ for _, part := range chunks(recordKeys) {
+ bodies := getEach(ctx, s, part)
+ for _, key := range part {
+ acct, app, host, _ := ParseRecordKey(key, known)
+ raw, err := bodies[key].body, bodies[key].err
+ if err != nil {
+ log.Printf("provision: skipping record %s: read failed: %v", key, err)
+ // The record's absence from recs below must not read as "the
+ // bucket retired it" — a transient Get failure disables removal
+ // for the whole pass, same as an unreadable account manifest.
+ readFailed = true
+ continue
+ }
+ rec, err := DecodeRecord(raw)
+ if err != nil {
+ log.Printf("provision: skipping record %s: malformed: %v", key, err)
+ protect[host] = true
+ continue
+ }
+ if rec.Host != host || rec.Account != acct || rec.App != app {
+ log.Printf("provision: skipping record %s: disagrees with its own path (host=%q account=%q app=%q)",
+ key, rec.Host, rec.Account, rec.App)
+ protect[host] = true
+ continue
+ }
+ // The record IS the authorization, and this pass turns its host into
+ // a socket path and a database path (DefaultSocket/DefaultDBPath)
+ // and a registry primary key. The console's create door enforces
+ // this same shape — but a door is not a guarantee about what is
+ // already in the bucket: anything with bucket write access, and any
+ // record written by a different (older, or future) writer, arrives
+ // here unexamined. Both doors hold the invariant, neither trusts
+ // the other. Protected like a corrupted body, and for the same
+ // reason: a record that cannot be used is not a record that was
+ // retired.
+ if !ValidHostShape(rec.Host) {
+ log.Printf("provision: skipping record %s: host %q is not a hostname", key, rec.Host)
+ protect[host] = true
+ continue
+ }
+ // ValidateShape is the console create door's policy check — but a
+ // door is not a guarantee about what is already in the bucket (the
+ // same reasoning as the host-shape check just above). This pass
+ // materializes whatever it finds, so a record whose Kind/Addr/Unit/
+ // Environment combination is malformed must be caught here too, or
+ // a bad value becomes a live route. Protected like a corrupted
+ // body: the record is there, it just cannot be used.
+ if err := ValidateShape(rec); err != nil {
+ log.Printf("provision: skipping record %s: invalid shape: %v", key, err)
+ protect[host] = true
+ continue
+ }
+ // ValidateShape deliberately does not inspect Channel (it validates
+ // shape fields, and Channel is not one), but a channel-less record
+ // must not become — or converge — a route pinned to no channel:
+ // that is issue #130's failure mode arriving through a different
+ // door than the one Task 3 closed. Protected like a corrupted body:
+ // the record is there, it just cannot be used.
+ if rec.Channel == "" {
+ log.Printf("provision: skipping record %s: empty channel", key)
+ protect[host] = true
+ continue
+ }
+ // Scope. For a home-boxed record this is the region/pool test it
+ // always was; for a static or public-addr one the region half widens
+ // to every platform box in the deployment (everywhereClass, above).
+ // A replica record narrows it: it belongs ONLY to the box whose
+ // placement is the record's own Region — the placement the create
+ // door validated as armed — so a replica record minted on the wrong
+ // region would mint a route with no mirror behind it and no lease
+ // speaking to it, a process holding live order data nobody
+ // authorized into that region (the residence rule, #162). Every
+ // other box skips it exactly like any other out-of-scope record;
+ // the replica's own box sees Region == cfg.Region through the
+ // ordinary test above.
+ if !cfg.inScope(rec.Region, rec.Pool, everywhereClass(rec)) {
+ continue // another agent's jurisdiction, not this pass's business
+ }
+ // Two accounts writing the same host is a collision this pass
+ // cannot arbitrate silently: whichever record this listing reaches
+ // FIRST wins the host, and the loser is skipped loudly rather than
+ // overwriting the winner in the map with no trace of the conflict.
+ if prev, dup := recs[rec.Host]; dup {
+ log.Printf("provision: duplicate host %s: keeping %s, skipping %s", rec.Host,
+ RecordKey(prev.Account, prev.App, prev.Host), RecordKey(rec.Account, rec.App, rec.Host))
+ continue
+ }
+ recs[rec.Host] = rec
}
- recs[rec.Host] = rec
}
// --- manifest seed cache, per (acct, app), lazily -------------------------
@@ -1406,28 +1405,27 @@ func ListAllRecords(ctx context.Context, s release.Store) ([]Record, error) {
recordKeys = append(recordKeys, key)
}
}
- bodies := getEach(ctx, s, recordKeys)
var recs []Record
- for _, key := range keys {
- acct, app, host, ok := ParseRecordKey(key, known)
- if !ok {
- continue
- }
- raw, err := bodies[key].body, bodies[key].err
- if err != nil {
- return nil, fmt.Errorf("provision: read record %s: %w", key, err)
- }
- rec, err := DecodeRecord(raw)
- if err != nil {
- log.Printf("provision: skipping record %s: malformed: %v", key, err)
- continue
- }
- if rec.Host != host || rec.Account != acct || rec.App != app {
- log.Printf("provision: skipping record %s: disagrees with its own path (host=%q account=%q app=%q)",
- key, rec.Host, rec.Account, rec.App)
- continue
+ for _, part := range chunks(recordKeys) {
+ bodies := getEach(ctx, s, part)
+ for _, key := range part {
+ acct, app, host, _ := ParseRecordKey(key, known)
+ raw, err := bodies[key].body, bodies[key].err
+ if err != nil {
+ return nil, fmt.Errorf("provision: read record %s: %w", key, err)
+ }
+ rec, err := DecodeRecord(raw)
+ if err != nil {
+ log.Printf("provision: skipping record %s: malformed: %v", key, err)
+ continue
+ }
+ if rec.Host != host || rec.Account != acct || rec.App != app {
+ log.Printf("provision: skipping record %s: disagrees with its own path (host=%q account=%q app=%q)",
+ key, rec.Host, rec.Account, rec.App)
+ continue
+ }
+ recs = append(recs, rec)
}
- recs = append(recs, rec)
}
return recs, nil
}
@@ -1520,6 +1518,25 @@ func listAppSubtree(ctx context.Context, s release.Store, known map[string]bool,
return out, partial, nil
}
+// readChunk is how many keys a judging loop prefetches at once: enough to keep
+// listWalkers busy, few enough that a large record set is never held in
+// memory whole, and that a loop stopping at its first bad read has read only
+// a chunk ahead of it.
+const readChunk = 64
+
+// chunks splits keys into runs of at most readChunk, in order.
+func chunks(keys []string) [][]string {
+ var out [][]string
+ for len(keys) > readChunk {
+ out = append(out, keys[:readChunk])
+ keys = keys[readChunk:]
+ }
+ if len(keys) > 0 {
+ out = append(out, keys)
+ }
+ return out
+}
+
// fetched is one prefetched object: its body, or why it could not be read.
type fetched struct {
body []byte
@@ -1536,8 +1553,18 @@ type fetched struct {
// visit keys in the same order and judge each one exactly as before, only
// reading the body from here rather than from the store. A key's read error
// is that key's, as it was.
+//
+// Callers prefetch one chunk (chunks) at a time and judge it before the next.
+// A context already cancelled reads nothing: every key reports ctx.Err(), so
+// a judging loop sees a read failure for it, never a missing body.
func getEach(ctx context.Context, s release.Store, keys []string) map[string]fetched {
out := make(map[string]fetched, len(keys))
+ if err := ctx.Err(); err != nil {
+ for _, k := range keys {
+ out[k] = fetched{err: err}
+ }
+ return out
+ }
var mu sync.Mutex
forEach(keys, func(k string) {
body, _, err := s.Get(ctx, k)
diff --git a/internal/provision/sidecar_reconcile.go b/internal/provision/sidecar_reconcile.go
index a861f7cfe..ca021d821 100644
--- a/internal/provision/sidecar_reconcile.go
+++ b/internal/provision/sidecar_reconcile.go
@@ -261,95 +261,94 @@ func ReconcileSidecars(ctx context.Context, s release.Store, reg *registry.Regis
recordKeys = append(recordKeys, key)
}
}
- bodies := getEach(ctx, s, recordKeys)
- for _, key := range keys {
- acct, app, name, ok := ParseSidecarKey(key, known)
- if !ok {
- continue
- }
- // Derived from the KEY, never from the body: the body is what is
- // under suspicion in every branch below.
- host := SidecarHost(name, app, acct)
- raw, err := bodies[key].body, bodies[key].err
- if err != nil {
- log.Printf("provision: skipping sidecar record %s: read failed: %v", key, err)
- // Its absence from recs below must not read as "the bucket retired
- // it" — a transient Get failure disables removal for the whole
- // pass, same as an unreadable account manifest.
- readFailed = true
- continue
- }
- var rec SidecarRecord
- if err := json.Unmarshal(raw, &rec); err != nil {
- log.Printf("provision: skipping sidecar record %s: malformed: %v", key, err)
- protect[host] = true
- continue
- }
- if rec.Name != name || rec.Account != acct || rec.App != app {
- log.Printf("provision: skipping sidecar record %s: disagrees with its own path (name=%q account=%q app=%q)",
- key, rec.Name, rec.Account, rec.App)
- protect[host] = true
- continue
- }
- // ValidateSidecarShape is the console create door's policy check — but
- // a door is not a guarantee about what is already in the bucket:
- // anything with bucket write access, and any record written by a
- // different (older, or future) writer, arrives here unexamined. Both
- // doors hold the invariant, neither trusts the other. Protected like a
- // corrupted body, and for the same reason: a record that cannot be
- // used is not a record that was retired.
- if err := ValidateSidecarShape(rec); err != nil {
- log.Printf("provision: skipping sidecar record %s: invalid shape: %v", key, err)
- protect[host] = true
- continue
- }
- // The host is DERIVED from the identity, never taken on trust: it
- // becomes a database path and a registry primary key, and a record
- // free to state its own host could name any row on the box.
- if rec.Host != host {
- log.Printf("provision: skipping sidecar record %s: host %q is not %q", key, rec.Host, host)
- protect[host] = true
- continue
- }
- // ValidateSidecarShape deliberately does not inspect Channel (it
- // validates shape fields, and Channel is not one), but a channel-less
- // record must not become — or converge — a route pinned to no channel:
- // such a row resolves no release, so it has no build to exec, and the
- // worker boot pass would try to run it against a symlink that does not
- // exist. The console stamps a channel at create, so a record without
- // one is malformed. Protected like a corrupted body: the record is
- // there, it just cannot be used.
- if rec.Channel == "" {
- log.Printf("provision: skipping sidecar record %s: empty channel", key)
- protect[host] = true
- continue
- }
- if rec.Region != cfg.Region || !poolServed(cfg.Pools, rec.Pool) {
- continue // another agent's jurisdiction, not this pass's business
- }
- // Placement is the third scope axis (design §8), and it is applied
- // HERE, beside region and pool, precisely so it behaves like them: a
- // record this box may not run never enters recs, and the removal phase
- // below therefore evacuates any row it left behind. "none" is a
- // property of the BOX's network; "no-inbound" is a requirement of the
- // WORKLOAD — the point of the two halves being independent is that a
- // member gets a mechanical guarantee rather than an operator's promise,
- // which a row left running on an inbound box would not be.
- if m, _ := manifestFor(acct, app); !placeable(rec, m, cfg) {
- log.Printf("provision: sidecar %s is not placeable here: it asks for %q placement and this box accepts inbound work (CARLOS_INBOUND=%q)",
- rec.Host, sidecarPlacement(rec, m), cfg.Inbound)
- continue
- }
- // Two records claiming one host is a collision this pass cannot
- // arbitrate silently: whichever this listing reaches FIRST wins, and
- // the loser is skipped loudly rather than overwriting the winner in
- // the map with no trace of the conflict.
- if prev, dup := recs[rec.Host]; dup {
- log.Printf("provision: duplicate sidecar host %s: keeping %s, skipping %s", rec.Host,
- SidecarRecordKey(prev.Account, prev.App, prev.Name), key)
- continue
+ for _, part := range chunks(recordKeys) {
+ bodies := getEach(ctx, s, part)
+ for _, key := range part {
+ acct, app, name, _ := ParseSidecarKey(key, known)
+ // Derived from the KEY, never from the body: the body is what is
+ // under suspicion in every branch below.
+ host := SidecarHost(name, app, acct)
+ raw, err := bodies[key].body, bodies[key].err
+ if err != nil {
+ log.Printf("provision: skipping sidecar record %s: read failed: %v", key, err)
+ // Its absence from recs below must not read as "the bucket retired
+ // it" — a transient Get failure disables removal for the whole
+ // pass, same as an unreadable account manifest.
+ readFailed = true
+ continue
+ }
+ var rec SidecarRecord
+ if err := json.Unmarshal(raw, &rec); err != nil {
+ log.Printf("provision: skipping sidecar record %s: malformed: %v", key, err)
+ protect[host] = true
+ continue
+ }
+ if rec.Name != name || rec.Account != acct || rec.App != app {
+ log.Printf("provision: skipping sidecar record %s: disagrees with its own path (name=%q account=%q app=%q)",
+ key, rec.Name, rec.Account, rec.App)
+ protect[host] = true
+ continue
+ }
+ // ValidateSidecarShape is the console create door's policy check — but
+ // a door is not a guarantee about what is already in the bucket:
+ // anything with bucket write access, and any record written by a
+ // different (older, or future) writer, arrives here unexamined. Both
+ // doors hold the invariant, neither trusts the other. Protected like a
+ // corrupted body, and for the same reason: a record that cannot be
+ // used is not a record that was retired.
+ if err := ValidateSidecarShape(rec); err != nil {
+ log.Printf("provision: skipping sidecar record %s: invalid shape: %v", key, err)
+ protect[host] = true
+ continue
+ }
+ // The host is DERIVED from the identity, never taken on trust: it
+ // becomes a database path and a registry primary key, and a record
+ // free to state its own host could name any row on the box.
+ if rec.Host != host {
+ log.Printf("provision: skipping sidecar record %s: host %q is not %q", key, rec.Host, host)
+ protect[host] = true
+ continue
+ }
+ // ValidateSidecarShape deliberately does not inspect Channel (it
+ // validates shape fields, and Channel is not one), but a channel-less
+ // record must not become — or converge — a route pinned to no channel:
+ // such a row resolves no release, so it has no build to exec, and the
+ // worker boot pass would try to run it against a symlink that does not
+ // exist. The console stamps a channel at create, so a record without
+ // one is malformed. Protected like a corrupted body: the record is
+ // there, it just cannot be used.
+ if rec.Channel == "" {
+ log.Printf("provision: skipping sidecar record %s: empty channel", key)
+ protect[host] = true
+ continue
+ }
+ if rec.Region != cfg.Region || !poolServed(cfg.Pools, rec.Pool) {
+ continue // another agent's jurisdiction, not this pass's business
+ }
+ // Placement is the third scope axis (design §8), and it is applied
+ // HERE, beside region and pool, precisely so it behaves like them: a
+ // record this box may not run never enters recs, and the removal phase
+ // below therefore evacuates any row it left behind. "none" is a
+ // property of the BOX's network; "no-inbound" is a requirement of the
+ // WORKLOAD — the point of the two halves being independent is that a
+ // member gets a mechanical guarantee rather than an operator's promise,
+ // which a row left running on an inbound box would not be.
+ if m, _ := manifestFor(acct, app); !placeable(rec, m, cfg) {
+ log.Printf("provision: sidecar %s is not placeable here: it asks for %q placement and this box accepts inbound work (CARLOS_INBOUND=%q)",
+ rec.Host, sidecarPlacement(rec, m), cfg.Inbound)
+ continue
+ }
+ // Two records claiming one host is a collision this pass cannot
+ // arbitrate silently: whichever this listing reaches FIRST wins, and
+ // the loser is skipped loudly rather than overwriting the winner in
+ // the map with no trace of the conflict.
+ if prev, dup := recs[rec.Host]; dup {
+ log.Printf("provision: duplicate sidecar host %s: keeping %s, skipping %s", rec.Host,
+ SidecarRecordKey(prev.Account, prev.App, prev.Name), key)
+ continue
+ }
+ recs[rec.Host] = rec
}
- recs[rec.Host] = rec
}
trashed := trashGate(ctx, s, "sidecar")