carlos / platform Public

genselect: GenerationMax, the one rule for a generation's maximum in a box's own bucket (moves Task 4)

Select and GenerationMax read a key's coordinates through one helper,
currentFile. platform-1e's answer on #236 (Q5): no cross-region reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 60c4700aa0aaf4bcad2017ea445701ba931afd7a parent e22f493
2 files changed, +184 −14
  • internal/genselect/generationmax_test.go +108 −0
  • internal/genselect/select.go +76 −14
diff --git a/internal/genselect/generationmax_test.go b/internal/genselect/generationmax_test.go
new file mode 100644
index 000000000..88c25319b
--- /dev/null
+++ b/internal/genselect/generationmax_test.go
@@ -0,0 +1,108 @@
+package genselect
+
+import (
+ "context"
+ "errors"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/hostclaim"
+ "github.com/carlosframework/platform/internal/lease"
+)
+
+// GenerationMax is the one rule for a generation's remote maximum in a box's
+// own bucket (moves Task 4, platform-1e's answer on #236): the highest
+// maximum among the generation's current level files, read as Select reads
+// them, so a selectable generation's maximum is Select's.
+func TestGenerationMax(t *testing.T) {
+ ctx := context.Background()
+ t.Run("the marker's generation is Select's maximum", func(t *testing.T) {
+ f := newFixture(t)
+ f.file("g0", 1, 1, 14)
+ f.file("g0", 0, 12, 14)
+ plan, err := Select(ctx, f.store, f.req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got, err := GenerationMax(ctx, f.store, "data", f.req.Holder.Location, "g0")
+ if err != nil || got != 14 || got != plan.MaxTXID {
+ t.Fatalf("GenerationMax %d %v, Select %d", got, err, plan.MaxTXID)
+ }
+ })
+ t.Run("one generation's files only", func(t *testing.T) {
+ f := newFixture(t)
+ f.file("g1", 0, 1, 99)
+ got, err := GenerationMax(ctx, f.store, "data", f.req.Holder.Location+"/", "g0")
+ if err != nil || got != 11 {
+ t.Fatalf("g0 %d %v", got, err)
+ }
+ if got, err = GenerationMax(ctx, f.store, "data", f.req.Holder.Location, "g1"); err != nil || got != 99 {
+ t.Fatalf("g1 %d %v", got, err)
+ }
+ })
+ t.Run("the origin is the location's own level files", func(t *testing.T) {
+ f := newFixture(t)
+ f.file(hostclaim.RootGeneration, 9, 1, 7)
+ f.file(hostclaim.RootGeneration, 0, 8, 8)
+ for _, gen := range []string{hostclaim.RootGeneration, ""} {
+ if got, err := GenerationMax(ctx, f.store, "data", f.req.Holder.Location, gen); err != nil || got != 8 {
+ t.Fatalf("%q: %d %v", gen, got, err)
+ }
+ }
+ })
+ t.Run("a retention tombstone is not a current file", func(t *testing.T) {
+ f := newFixture(t)
+ late := f.file("g0", 0, 12, 12)
+ f.b.BareDelete("data", late.Key)
+ if got, err := GenerationMax(ctx, f.store, "data", f.req.Holder.Location, "g0"); err != nil || got != 11 {
+ t.Fatalf("%d %v", got, err)
+ }
+ })
+ t.Run("refusals", func(t *testing.T) {
+ f := newFixture(t)
+ if _, err := GenerationMax(ctx, f.store, "data", f.req.Holder.Location, "none"); !errors.Is(err, ErrEmpty) {
+ t.Fatalf("no files: %v", err)
+ }
+ for name, in := range map[string][3]string{
+ "no bucket": {"", f.req.Holder.Location, "g0"},
+ "no location": {"data", "", "g0"},
+ "a climbing path": {"data", "instances/../h", "g0"},
+ "a nested gen": {"data", f.req.Holder.Location, "g0/x"},
+ "a dot generation": {"data", f.req.Holder.Location, ".."},
+ "an absolute path": {"data", "/instances/h", "g0"},
+ "an empty element": {"data", "instances//h", "g0"},
+ "a trailing double": {"data", "instances/h//", "g0"},
+ } {
+ if _, err := GenerationMax(ctx, f.store, in[0], in[1], in[2]); !errors.Is(err, ErrInput) {
+ t.Fatalf("%s: %v", name, err)
+ }
+ }
+ if _, err := GenerationMax(ctx, nil, "data", f.req.Holder.Location, "g0"); !errors.Is(err, ErrInput) {
+ t.Fatalf("no reader: %v", err)
+ }
+ listing := altered{Reader: f.store, list: func([]lease.ObjectVersion) ([]lease.ObjectVersion, error) {
+ return nil, errors.New("access denied")
+ }}
+ if _, err := GenerationMax(ctx, listing, "data", f.req.Holder.Location, "g0"); !errors.Is(err, ErrListing) {
+ t.Fatalf("an unreadable listing: %v", err)
+ }
+ malformed := altered{Reader: f.store, list: func(vs []lease.ObjectVersion) ([]lease.ObjectVersion, error) {
+ return append(vs, lease.ObjectVersion{Key: f.req.Holder.Location + "/g-g0/0000/bogus.ltx", VersionID: "v", IsLatest: true}), nil
+ }}
+ if _, err := GenerationMax(ctx, malformed, "data", f.req.Holder.Location, "g0"); !errors.Is(err, ErrListing) {
+ t.Fatalf("a malformed level file: %v", err)
+ }
+ unversioned := altered{Reader: f.store, list: func(vs []lease.ObjectVersion) ([]lease.ObjectVersion, error) {
+ for i := range vs {
+ vs[i].VersionID = "null"
+ }
+ return vs, nil
+ }}
+ if _, err := GenerationMax(ctx, unversioned, "data", f.req.Holder.Location, "g0"); !errors.Is(err, ErrVersionID) {
+ t.Fatalf("no version ids: %v", err)
+ }
+ f.file("g0", 9, 2, 20)
+ if _, err := GenerationMax(ctx, f.store, "data", f.req.Holder.Location, "g0"); !errors.Is(err, ErrRoot) {
+ t.Fatalf("a snapshot not from 1: %v", err)
+ }
+ })
+}
diff --git a/internal/genselect/select.go b/internal/genselect/select.go
index 2dc4acad0..411f3b618 100644
--- a/internal/genselect/select.go
+++ b/internal/genselect/select.go
@@ -298,6 +298,78 @@ func SelectPrepared(ctx context.Context, s Reader, req Request, prepared Prepare
return selectFiles(ctx, s, req.Bucket, base, versions, latest, p, pin)
}
+// GenerationMax is generation gen's remote maximum under location in
+// bucket: the highest maximum TXID among its current level files (the
+// latest version of each key, never a retention tombstone), read with the
+// coordinates and version rules Select reads, so a generation Select would
+// choose has Select's maximum. It is the one rule for a generation's
+// maximum where a box reads its own bucket: the claim writer's loss restore
+// point (hostbox.Writer) and moves' loss baseline validator read it alike.
+// "" or hostclaim.RootGeneration is the location's own level files. A
+// generation with no current level file is ErrEmpty.
+func GenerationMax(ctx context.Context, s Reader, bucket, location, gen string) (uint64, error) {
+ location = strings.TrimSuffix(location, "/")
+ if s == nil || bucket == "" || location == "" || (gen != "" && gen != hostclaim.RootGeneration && !generation(gen)) {
+ return 0, ErrInput
+ }
+ for _, part := range strings.Split(location, "/") {
+ if !component(part) {
+ return 0, ErrInput
+ }
+ }
+ base := prefix(location+"/", origin(gen))
+ _, latest, err := listVersions(ctx, s, bucket, base)
+ if err != nil {
+ return 0, err
+ }
+ max, found := uint64(0), false
+ for _, v := range latest {
+ f, ok, err := currentFile(v, base)
+ if err != nil {
+ return 0, err
+ }
+ if !ok {
+ continue
+ }
+ if f.Level == 9 && f.MinTXID != 1 {
+ return 0, ErrRoot
+ }
+ if !found || f.MaxTXID > max {
+ max, found = f.MaxTXID, true
+ }
+ }
+ if !found {
+ return 0, ErrEmpty
+ }
+ return max, nil
+}
+
+// currentFile reads one latest version under base: a current level file
+// (ok), not one (a retention tombstone, another generation's key, a marker),
+// or a key shaped as a level file that does not parse (ErrListing). Select
+// and GenerationMax read the coordinates through it alone.
+func currentFile(v lease.ObjectVersion, base string) (File, bool, error) {
+ if _, ok := coordinates(v.Key, base); !ok {
+ if strings.HasPrefix(v.Key, base) {
+ parts := strings.Split(strings.TrimPrefix(v.Key, base), "/")
+ if len(parts) == 2 && len(parts[0]) == 4 && strings.HasSuffix(parts[1], ".ltx") {
+ if level, e := strconv.ParseUint(parts[0], 16, 16); e == nil && level <= 9 {
+ return File{}, false, ErrListing
+ }
+ }
+ }
+ return File{}, false, nil
+ }
+ if v.DeleteMarker {
+ return File{}, false, nil
+ } // Retention tombstones are not current files.
+ f, err := exactFile(v, base)
+ if err != nil {
+ return File{}, false, err
+ }
+ return f, true, nil
+}
+
// Both entry points share exact-version root selection and continuation.
func selectFiles(ctx context.Context, s Reader, bucket, base string, versions []lease.ObjectVersion,
latest map[string]lease.ObjectVersion, p *Plan, pin *Pin) (*Plan, error) {
@@ -319,23 +391,13 @@ func selectFiles(ctx context.Context, s Reader, bucket, base string, versions []
}
var current []File
for _, v := range latest {
- f, ok := coordinates(v.Key, base)
- if !ok && strings.HasPrefix(v.Key, base) {
- rel := strings.TrimPrefix(v.Key, base)
- parts := strings.Split(rel, "/")
- if len(parts) == 2 && len(parts[0]) == 4 && strings.HasSuffix(parts[1], ".ltx") {
- if level, e := strconv.ParseUint(parts[0], 16, 16); e == nil && level <= 9 {
- return nil, ErrListing
- }
- }
- }
- if !ok || v.DeleteMarker {
- continue
- } // Retention tombstones are not current files.
- f, err := exactFile(v, base)
+ f, ok, err := currentFile(v, base)
if err != nil {
return nil, err
}
+ if !ok {
+ continue
+ }
if f.Level == 9 && f.MinTXID != 1 {
return nil, ErrRoot
}