§469 (GR, A-8 line of G): a bucket composed from an old state section takes its maximum size from its max-mib

The grants part's composer gains Bucket.MaxMiB and Bucket.MaxBytes() (as mesh-sdk's buscompose has them): the bucket's stream is
created with that cap and an assertion raises a held one to it; an entry naming none keeps the mesh's cap, so no bucket of today
changes.

Builds: design 47 G §469, §695
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-11 23:05:15 +01:00
parent a2ac794e7a
commit 2992b08a11
5 changed files with 52 additions and 4 deletions
+29
View File
@@ -5033,3 +5033,32 @@ func TestThePartNamesTheStreamItReadsWholeWaiting(t *testing.T) {
t.Fatalf("after its whole read ended the streams are %+v, want it read", got)
}
}
// TestAnOldStateSectionDeclaresItsCap is design 47 G §469: a bucket composed from an old state section takes its maximum size from the
// entry's `max-mib`, and one naming none keeps the mesh's cap, so no bucket of today changes.
func TestAnOldStateSectionDeclaresItsCap(t *testing.T) {
m, err := catalogue.Decode([]byte(`{"module":"mod-p","version":"1","state":[{"name":"pipelines","max-mib":256},"plain"]}`))
if err != nil {
t.Fatal(err)
}
want := map[string]int64{"KV_mod-p_pipelines": 256 << 20, "KV_mod-p_plain": 0}
got := map[string]int64{}
for _, it := range grants.Composed(m, []string{"a"}) {
if it.Kind == "bucket" {
got[it.Stream] = it.Composed.MaxBytes
}
}
if !maps.Equal(got, want) {
t.Errorf("the composed buckets' caps = %v, want %v", got, want)
}
for name, max := range map[string]int{"pipelines": 256, "plain": 0} {
cfg := buscompose.BucketStreamConfig(buscompose.Bucket{Module: "mod-p", Name: name, MaxMiB: max})
wantBytes := int64(buscompose.StateMaxBytes)
if max > 0 {
wantBytes = int64(max) << 20
}
if cfg.MaxBytes != wantBytes {
t.Errorf("%s is created with %d bytes as its maximum size, want %d", name, cfg.MaxBytes, wantBytes)
}
}
}
@@ -43,6 +43,10 @@ func BucketStreamConfig(b Bucket) jetstream.StreamConfig {
AllowDirect: true,
Discard: jetstream.DiscardNew,
}
// An old state section's entry names its own cap (design 47 G §469); one naming none keeps the mesh's.
if max := b.MaxBytes(); max > 0 {
cfg.MaxBytes = max
}
if b.KeyTTL {
cfg.AllowMsgTTL, cfg.SubjectDeleteMarkerTTL = true, KeyTTLMarkerTTL
}
@@ -48,6 +48,18 @@ type Bucket struct {
// Whole is the bucket's name where it is not BucketName(Module, Name): one node's bucket of a state kept one bucket per
// node, the node-engine's compositions `composed-<node>` (ADR 0593 §6, EngineComposedBuckets).
Whole string
// MaxMiB is the bucket's cap in MiB, from an old state section's `max-mib` (design 47 G §469); zero names none, and the bucket
// keeps the mesh's cap (StateMaxBytes) as every bucket of today does.
MaxMiB int
}
// MaxBytes is the bucket's cap in bytes as the bus's maximum size, from its MaxMiB: -1 where it names none (buscompose.Bucket.MaxBytes
// of mesh-sdk).
func (b Bucket) MaxBytes() int64 {
if b.MaxMiB <= 0 {
return -1
}
return int64(b.MaxMiB) * 1024 * 1024
}
// KeyTTLMarkerTTL is how long the marker an expired key leaves is kept: long enough for a watcher to hear it.
@@ -277,9 +277,12 @@ func Composed(m catalogue.Manifest, nodes []string) []Item {
if per < 1 {
per = 1
}
out = append(out, Item{Kind: "bucket", Stream: "KV_" + b.Bucket(), Composed: Limits{
Subjects: []string{"$KV." + b.Bucket() + ".>"}, Retention: string(buscompose.RetentionLimits),
MaxAgeHours: b.TTLSeconds / 3600, MaxPerSubject: per}})
limits := Limits{Subjects: []string{"$KV." + b.Bucket() + ".>"}, Retention: string(buscompose.RetentionLimits),
MaxAgeHours: b.TTLSeconds / 3600, MaxPerSubject: per}
if max := b.MaxBytes(); max > 0 {
limits.MaxBytes = max
}
out = append(out, Item{Kind: "bucket", Stream: "KV_" + b.Bucket(), Composed: limits})
}
for _, l := range logsOf(m) {
c := l.Config()
@@ -308,7 +308,7 @@ func issuedTools(m catalogue.Manifest) []string {
func bucketsOf(m catalogue.Manifest) []buscompose.Bucket {
var out []buscompose.Bucket
for _, s := range m.State {
out = append(out, buscompose.Bucket{Module: m.Module, Name: s.Name, History: s.History, TTLSeconds: s.TTLSeconds,
out = append(out, buscompose.Bucket{Module: m.Module, Name: s.Name, History: s.History, TTLSeconds: s.TTLSeconds, MaxMiB: s.MaxMiB,
PerMachine: s.PerMachine, KeyTTL: s.KeyTTL, ReadOnly: s.ReadOnly})
}
return out