O-1: C-S1: one rule for the bus names of an entry in catalogue, the object store beside a state and the sent store (milestone 47/G, issue 1966, ADR 0666 section 16)
Builds: design 47 G §58, §59 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package catalogue
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The names of what a module keeps on the bus, as one rule (design 25 § 12.1; novox/hq ADR 0666 §16): the bus module
|
||||
// creates them from a module's entries and the grants part grants them, and both take the names from here.
|
||||
|
||||
// BusNameSeparator joins a module's name and an entry's local name into the name the mesh gives the entry on the bus.
|
||||
const BusNameSeparator = "_"
|
||||
|
||||
// SentObjectsLocal is the local name of a module's store of sent objects (the bodies of a reply or an event over the
|
||||
// per-message limit): the bus module plans `<module>_sent-objects` for every module, and no entry may take the name.
|
||||
const SentObjectsLocal = "sent-objects"
|
||||
|
||||
// ObjectsSuffix ends the local name of the object store beside a state: `<state>-objects`.
|
||||
const ObjectsSuffix = "-objects"
|
||||
|
||||
// safeToken is a name that is one plain subject token.
|
||||
var safeToken = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
|
||||
|
||||
// BusName is `<module>_<local>`, the name the mesh gives an entry on the bus.
|
||||
func BusName(module, local string) string { return module + BusNameSeparator + local }
|
||||
|
||||
// StateObjectsName is the local name of the object store that holds the large values of the state `state`.
|
||||
func StateObjectsName(state string) string { return state + ObjectsSuffix }
|
||||
|
||||
// SentObjectsName is the bus name of a module's store of sent objects.
|
||||
func SentObjectsName(module string) string { return BusName(module, SentObjectsLocal) }
|
||||
|
||||
// BusStateStreams are the streams one bus-state entry of module is kept in on the bus, by the mesh's names
|
||||
// `<module>_<local>`: a stream's own; a key-value bucket's KV_ stream with the OBJ_ stream of its object store
|
||||
// `<local>-objects`; an object store's OBJ_ stream; a ledger's history stream, its snapshot stream, and its state and
|
||||
// index buckets' KV_ streams. A name that is not one plain subject token names nothing.
|
||||
func BusStateStreams(module string, e BusStateEntry) []string {
|
||||
base := BusName(module, e.Name)
|
||||
if !safeToken.MatchString(base) || strings.Contains(e.Name, BusNameSeparator) || strings.Contains(module, BusNameSeparator) {
|
||||
return nil
|
||||
}
|
||||
var out []string
|
||||
switch e.Storage {
|
||||
case StorageKeyValue:
|
||||
out = []string{"KV_" + base}
|
||||
if e.Template == TemplateState {
|
||||
out = append(out, "OBJ_"+BusName(module, StateObjectsName(e.Name)))
|
||||
}
|
||||
case StorageObjectStore:
|
||||
out = []string{"OBJ_" + base}
|
||||
case StorageLedger:
|
||||
out = []string{base, base + "_snapshot", "KV_" + base + "_state"}
|
||||
for _, t := range e.Types {
|
||||
for _, ix := range t.Indexes {
|
||||
if safeToken.MatchString(ix.Name) {
|
||||
out = append(out, "KV_"+base+"_index-"+ix.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
out = []string{base}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return slices.Compact(out)
|
||||
}
|
||||
|
||||
// ObjectNameClashes are the local names of a module's bus-state entries that meet a name the mesh makes itself: the
|
||||
// name of a state's object store (`<state>-objects` beside the state `<state>`) and the store of sent objects. The
|
||||
// indexer's judge refuses each.
|
||||
func ObjectNameClashes(entries []BusStateEntry) []string {
|
||||
states := map[string]bool{}
|
||||
for _, e := range entries {
|
||||
if e.Storage == StorageKeyValue && e.Template == TemplateState {
|
||||
states[e.Name] = true
|
||||
}
|
||||
}
|
||||
var out []string
|
||||
for _, e := range entries {
|
||||
stem, ok := strings.CutSuffix(e.Name, ObjectsSuffix)
|
||||
if e.Name == SentObjectsLocal || (ok && states[stem]) {
|
||||
out = append(out, e.Name)
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return slices.Compact(out)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package catalogue
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ADR 0666 §16: the bus names of an entry are one rule. A state entry answers its bucket's stream and the stream of
|
||||
// its object store; a local name that meets a name the mesh makes is a clash for the judge.
|
||||
func TestTheBusNamesOfAnEntryAreOneRule(t *testing.T) {
|
||||
state := BusStateEntry{Name: "views", Storage: StorageKeyValue, Template: TemplateState}
|
||||
rows := []struct {
|
||||
name string
|
||||
entry BusStateEntry
|
||||
streams []string
|
||||
}{
|
||||
{"a state: its bucket and its object store", state, []string{"KV_m_views", "OBJ_m_views-objects"}},
|
||||
{"a key-value that is not a state template: its bucket only", BusStateEntry{Name: "x", Storage: StorageKeyValue}, []string{"KV_m_x"}},
|
||||
{"an object store", BusStateEntry{Name: "blobs", Storage: StorageObjectStore, Template: TemplateObjects}, []string{"OBJ_m_blobs"}},
|
||||
{"a stream", BusStateEntry{Name: "log", Storage: StorageStream, Template: TemplateLog}, []string{"m_log"}},
|
||||
{"a ledger", BusStateEntry{Name: "l", Storage: StorageLedger, Template: TemplateLedger}, []string{"KV_m_l_state", "m_l", "m_l_snapshot"}},
|
||||
{"a name that is not one token", BusStateEntry{Name: "a.b", Storage: StorageStream}, nil},
|
||||
{"a local name holding the separator", BusStateEntry{Name: "a_b", Storage: StorageStream}, nil},
|
||||
}
|
||||
for _, r := range rows {
|
||||
if got := BusStateStreams("m", r.entry); !slices.Equal(got, r.streams) {
|
||||
t.Errorf("%s: streams %v, want %v", r.name, got, r.streams)
|
||||
}
|
||||
}
|
||||
if got := SentObjectsName("m"); got != "m_sent-objects" {
|
||||
t.Errorf("sent store %q", got)
|
||||
}
|
||||
clash := []BusStateEntry{state, {Name: "views-objects", Storage: StorageObjectStore}, {Name: "other-objects", Storage: StorageObjectStore},
|
||||
{Name: SentObjectsLocal, Storage: StorageObjectStore}}
|
||||
if got := ObjectNameClashes(clash); !slices.Equal(got, []string{"sent-objects", "views-objects"}) {
|
||||
t.Errorf("clashes %v", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user