carlos / platform Public

provision: sorted fallback listing; a real sidecar through the walk

Astra round 2 (accept with fixes):

- P3: the fallback path returned Store.List's order, and ListAllRecords
  had lost its own sort. It is sorted again, with a test over a store
  that lists in reverse.
- P3: the sidecar reader is now driven over the walk with a real sidecar
  record, and must mint and keep it across two passes.

Declined from the same round: request cost. The walk is about
1 + accounts + apps small listings per reader, against one full listing
of every object. On the flagship that is a few hundred one-page requests
in place of sixty pages per reader; a deployment of a thousand one-app
accounts would pay more requests for fewer objects, which is noted on
#231 rather than designed for now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev f745eb3c40273887c9632add042f001cd7c93b93 parent dc8c429
2 files changed, +57 −0
  • internal/provision/applisting_test.go +56 −0
  • internal/provision/reconcile.go +1 −0

CI passed — run details

diff --git a/internal/provision/applisting_test.go b/internal/provision/applisting_test.go
index 135eea120..71a61c3af 100644
--- a/internal/provision/applisting_test.go
+++ b/internal/provision/applisting_test.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"reflect"
+ "slices"
"sort"
"strings"
"sync"
@@ -279,3 +280,58 @@ func TestListAllRecordsRefusesAPartialWalk(t *testing.T) {
t.Fatalf("ListAllRecords over a failed app listing = %+v, nil; want an error", recs)
}
}
+
+// The sidecar reader over the walk mints the sidecar it mints over the full
+// listing, and keeps it on the next pass. Returning no sidecar keys at all
+// would otherwise pass every other test here.
+func TestSidecarReconcileOverTheWalkMintsAndKeeps(t *testing.T) {
+ f := sidecarSetup(t)
+ f.putManifest(t, "eleven", SidecarManifest{
+ Schema: 1, App: "eleven", Args: []string{"sidecar", "run"}, Channel: "stable"})
+ f.putRecord(t, SidecarRecord{Schema: 1, Name: "bot", App: "eleven", Channel: "stable"})
+ s := &bucketShaped{Store: f.store}
+ cfg := ReconcileConfig{DataDir: f.dir}
+ for pass := 1; pass <= 2; pass++ {
+ if err := ReconcileSidecars(ctx, s, f.reg, cfg); err != nil {
+ t.Fatalf("pass %d: %v", pass, err)
+ }
+ hasRoute(t, f.reg, f.host("bot", "eleven"), "the walk lost the sidecar")
+ }
+ if s.listedAllOfApps() {
+ t.Fatalf("the sidecar reader listed the whole of apps/: %v", s.lists)
+ }
+}
+
+// reversedList answers List in reverse order and makes no bucket-shape
+// promise: Store.List does not promise an order.
+type reversedList struct{ release.Store }
+
+func (s reversedList) List(ctx context.Context, prefix string) ([]string, error) {
+ keys, err := s.Store.List(ctx, prefix)
+ slices.Reverse(keys)
+ return keys, err
+}
+
+// ListAllRecords returns records in a stable order whatever order the store
+// lists in, on the fallback path as on the walk.
+func TestListAllRecordsIsSortedOnTheFallback(t *testing.T) {
+ inner, sqid := setup(t)
+ for _, slug := range []string{"acme", "zeta", "beta"} {
+ rec := acmeRecord(sqid)
+ rec.Host, rec.Slug = slug+".go.tito.io", slug
+ if err := CreateRecord(ctx, inner, rec); err != nil {
+ t.Fatal(err)
+ }
+ }
+ recs, err := ListAllRecords(ctx, reversedList{inner})
+ if err != nil {
+ t.Fatal(err)
+ }
+ var hosts []string
+ for _, r := range recs {
+ hosts = append(hosts, r.Host)
+ }
+ if !slices.IsSorted(hosts) {
+ t.Fatalf("ListAllRecords over a reverse-listing store = %v, want sorted", hosts)
+ }
+}
diff --git a/internal/provision/reconcile.go b/internal/provision/reconcile.go
index 0551541bb..b77b5e50e 100644
--- a/internal/provision/reconcile.go
+++ b/internal/provision/reconcile.go
@@ -1398,6 +1398,7 @@ func ListAllRecords(ctx context.Context, s release.Store) ([]Record, error) {
func listAppSubtree(ctx context.Context, s release.Store, known map[string]bool, sub string) (keys []string, partial bool, err error) {
if !release.HasBucketListDirs(s) {
keys, err := s.List(ctx, "apps/")
+ slices.Sort(keys) // Store.List promises no order; the walk's is sorted
return keys, false, err
}
accts, err := s.ListDirs(ctx, "apps/")