Documentation
¶
Index ¶
- Variables
- func AllocateShortId(entityId string, exists ExistsCheck) (string, error)
- func As[T any, P interface{ ... }](ent AttrGetter) (P, bool)
- func Decode(data []byte, v any) error
- func Empty[T comparable](x T) bool
- func Encode(v any) ([]byte, error)
- func ExtractBase58Suffix(entityId string) (string, bool)
- func InitSystemEntities(save func(*Entity) error) error
- func Is(ent AttrGetter, kind Id) bool
- func MigrateEntityStore(ctx context.Context, log *slog.Logger, client *clientv3.Client, ...) (migrated int, skipped int, err error)
- func MigrateShortIds(ctx context.Context, log *slog.Logger, client *clientv3.Client, ...) (migrated int, skipped int, err error)
- func MustKeyword(str string) types.Keyword
- func TryAs(ent AttrGetter, ea EntityAs) bool
- func ValidKeyword(str string) bool
- func WithOverwrite(opts *entityOpts)
- type Attr
- func Any(id Id, v any) Attr
- func Bool(id Id, v bool) Attr
- func Bytes(id Id, b []byte) Attr
- func Component(id Id, attrs []Attr) Attr
- func Diff(a, b *Entity) []Attr
- func Duration(id Id, v time.Duration) Attr
- func Float64(id Id, v float64) Attr
- func Int(id Id, value int) Attr
- func Int64(id Id, value int64) Attr
- func Keyword[T Keywordable](id Id, v T) Attr
- func Label(id Id, key, val string) Attr
- func MustGet(e AttrGetter, name Id) Attr
- func Named[T Keywordable](v T) Attr
- func Pair(id Id, x, y Value) Attr
- func Ref(id Id, v Id) Attr
- func SortedAttrs(attrs []Attr) []Attr
- func String(id Id, value string) Attr
- func Time(id Id, v time.Time) Attr
- func Uint64(id Id, v uint64) Attr
- type AttrGetter
- type AttrOp
- type AttrOpCode
- type AttrOps
- type AttributeSchema
- type CleanupOptions
- type CleanupStats
- type EncodedDomain
- type EncodedSchema
- type Entity
- func (e *Entity) Attrs() []Attr
- func (e *Entity) Clone() *Entity
- func (e *Entity) Compare(other *Entity) int
- func (e *Entity) Fixup() error
- func (e *Entity) ForceID()
- func (e *Entity) Get(name Id) (Attr, bool)
- func (e *Entity) GetAll(name Id) []Attr
- func (e *Entity) GetCreatedAt() time.Time
- func (e *Entity) GetRevision() int64
- func (e *Entity) GetUpdatedAt() time.Time
- func (e *Entity) Id() Id
- func (e *Entity) MarshalCBOR() ([]byte, error)
- func (e *Entity) MarshalJSON() ([]byte, error)
- func (e *Entity) Merge(other *Entity) error
- func (e *Entity) ReadInfo(val any) error
- func (e *Entity) Remove(id Id) bool
- func (e *Entity) Set(newAttr Attr) bool
- func (e *Entity) SetCreatedAt(ts time.Time) bool
- func (e *Entity) SetID(id Id) bool
- func (e *Entity) SetRevision(rev int64)
- func (e *Entity) SetUpdatedAt(ts time.Time) bool
- func (e *Entity) ShortId() string
- func (e *Entity) Timeless() *Entity
- func (e *Entity) UnmarshalCBOR(data []byte) error
- func (e *Entity) UnmarshalJSON(data []byte) error
- func (e *Entity) Update(attrs []Attr) error
- type EntityAs
- type EntityComponent
- func (e *EntityComponent) Attrs() []Attr
- func (e *EntityComponent) Compare(other *EntityComponent) int
- func (e *EntityComponent) Equal(other *EntityComponent) bool
- func (e *EntityComponent) Get(name Id) (Attr, bool)
- func (e *EntityComponent) GetAll(name Id) []Attr
- func (e *EntityComponent) MarshalCBOR() ([]byte, error)
- func (e *EntityComponent) MarshalJSON() ([]byte, error)
- func (e *EntityComponent) ReadInfo(val any) error
- func (e *EntityComponent) UnmarshalCBOR(data []byte) error
- func (e *EntityComponent) UnmarshalJSON(data []byte) error
- type EntityGetter
- type EntityOp
- type EntityOpType
- type EntityOption
- type EntityPage
- type EntityStore
- type EtcdStore
- func (s *EtcdStore) AssertLease(ctx context.Context, lease int64) error
- func (s *EtcdStore) BuildUniqueKeyForTest(attr Attr) string
- func (s *EtcdStore) CleanupStaleCollectionEntries(ctx context.Context, log *slog.Logger, opts CleanupOptions) (*CleanupStats, error)
- func (s *EtcdStore) ClearReindexState(ctx context.Context) error
- func (s *EtcdStore) ClearSchemaCache()
- func (s *EtcdStore) Client() *clientv3.Client
- func (s *EtcdStore) CollectionPrefix(ctx context.Context, collection string) (string, error)
- func (s *EtcdStore) CreateEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
- func (s *EtcdStore) CreateLease(ctx context.Context, ttl int64) (int64, error)
- func (s *EtcdStore) CreateSession(ctx context.Context, ttl int64) ([]byte, error)
- func (s *EtcdStore) DeleteEntity(ctx context.Context, id Id) error
- func (s *EtcdStore) DeletePrefixCount(ctx context.Context, prefix string) (int64, error)
- func (s *EtcdStore) EnsureEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, bool, error)
- func (s *EtcdStore) GetAttributeSchema(ctx context.Context, id Id) (*AttributeSchema, error)
- func (s *EtcdStore) GetEntities(ctx context.Context, ids []Id) ([]*Entity, error)
- func (s *EtcdStore) GetEntity(ctx context.Context, id Id) (*Entity, error)
- func (s *EtcdStore) GetEntityAtRevision(ctx context.Context, id Id, rev int64) (*Entity, error)
- func (s *EtcdStore) GetOneIndex(ctx context.Context, attr Attr) (Id, error)
- func (s *EtcdStore) IndexPrefix(ctx context.Context, attr Attr) (string, error)
- func (s *EtcdStore) ListCollection(ctx context.Context, collection string) ([]Id, error)
- func (s *EtcdStore) ListIndex(ctx context.Context, attr Attr) ([]Id, error)
- func (s *EtcdStore) ListIndexEntitiesPage(ctx context.Context, attr Attr, cursor string, limit int64) (*EntityPage, error)
- func (s *EtcdStore) ListIndexPage(ctx context.Context, attr Attr, cursor string, limit int64) (*IndexPage, error)
- func (s *EtcdStore) ListIndexPageAtRevision(ctx context.Context, attr Attr, cursor string, limit, revision int64) (*IndexPage, error)
- func (s *EtcdStore) ListIndexRevision(ctx context.Context, attr Attr) ([]Id, int64, error)
- func (s *EtcdStore) ListSessionEntities(ctx context.Context, session []byte) ([]Id, error)
- func (s *EtcdStore) LoadIndexHash(ctx context.Context) (string, error)
- func (s *EtcdStore) LoadReindexState(ctx context.Context, log *slog.Logger) (*ReindexState, error)
- func (s *EtcdStore) PatchEntity(ctx context.Context, current *Entity, opts ...EntityOption) (*Entity, error)
- func (s *EtcdStore) PingSession(ctx context.Context, session []byte) error
- func (s *EtcdStore) Prefix() string
- func (s *EtcdStore) Reindex(ctx context.Context, log *slog.Logger, opts ReindexOptions) (*ReindexStats, error)
- func (s *EtcdStore) ReplaceEntity(ctx context.Context, current *Entity, opts ...EntityOption) (*Entity, error)
- func (s *EtcdStore) RevokeLease(ctx context.Context, lease int64) error
- func (s *EtcdStore) RevokeSession(ctx context.Context, session []byte) error
- func (s *EtcdStore) SaveIndexHash(ctx context.Context, hash string) error
- func (s *EtcdStore) SaveReindexState(ctx context.Context, state *ReindexState) error
- func (s *EtcdStore) SourceEpoch(ctx context.Context) (string, error)
- func (s *EtcdStore) UpdateEntity(ctx context.Context, id Id, changes *Entity, opts ...EntityOption) (*Entity, error)
- func (s *EtcdStore) WatchEntity(ctx context.Context, id Id) (chan EntityOp, error)
- func (s *EtcdStore) WatchIndex(ctx context.Context, attr Attr, fromRev int64) (clientv3.WatchChan, error)
- type ExistsCheck
- type FileStore
- func (s *FileStore) CreateEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
- func (s *FileStore) DeleteEntity(_ context.Context, id Id) error
- func (s *FileStore) GetAttributeSchema(ctx context.Context, id Id) (*AttributeSchema, error)
- func (s *FileStore) GetEntities(_ context.Context, ids []Id) ([]*Entity, error)
- func (s *FileStore) GetEntity(_ context.Context, id Id) (*Entity, error)
- func (s *FileStore) ListCollection(_ context.Context, collection string) ([]Id, error)
- func (s *FileStore) ListIndex(ctx context.Context, attr Attr) ([]Id, error)
- func (s *FileStore) UpdateEntity(ctx context.Context, id Id, updates *Entity, opts ...EntityOption) (*Entity, error)
- type Id
- type IndexPage
- type Keywordable
- type Meta
- type MigrateOptions
- type MigrateShortIdOptions
- type MockStore
- func (m *MockStore) AddEntity(id Id, entity *Entity)
- func (m *MockStore) AddStaleIndexEntry(attr Attr, id Id)
- func (m *MockStore) CreateEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
- func (m *MockStore) CreateSession(ctx context.Context, id int64) ([]byte, error)
- func (m *MockStore) DeleteEntity(ctx context.Context, id Id) error
- func (m *MockStore) EnsureEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, bool, error)
- func (m *MockStore) GetAttributeSchema(ctx context.Context, id Id) (*AttributeSchema, error)
- func (m *MockStore) GetEntities(ctx context.Context, ids []Id) ([]*Entity, error)
- func (m *MockStore) GetEntity(ctx context.Context, id Id) (*Entity, error)
- func (m *MockStore) GetEntityAtRevision(ctx context.Context, id Id, rev int64) (*Entity, error)
- func (m *MockStore) ListCollection(ctx context.Context, collection string) ([]Id, error)
- func (m *MockStore) ListIndex(ctx context.Context, attr Attr) ([]Id, error)
- func (m *MockStore) ListIndexEntitiesPage(ctx context.Context, attr Attr, cursor string, limit int64) (*EntityPage, error)
- func (m *MockStore) ListIndexPage(ctx context.Context, attr Attr, cursor string, limit int64) (*IndexPage, error)
- func (m *MockStore) ListIndexPageAtRevision(ctx context.Context, attr Attr, cursor string, limit, revision int64) (*IndexPage, error)
- func (m *MockStore) ListIndexRevision(ctx context.Context, attr Attr) ([]Id, int64, error)
- func (m *MockStore) ListSessionEntities(ctx context.Context, id []byte) ([]Id, error)
- func (m *MockStore) Now() time.Time
- func (m *MockStore) PatchEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
- func (m *MockStore) PingSession(ctx context.Context, id []byte) error
- func (m *MockStore) RemoveEntity(id Id)
- func (m *MockStore) ReplaceEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
- func (m *MockStore) RevokeSession(ctx context.Context, id []byte) error
- func (m *MockStore) SourceEpoch(context.Context) (string, error)
- func (m *MockStore) UpdateEntity(ctx context.Context, id Id, entity *Entity, opts ...EntityOption) (*Entity, error)
- func (m *MockStore) WaitForEntityWatcher(ctx context.Context, id Id) error
- func (m *MockStore) WaitForIndexWatcher(ctx context.Context, attr Attr) error
- func (m *MockStore) WatchEntity(ctx context.Context, id Id) (chan EntityOp, error)
- func (m *MockStore) WatchFromRevsCopy() []int64
- func (m *MockStore) WatchIndex(ctx context.Context, attr Attr, fromRev int64) (clientv3.WatchChan, error)
- type OldEntity
- type OldMeta
- type ReindexOptions
- type ReindexState
- type ReindexStats
- type SchemaCache
- type SchemaField
- type Store
- type Validator
- func (v *Validator) ValidateAttribute(ctx context.Context, attr *Attr) error
- func (v *Validator) ValidateAttributes(ctx context.Context, attrs []Attr) error
- func (v *Validator) ValidateEntity(ctx context.Context, entity *Entity) error
- func (v *Validator) ValidateUpdate(ctx context.Context, newAttrs, originalAttrs []Attr) error
- type Value
- func AnyValue(v any) Value
- func ArrayValue(values ...any) Value
- func BoolValue(v bool) Value
- func BytesValue(b []byte) Value
- func ComponentValue(vals ...any) Value
- func DurationValue(v time.Duration) Value
- func Float64Value(v float64) Value
- func Int64Value(v int64) Value
- func IntValue(v int) Value
- func KeywordValue[T Keywordable](v T) Value
- func LabelValue(key, val string) Value
- func RefValue(v Id) Value
- func StringValue(value string) Value
- func TimeValue(v time.Time) Value
- func Uint64Value(v uint64) Value
- func (v Value) Any() any
- func (v Value) Array() []Value
- func (v Value) Bool() bool
- func (v Value) Bytes() []byte
- func (v Value) Clone() Value
- func (v Value) Compare(w Value) int
- func (v Value) Component() *EntityComponent
- func (v Value) Duration() time.Duration
- func (v Value) Equal(w Value) bool
- func (v Value) Float64() float64
- func (v Value) Id() types.Id
- func (v Value) Int64() int64
- func (v Value) Keyword() types.Keyword
- func (v Value) Kind() ValueKind
- func (v Value) Label() types.Label
- func (v *Value) MarshalCBOR() ([]byte, error)
- func (v Value) MarshalJSON() ([]byte, error)
- func (v Value) String() string
- func (v Value) Time() time.Time
- func (v Value) Uint64() uint64
- func (v *Value) UnmarshalCBOR(b []byte) error
- func (v *Value) UnmarshalJSON(b []byte) error
- type ValueKind
Constants ¶
This section is empty.
Variables ¶
var ( ErrEntityNotFound = errors.New("entity not found") ErrEntityAlreadyExists = errors.New("entity already exists") ErrAttributeNotFound = errors.New("attribute not found") ErrInvalidAttribute = errors.New("invalid attribute") ErrSchemaNotFound = errors.New("schema not found") ErrShortIdExhausted = errors.New("failed to allocate unique short-id") )
Common errors
var ErrNotFound = errors.New("entity not found")
Functions ¶
func AllocateShortId ¶ added in v0.7.0
func AllocateShortId(entityId string, exists ExistsCheck) (string, error)
AllocateShortId generates a short, globally-unique identifier for an entity. It first tries to derive the short-id from the entity's base58 suffix, falling back to random generation if no suffix is available or all suffix-derived candidates collide.
func Empty ¶
func Empty[T comparable](x T) bool
func ExtractBase58Suffix ¶ added in v0.7.0
ExtractBase58Suffix extracts the high-entropy base58 portion from an entity ID. Returns the base58 portion and true if found, or empty string and false otherwise.
Handles all entity ID categories:
- Category 1 (named): "app/blog" → ("", false)
- Category 2 (name+prefix+base58): "blog-vCZ1eUgSgNd28ed6vt2DgY" → ("CZ1eUgSgNd28ed6vt2DgY", true)
- Category 3 (namespace/prefix-base58): "sandbox/blog-web-CZAtBvhsMNbG38MceikkB" → ("CZAtBvhsMNbG38MceikkB", true)
- Category 4 (kind-base58): "deployment-CZ1eUgSgNd28ed6vt2DgY" → ("CZ1eUgSgNd28ed6vt2DgY", true)
func InitSystemEntities ¶
func Is ¶
func Is(ent AttrGetter, kind Id) bool
func MigrateEntityStore ¶
func MigrateEntityStore(ctx context.Context, log *slog.Logger, client *clientv3.Client, opts MigrateOptions) (migrated int, skipped int, err error)
MigrateEntityStore migrates entities from the old CBOR format (with struct fields) to the new format (with attribute-based metadata).
This migration:
- Reads all entities from etcd under the given prefix
- Detects entities in the old format (ID/Revision/CreatedAt/UpdatedAt as struct fields)
- Converts them to the new format (ID/Revision/CreatedAt/UpdatedAt as attributes)
- Writes the migrated entities back to etcd
Returns the number of entities migrated, skipped, and any error encountered.
func MigrateShortIds ¶ added in v0.7.0
func MigrateShortIds(ctx context.Context, log *slog.Logger, client *clientv3.Client, opts MigrateShortIdOptions) (migrated int, skipped int, err error)
MigrateShortIds backfills db/short-id for all entities that don't have one. This is idempotent — entities that already have a short-id are skipped.
func MustKeyword ¶
MustKeyword converts a string to a Keyword, panicking if it's not valid
func TryAs ¶
func TryAs(ent AttrGetter, ea EntityAs) bool
func ValidKeyword ¶
ValidKeyword checks if a string is a valid keyword
func WithOverwrite ¶
func WithOverwrite(opts *entityOpts)
Types ¶
type Attr ¶
func Diff ¶
Diff returns the difference between two entities. Returns attributes that are in entity 'a' but not in entity 'b'
func Keyword ¶
func Keyword[T Keywordable](id Id, v T) Attr
func MustGet ¶
func MustGet(e AttrGetter, name Id) Attr
func Named ¶
func Named[T Keywordable](v T) Attr
func SortedAttrs ¶
type AttrGetter ¶
type AttrOpCode ¶
type AttrOpCode int
const ( AttrOpCodeNone AttrOpCode = iota AttrOpCodeAdd AttrOpCodeRemove )
type AttributeSchema ¶
type AttributeSchema struct {
ID Id
Doc string
Type Id
ElemType Id
Unique uniq
EnumValues []Value
AllowMany bool
Index bool
Session bool
Predicate []*Entity
CheckProgs []string
Tags []string
}
AttributeSchema defines the schema for an attribute
func GetAttributeSchemasByTag ¶
func GetAttributeSchemasByTag(ctx context.Context, store Store, tag string) ([]AttributeSchema, error)
GetAttributeSchemasByTag queries all attributes that have the specified tag and returns their schemas
type CleanupOptions ¶ added in v0.12.0
type CleanupOptions struct {
// DryRun scans and counts stale entries but deletes nothing.
DryRun bool
// MaxDeletes caps how many stale entries a single pass removes. A large
// legacy backlog then drains over several passes rather than one thundering
// sweep. Zero means unbounded (the manual reindex "big hammer").
MaxDeletes int
// BatchPause is slept after every cleanupDeleteBatchSize deletes to
// rate-limit write pressure. Zero disables pacing.
BatchPause time.Duration
}
CleanupOptions bounds a single stale-cleanup pass so it can run continuously in the background without overwhelming the store.
type CleanupStats ¶ added in v0.12.0
type CleanupStats struct {
CollectionEntriesScanned int64
StaleEntriesFound int64
StaleEntriesRemoved int64
// OrphanedEntriesFound counts stale entries whose entity is gone entirely;
// MismatchedEntriesFound those whose entity exists but no longer carries the
// value the entry indexes. The two have different sources, so the split says
// which leak is live when one keeps re-accumulating.
OrphanedEntriesFound int64
MismatchedEntriesFound int64
// CASConflicts counts entries that changed between scan and delete (the slot
// was legitimately recreated/re-leased), so the CAS guard skipped them.
CASConflicts int64
RemovedByCollection map[string]int64
}
CleanupStats reports what a stale-cleanup pass observed and did. RemovedByCollection breaks removals down by index/collection so a persistent, post-drain leak shows up as a specific collection that keeps re-accumulating orphans rather than hiding in an aggregate count.
type EncodedDomain ¶
type EncodedSchema ¶
type EncodedSchema struct {
Domain string `json:"domain" cbor:"domain"`
Name string `json:"name" cbor:"name"`
Version string `json:"version" cbor:"version"`
Kinds []string `json:"kinds" cbor:"kinds"`
Fields []*SchemaField `json:"fields" cbor:"fields"`
PrimaryKind string `json:"primary_kind" cbor:"primary_kind"`
}
func (*EncodedSchema) GetField ¶
func (es *EncodedSchema) GetField(name string) *SchemaField
type Entity ¶
type Entity struct {
// contains filtered or unexported fields
}
Entity represents an entity with a set of attributes
func New ¶
New constructs a slice of attributes from a variadic list of Attr, []Attr, func() []Attr, or key-value pairs.
func (*Entity) GetCreatedAt ¶
func (*Entity) GetRevision ¶
func (*Entity) GetUpdatedAt ¶
func (*Entity) MarshalCBOR ¶
func (*Entity) MarshalJSON ¶
func (*Entity) SetID ¶
SetID sets the entity ID (db/id attribute) and returns true if it replaced an existing ID
func (*Entity) SetRevision ¶
func (*Entity) ShortId ¶ added in v0.7.0
ShortId returns the entity's short-id, or empty string if not set.
func (*Entity) Timeless ¶
Timeless returns a copy of the entity without CreatedAt, UpdatedAt and Revision attributes
func (*Entity) UnmarshalCBOR ¶
func (*Entity) UnmarshalJSON ¶
type EntityAs ¶
type EntityAs interface {
Is(ag AttrGetter) bool
Decode(ag AttrGetter)
}
type EntityComponent ¶
type EntityComponent struct {
// contains filtered or unexported fields
}
func (*EntityComponent) Attrs ¶
func (e *EntityComponent) Attrs() []Attr
Attrs returns a clone of the component's attributes
func (*EntityComponent) Compare ¶
func (e *EntityComponent) Compare(other *EntityComponent) int
func (*EntityComponent) Equal ¶
func (e *EntityComponent) Equal(other *EntityComponent) bool
func (*EntityComponent) GetAll ¶
func (e *EntityComponent) GetAll(name Id) []Attr
func (*EntityComponent) MarshalCBOR ¶
func (e *EntityComponent) MarshalCBOR() ([]byte, error)
func (*EntityComponent) MarshalJSON ¶
func (e *EntityComponent) MarshalJSON() ([]byte, error)
func (*EntityComponent) ReadInfo ¶
func (e *EntityComponent) ReadInfo(val any) error
func (*EntityComponent) UnmarshalCBOR ¶
func (e *EntityComponent) UnmarshalCBOR(data []byte) error
func (*EntityComponent) UnmarshalJSON ¶
func (e *EntityComponent) UnmarshalJSON(data []byte) error
type EntityGetter ¶
type EntityOp ¶
type EntityOp struct {
Type EntityOpType
*Entity
}
type EntityOpType ¶
type EntityOpType int
const ( EntityOpCreate EntityOpType = iota EntityOpUpdate EntityOpDelete EntityOpStated )
type EntityOption ¶
type EntityOption func(*entityOpts)
func BondToSession ¶
func BondToSession(session []byte) EntityOption
func WithFromRevision ¶
func WithFromRevision(revision int64) EntityOption
func WithSession ¶
func WithSession(session []byte) EntityOption
type EntityPage ¶ added in v0.15.0
type EntityPage struct {
// Ids are the index entries in this page.
Ids []Id
// Entities line up with Ids positionally. A nil means the store no longer
// holds that id, or holds it in a form that would not decode; Undecodable
// separates the two, because a caller that reads nil as proof the entity is
// gone would be wrong about exactly the entities it can say the least
// about. It is always non-nil, so it is safe to index without a nil check.
Entities []*Entity
Undecodable map[Id]bool
// Cursor resumes the listing after this page, empty once the index is
// exhausted.
Cursor string
// Total counts the entries in the index, filled only when the caller starts
// from the head.
Total int64
// Revision is the store revision both reads were made at.
Revision int64
}
EntityPage is one bounded page of an index together with the entities it names, as returned by ListIndexEntitiesPage.
type EntityStore ¶
type EntityStore interface {
GetEntity(ctx context.Context, id Id) (*Entity, error)
GetAttributeSchema(ctx context.Context, name Id) (*AttributeSchema, error)
CreateEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
UpdateEntity(ctx context.Context, id Id, entity *Entity, opts ...EntityOption) (*Entity, error)
DeleteEntity(ctx context.Context, id Id) error
ListIndex(ctx context.Context, attr Attr) ([]Id, error)
ListCollection(ctx context.Context, collection string) ([]Id, error)
}
type EtcdStore ¶
type EtcdStore struct {
// contains filtered or unexported fields
}
EtcdStore implements Store using etcd
func NewEtcdStore ¶
func NewEtcdStore(ctx context.Context, log *slog.Logger, client *clientv3.Client, prefix string) (*EtcdStore, error)
NewEtcdStore creates a new etcd-backed entity store
func (*EtcdStore) AssertLease ¶
func (*EtcdStore) BuildUniqueKeyForTest ¶ added in v0.7.0
BuildUniqueKeyForTest exposes buildUniqueKey for use in tests.
func (*EtcdStore) CleanupStaleCollectionEntries ¶ added in v0.12.0
func (s *EtcdStore) CleanupStaleCollectionEntries(ctx context.Context, log *slog.Logger, opts CleanupOptions) (*CleanupStats, error)
CleanupStaleCollectionEntries scans collection (index) entries and removes every entry the backing entity does not justify. An entry is justified when the entity still exists and still carries an indexed value hashing to the entry's collection segment; anything else is drift. An orphan is the degenerate case, an entity with no justified values at all.
Safety rests on three things. Creates are atomic, so an entry whose entity is absent is genuinely orphaned with no create-in-flight race. Entities are resolved with a batched, linearizable read taken after the entry was scanned, so the verdict sees state at least as new as the entry. And each delete is CAS'd on the entry's mod-revision from scan time, which is what extends the ABA guard to the mismatch verdict: any writer that re-justifies an entry has to rewrite the key, so the compare fails and the entry survives.
Session-scoped entries and entities whose attributes cannot be resolved are left alone.
The pass is idempotent and bounded, so it is safe to run repeatedly as a background sweep.
func (*EtcdStore) ClearReindexState ¶ added in v0.13.0
ClearReindexState removes in-flight reindex progress, marking the work done.
func (*EtcdStore) ClearSchemaCache ¶ added in v0.4.0
func (s *EtcdStore) ClearSchemaCache()
ClearSchemaCache clears the in-memory schema cache, forcing subsequent GetAttributeSchema calls to re-read from etcd. This is used before reindexing to ensure fresh schema definitions are used.
func (*EtcdStore) CollectionPrefix ¶
func (*EtcdStore) CreateEntity ¶
func (s *EtcdStore) CreateEntity( ctx context.Context, entity *Entity, opts ...EntityOption, ) (*Entity, error)
CreateEntity implements Store interface
func (*EtcdStore) CreateLease ¶
func (*EtcdStore) CreateSession ¶
func (*EtcdStore) DeleteEntity ¶
DeleteEntity implements Store interface.
The delete is atomic: the entity key, its index (collection) entries, and its unique-value keys are all removed in a single revision-guarded transaction. If they were removed separately (as they once were, with the index deletes issued as immediate unconditional Deletes before the entity-key txn), a concurrent no-OCC patch could slip into the gap and re-add index entries after the entity was gone, leaking a stale index entry (MIR-1334).
Because the txn is guarded on the entity's ModRevision, a concurrent write that bumps the revision between our read and the txn makes it fail. Pools are patched on every reconcile tick via the no-OCC path, so a single-shot delete could lose that race repeatedly; we re-read and retry a bounded number of times so the delete wins against a busy reconcile loop instead of spuriously reporting the entity as already gone.
func (*EtcdStore) DeletePrefixCount ¶
DeletePrefixCount deletes all keys with prefix and returns count
func (*EtcdStore) EnsureEntity ¶
func (s *EtcdStore) EnsureEntity( ctx context.Context, entity *Entity, opts ...EntityOption, ) (*Entity, bool, error)
EnsureEntity creates an entity only if it doesn't already exist (idempotent create) The db/id attribute must be present in the attributes to identify the entity Returns (entity, true, nil) if created, (entity, false, nil) if already exists
func (*EtcdStore) GetAttributeSchema ¶
GetAttributeSchema implements Store interface
func (*EtcdStore) GetEntities ¶
func (*EtcdStore) GetEntityAtRevision ¶ added in v0.4.0
GetEntityAtRevision reads an entity at a specific etcd revision. This is used to retrieve entity data for delete events where the entity may no longer exist at the current revision. GetEntityAtRevision reads the entity as it was at rev, session attributes included. It goes through the same pinned batch read as GetEntities so both halves of the entity come from one point in time; reading the primary key alone would hand back an entity missing every session-scoped attribute, which for a node is its status.
func (*EtcdStore) GetOneIndex ¶ added in v0.7.0
GetOneIndex looks up a single entity by an indexed attribute value. Returns the entity ID if exactly one match is found, or ErrNotFound.
func (*EtcdStore) IndexPrefix ¶
func (*EtcdStore) ListCollection ¶
func (*EtcdStore) ListIndexEntitiesPage ¶ added in v0.15.0
func (s *EtcdStore) ListIndexEntitiesPage( ctx context.Context, attr Attr, cursor string, limit int64, ) (*EntityPage, error)
ListIndexEntitiesPage reads one page of an index and the entities it names, both as of one store revision.
Pinning is per page and deliberately not carried across a walk. A revision only stays readable inside etcd's compaction window, so a scan that held one revision across many pages of a large index would eventually fail with ErrCompacted rather than return a torn result, and for a caller paging a six-figure backlog on a small host that is the worse of the two failures. Within a page the window is milliseconds. So an entity written mid-walk may still be missed or repeated between pages; a caller that cannot tolerate that wants a watch, not a listing.
Entities line up with Ids positionally, with a nil for any id the store no longer holds, which for ids read out of an index is an ordinary race rather than a surprise.
func (*EtcdStore) ListIndexPage ¶ added in v0.14.0
func (s *EtcdStore) ListIndexPage(ctx context.Context, attr Attr, cursor string, limit int64) (*IndexPage, error)
ListIndexPage reads up to limit ids from attr's index, resuming after cursor.
Pages are not pinned to a common revision across calls, so an entity written between two pages can be missed or repeated. Pinning would need the caller to carry a revision that outlives the compaction window, which is the wrong trade for a debug listing; within a single page the read is consistent.
func (*EtcdStore) ListIndexPageAtRevision ¶ added in v0.15.0
func (s *EtcdStore) ListIndexPageAtRevision( ctx context.Context, attr Attr, cursor string, limit, revision int64, ) (*IndexPage, error)
ListIndexPageAtRevision reads one index page from a caller-selected store revision. A zero revision establishes a snapshot at the first read. Callers can then pass the returned revision with each continuation cursor so writes during a long scan cannot make entries disappear or repeat between pages.
func (*EtcdStore) ListIndexRevision ¶ added in v0.9.1
ListIndexRevision lists the ids matching attr and returns the etcd revision the listing was read at. The revision lets a caller resume a watch from that point (gap-free) instead of re-reconciling on every reconnect.
func (*EtcdStore) ListSessionEntities ¶
func (*EtcdStore) LoadIndexHash ¶ added in v0.13.0
LoadIndexHash returns the index schema hash the store is recorded as being consistent with. An empty string means no reindex has ever completed.
func (*EtcdStore) LoadReindexState ¶ added in v0.13.0
LoadReindexState returns the in-flight reindex progress, or nil if there is none. Unreadable or corrupt state is reported as absent rather than as an error: the safe fallback is a reindex from scratch, which is idempotent.
func (*EtcdStore) PatchEntity ¶
func (s *EtcdStore) PatchEntity( ctx context.Context, current *Entity, opts ...EntityOption, ) (*Entity, error)
PatchEntity merges attributes into an existing entity For cardinality=one attributes, replaces the value For cardinality=many attributes, adds to existing values The db/id attribute must be present in the attributes to identify the entity
func (*EtcdStore) PingSession ¶
func (*EtcdStore) Reindex ¶ added in v0.4.0
func (s *EtcdStore) Reindex(ctx context.Context, log *slog.Logger, opts ReindexOptions) (*ReindexStats, error)
Reindex rebuilds index (collection) entries for entities in the store, streaming the entity keyspace in bounded pages rather than materializing every ID up front.
A pass may be bounded with opts.MaxEntities and resumed from a later pass via stats.NextCursor, so a store too large to reindex inside one deadline still converges across several passes. Resuming forward-only is safe because the process running the reindex already has the current index schema loaded: anything written while it runs is indexed correctly on the write path, so only pre-existing entities need backfill and their keys don't move. The coordinator is a singleton, so there is no concurrent writer running an older schema that could land un-indexed entities behind the cursor.
If opts.CleanupStale is true, a pass that runs to completion also scans for and removes stale collection entries that point to non-existent entities.
func (*EtcdStore) ReplaceEntity ¶
func (s *EtcdStore) ReplaceEntity( ctx context.Context, current *Entity, opts ...EntityOption, ) (*Entity, error)
ReplaceEntity atomically replaces all attributes of an entity The db/id attribute must be present in the attributes to identify the entity
func (*EtcdStore) RevokeLease ¶
func (*EtcdStore) RevokeSession ¶
func (*EtcdStore) SaveIndexHash ¶ added in v0.13.0
SaveIndexHash records that the store is consistent with hash. Only call this after a reindex pass that ran to completion: it is the gate that stops the work from being retried, so writing it early strands entities un-indexed with nothing left to notice.
func (*EtcdStore) SaveReindexState ¶ added in v0.13.0
func (s *EtcdStore) SaveReindexState(ctx context.Context, state *ReindexState) error
SaveReindexState persists reindex progress so the next pass resumes from it.
func (*EtcdStore) SourceEpoch ¶ added in v0.15.0
SourceEpoch identifies one etcd revision history. Snapshot restore creates a new logical etcd cluster and therefore a new epoch, preventing a cloud cursor from being reused against an unrelated revision sequence.
func (*EtcdStore) UpdateEntity ¶
func (s *EtcdStore) UpdateEntity( ctx context.Context, id Id, changes *Entity, opts ...EntityOption, ) (*Entity, error)
UpdateEntity implements Store interface
func (*EtcdStore) WatchEntity ¶
type ExistsCheck ¶ added in v0.7.0
ExistsCheck is a function that reports whether a given short-id candidate is already in use.
type FileStore ¶
type FileStore struct {
// contains filtered or unexported fields
}
FileStore provides CRUD operations for entities
func NewFileStore ¶
NewFileStore creates a new entity store with the given base path
func (*FileStore) CreateEntity ¶
func (s *FileStore) CreateEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
CreateEntity creates a new entity with the given type and attributes
func (*FileStore) DeleteEntity ¶
DeleteEntity deletes an entity by ID
func (*FileStore) GetAttributeSchema ¶
GetAttributeSchema retrieves an attribute schema by ID
func (*FileStore) GetEntities ¶
GetEntities retrieves multiple entities by their IDs
func (*FileStore) ListCollection ¶
func (*FileStore) UpdateEntity ¶
func (s *FileStore) UpdateEntity(ctx context.Context, id Id, updates *Entity, opts ...EntityOption) (*Entity, error)
UpdateEntity updates an existing entity
type Id ¶
const ( DBId Id = "db/id" DBShortId Id = "db/short-id" Ident Id = "db/ident" Doc Id = "db/doc" Uniq Id = "db/uniq" Cardinality Id = "db/cardinality" Type Id = "db/type" EnumValues Id = "db/enumValues" EntityElemType Id = "db/elementType" UniqueId Id = "db/unique.identity" UniqueValue Id = "db/unique.value" CardinalityOne Id = "db/cardinality.one" CardinalityMany Id = "db/cardinality.many" TypeAny Id = "db/type.any" TypeRef Id = "db/type.ref" TypeStr Id = "db/type.str" TypeKeyword Id = "db/type.keyword" TypeInt Id = "db/type.int" TypeFloat Id = "db/type.float" TypeBool Id = "db/type.bool" TypeTime Id = "db/type.time" TypeEnum Id = "db/type.enum" TypeArray Id = "db/type.array" TypeDuration Id = "db/type.duration" TypeComponent Id = "db/type.component" TypeLabel Id = "db/type.label" TypeBytes Id = "db/type.bytes" Index Id = "db/index" Session Id = "db/session" Tag Id = "db/tag" AttrSession Id = "db/attr.session" EntityAttrs Id = "db/entity.attrs" EntityPreds Id = "db/entity.preds" Ensure Id = "db/ensure" AttrPred Id = "db/attr.pred" Program Id = "db/program" EntityKind Id = "entity/kind" EntitySchema Id = "entity/schema" SchemaKind Id = "schema/kind" PredIP Id = "db/pred.ip" PredCIDR Id = "db/pred.cidr" Schema Id = "db/schema" TTL Id = "db/entity.ttl" Revision Id = "db/entity.revision" CreatedAt Id = "db/entity.created" UpdatedAt Id = "db/entity.updated" )
type IndexPage ¶ added in v0.14.0
type IndexPage struct {
Ids []Id
// Cursor resumes the listing after the last entry in this page, and is
// empty once the index is exhausted. Treat it as opaque: EtcdStore encodes
// a store key in it, because the index is ordered by its own keys and
// making an entity-id cursor work would mean sorting the whole index on
// every page, while MockStore encodes an id. A cursor is only ever handed
// back to the store that issued it.
Cursor string
// Total counts the entries in the index and is filled only when the caller
// starts from the head, since that is the only point a total is meaningful.
// It counts index entries, which equals the entity count unless an entity
// somehow holds two entries in the same index.
Total int64
// Revision is the store revision the page was read at.
Revision int64
}
IndexPage is one bounded page of an index listing.
type Keywordable ¶
type Meta ¶
type Meta struct {
*Entity `json:"entity" cbor:"entity"`
Revision int64 `json:"version" cbor:"version"`
Previous int64 `json:"previous" cbor:"previous"`
}
func MigrateMetaFromBytes ¶
MigrateMetaFromBytes attempts to decode CBOR bytes as Meta, automatically migrating from old format if needed. This is used for read-time migration of persisted entity files.
func (*Meta) GetRevision ¶
func (*Meta) MarshalCBOR ¶
func (*Meta) MarshalJSON ¶
func (*Meta) UnmarshalCBOR ¶
func (*Meta) UnmarshalJSON ¶
type MigrateOptions ¶
type MigrateOptions struct {
// DryRun prevents writing changes back to etcd
DryRun bool
// Prefix is the etcd key prefix to scan (default: "/entity/")
Prefix string
}
MigrateOptions configures the migration behavior
type MigrateShortIdOptions ¶ added in v0.7.0
MigrateShortIdOptions configures the short-id migration behavior.
type MockStore ¶
type MockStore struct {
Entities map[Id]*Entity
OnWatchIndex func(ctx context.Context, attr Attr) (clientv3.WatchChan, error)
GetEntitiesFunc func(ctx context.Context, ids []Id) ([]*Entity, error)
OnListIndex func(ctx context.Context, attr Attr) ([]Id, error) // Hook to track ListIndex calls
NowFunc func() time.Time // Optional function to override current time
// WatchFromRevs records the fromRev argument of every WatchIndex call, in
// order, so tests can assert resume behavior.
WatchFromRevs []int64
// contains filtered or unexported fields
}
func NewMockStore ¶
func NewMockStore() *MockStore
func (*MockStore) AddEntity ¶
AddEntity is a thread-safe helper to directly add an entity to the mock store. It is fixture setup rather than a write: no revision is assigned and no watch event is produced. A fixture that carries its own revision moves the store head up to it so a watch resumed from that head starts past the fixture.
func (*MockStore) AddStaleIndexEntry ¶ added in v0.15.0
AddStaleIndexEntry makes ListIndex report id under attr even though the stored entity does not carry that value. MockStore derives its index from live attributes and so cannot drift on its own; this is how a test reaches the case where EtcdStore's separate collection keyspace disagrees with the entity.
func (*MockStore) CreateEntity ¶
func (*MockStore) CreateSession ¶
func (*MockStore) EnsureEntity ¶
func (*MockStore) GetAttributeSchema ¶
func (*MockStore) GetEntities ¶
func (*MockStore) GetEntityAtRevision ¶ added in v0.4.0
func (*MockStore) ListCollection ¶
func (*MockStore) ListIndexEntitiesPage ¶ added in v0.15.0
func (m *MockStore) ListIndexEntitiesPage( ctx context.Context, attr Attr, cursor string, limit int64, ) (*EntityPage, error)
ListIndexEntitiesPage composes the mock's own paging and batch read.
It reports nothing undecodable and ignores the revision when resolving entities: the mock keeps one version of each entity, so there is no history to read at and nothing stored that could fail to decode. It cannot prove a caller pinned the right revision, which is what the EtcdStore conformance case is for. What it can still check is that a caller handles nils, indexes the map without a nil check, and walks the cursor to the end.
func (*MockStore) ListIndexPage ¶ added in v0.14.0
func (m *MockStore) ListIndexPage(ctx context.Context, attr Attr, cursor string, limit int64) (*IndexPage, error)
ListIndexPage pages the mock's index by sorting the ids and slicing. The real store gets its ordering from etcd's keyspace; the mock has no keyspace, so it imposes id order and its cursor is an entity id rather than a store key. Cursors are opaque and never cross between backends, so the difference is invisible to callers, but it is why IndexPage.Cursor promises only that a cursor is opaque and resumable.
func (*MockStore) ListIndexPageAtRevision ¶ added in v0.15.0
func (*MockStore) ListIndexRevision ¶ added in v0.9.1
ListIndexRevision returns the matching ids along with the store revision they were read at. A WatchIndex resumed from one past that revision sees exactly the writes that landed after this list, the way etcd's header revision pairs with WithRev. The ids and the revision are read under one lock so no write can land between them.
With OnListIndex installed, the revision is read before the hook runs, so a hook that blocks to model a slow List reports the revision the List started at rather than one that includes writes landing while it was held open.
func (*MockStore) ListSessionEntities ¶
ListSessionEntities
func (*MockStore) PatchEntity ¶
func (*MockStore) PingSession ¶
PingSession
func (*MockStore) RemoveEntity ¶
RemoveEntity is a thread-safe helper to directly remove an entity from the mock store
func (*MockStore) ReplaceEntity ¶
func (*MockStore) RevokeSession ¶
RevokeSession
func (*MockStore) SourceEpoch ¶ added in v0.15.0
func (*MockStore) UpdateEntity ¶
func (*MockStore) WaitForEntityWatcher ¶ added in v0.4.0
WaitForEntityWatcher blocks until at least one watcher is registered for the given entity ID, or the context is cancelled.
func (*MockStore) WaitForIndexWatcher ¶ added in v0.3.0
WaitForIndexWatcher blocks until at least one watcher is registered for the given attribute, or the context is cancelled. This is useful in tests to ensure a watch is established before performing operations that should trigger watch notifications.
func (*MockStore) WatchEntity ¶
WatchEntity registers a watcher for an entity and returns a channel that receives updates
func (*MockStore) WatchFromRevsCopy ¶ added in v0.9.1
WatchFromRevsCopy returns a snapshot of the fromRev arguments seen by WatchIndex, for tests asserting resume behavior.
func (*MockStore) WatchIndex ¶
func (m *MockStore) WatchIndex(ctx context.Context, attr Attr, fromRev int64) (clientv3.WatchChan, error)
WatchIndex registers a watcher for the index. With fromRev > 0 it first replays every event committed at or after that revision, the way etcd's WithRev does, so a write landing between a caller's List and its WatchIndex is resumed rather than lost. Consumers built on indexwatch.Watcher depend on that for gap-free delivery. With fromRev == 0 it delivers changes from this moment on only.
Replay and registration happen under one lock, so a write can never fall between them. The mock never compacts, so any fromRev is resumable.
type OldEntity ¶
type OldEntity struct {
ID types.Id `cbor:"id"`
Revision int64 `cbor:"revision,omitempty"`
CreatedAt int64 `cbor:"created_at"` // milliseconds since epoch
UpdatedAt int64 `cbor:"updated_at"` // milliseconds since epoch
Attrs []Attr `cbor:"attrs"`
}
OldEntity represents the entity format before the migration to attribute-based metadata. In the old format, ID, Revision, CreatedAt, and UpdatedAt were struct fields in the CBOR encoding.
type OldMeta ¶
type OldMeta struct {
Entity OldEntity `cbor:"entity"`
Revision int64 `cbor:"version"`
Previous int64 `cbor:"previous"`
}
OldMeta represents the Meta format before the entity migration where the embedded Entity might be in OldEntity format
type ReindexOptions ¶ added in v0.4.0
type ReindexOptions struct {
DryRun bool
CleanupStale bool
// StartKey resumes the entity scan at a cursor from an earlier pass's
// ReindexStats.NextCursor. Zero scans from the beginning.
StartKey string
// MaxEntities caps how many entities a single pass processes before it
// stops and reports a resume cursor, so a large store is reindexed across
// several bounded passes rather than one unbounded run. Zero means
// unbounded (the manual `miren debug reindex` big hammer).
MaxEntities int
// BatchPause is slept after every reindexBatchSize entities to rate-limit
// write pressure. Zero disables pacing.
BatchPause time.Duration
}
ReindexOptions controls the behavior of a reindex operation.
type ReindexState ¶ added in v0.13.0
type ReindexState struct {
// TargetHash is the index schema hash this reindex is working toward. If it
// no longer matches the running schema the work in flight is stale, and the
// scan must restart from the beginning against the new target.
TargetHash string `json:"target_hash"`
// Cursor is the entity key to resume the scan at.
Cursor string `json:"cursor"`
// EntitiesProcessed counts entities covered across every pass so far. It is
// operator visibility only; nothing keys off it.
EntitiesProcessed int64 `json:"entities_processed"`
}
ReindexState is the persisted progress of an in-flight schema reindex, so a reindex too large to finish in one pass resumes where it stopped instead of restarting from the beginning on every coordinator boot.
type ReindexStats ¶ added in v0.4.0
type ReindexStats struct {
EntitiesProcessed int64
IndexesRebuilt int64
CollectionEntriesScanned int64
StaleEntriesFound int64
StaleEntriesRemoved int64
// Complete reports whether the pass reached the end of the entity
// keyspace. Only a complete pass has observed every entity, so only a
// complete pass may be used to conclude the store matches the current
// index schema and stamp the new hash.
//
// Complete is about coverage, not success: check EntitiesFailed too. A
// pass can reach the end of the keyspace while individual entities failed
// to index, and treating that as consistent would strand them.
Complete bool
// EntitiesFailed counts entities the pass could not index, either because
// the entity could not be read or because a collection write was rejected.
// Failures are logged and skipped rather than aborting the scan, so this
// is the only signal that a pass which reached the end of the keyspace did
// not actually finish the job.
EntitiesFailed int64
// NextCursor is the key to resume at, set whenever the pass stopped early
// (budget exhausted, deadline, or shutdown). It is populated even when
// Reindex returns an error, so a caller that is interrupted can still
// persist the progress it made.
NextCursor string
}
ReindexStats holds statistics about a reindex operation.
type SchemaCache ¶
type SchemaCache struct {
// contains filtered or unexported fields
}
func NewSchemaCache ¶
func NewSchemaCache(store Store) (*SchemaCache, error)
func (*SchemaCache) GetKindSchema ¶
func (f *SchemaCache) GetKindSchema(ctx context.Context, id Id) (*EncodedSchema, error)
func (*SchemaCache) GetSchema ¶
func (f *SchemaCache) GetSchema(ctx context.Context, schemaId Id) (*EncodedDomain, error)
type SchemaField ¶
type SchemaField struct {
Name string `json:"name" cbor:"name"`
Type string `json:"type" cbor:"type"`
Id Id `json:"id" cbor:"id"`
Many bool `json:"many,omitempty" cbor:"many,omitempty"`
EnumValues map[string]Id `json:"enum_values,omitempty" cbor:"enum_values,omitempty"`
Component *EncodedSchema `json:"component,omitempty" cbor:"component,omitempty"`
}
type Store ¶
type Store interface {
// SourceEpoch identifies the revision history served by this store. Revisions
// are comparable only while this value remains unchanged.
SourceEpoch(ctx context.Context) (string, error)
GetEntity(ctx context.Context, id Id) (*Entity, error)
GetEntityAtRevision(ctx context.Context, id Id, rev int64) (*Entity, error)
GetEntities(ctx context.Context, ids []Id) ([]*Entity, error)
WatchEntity(ctx context.Context, id Id) (chan EntityOp, error)
GetAttributeSchema(ctx context.Context, name Id) (*AttributeSchema, error)
CreateEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
UpdateEntity(ctx context.Context, id Id, entity *Entity, opts ...EntityOption) (*Entity, error)
ReplaceEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
PatchEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, error)
EnsureEntity(ctx context.Context, entity *Entity, opts ...EntityOption) (*Entity, bool, error)
DeleteEntity(ctx context.Context, id Id) error
WatchIndex(ctx context.Context, attr Attr, fromRev int64) (clientv3.WatchChan, error)
ListIndex(ctx context.Context, attr Attr) ([]Id, error)
// ListIndexRevision is like ListIndex but also returns the etcd revision the
// listing was read at, so a caller can resume a watch from that point.
ListIndexRevision(ctx context.Context, attr Attr) ([]Id, int64, error)
// ListIndexPage reads one bounded page of an index. Unlike ListIndex it does
// not materialize the whole index, so paging through a large one costs
// O(page) per call rather than O(index).
ListIndexPage(ctx context.Context, attr Attr, cursor string, limit int64) (*IndexPage, error)
// ListIndexPageAtRevision reads a bounded page at revision. Passing zero on
// the first call establishes the revision reported by the returned page;
// passing that revision to every continuation produces one consistent scan.
ListIndexPageAtRevision(ctx context.Context, attr Attr, cursor string, limit, revision int64) (*IndexPage, error)
// ListIndexEntitiesPage reads one bounded page of an index together with the
// entities it names, both at a single store revision.
//
// This is the operation callers actually want, and it exists because
// assembling it by hand is easy to get subtly wrong. Listing an index and
// fetching what it named are two reads, and every caller that paired them
// itself paired them unpinned, so an entity written between the two came
// back carrying a value the index had not caught up to. That is
// indistinguishable from a stale index entry, which is a real defect this
// tree has had, so the difference is worth removing rather than documenting.
ListIndexEntitiesPage(ctx context.Context, attr Attr, cursor string, limit int64) (*EntityPage, error)
ListCollection(ctx context.Context, collection string) ([]Id, error)
CreateSession(ctx context.Context, ttl int64) ([]byte, error)
RevokeSession(ctx context.Context, session []byte) error
PingSession(ctx context.Context, session []byte) error
ListSessionEntities(ctx context.Context, session []byte) ([]Id, error)
}
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator provides methods to validate attribute values against their schemas
func NewValidator ¶
func NewValidator(store EntityStore) *Validator
NewValidator creates a new attribute validator
func (*Validator) ValidateAttribute ¶
ValidateAttribute validates a single attribute against its schema
func (*Validator) ValidateAttributes ¶
func (*Validator) ValidateEntity ¶
ValidateEntity validates all attributes in an entity against their schemas
func (*Validator) ValidateUpdate ¶ added in v0.12.0
ValidateUpdate validates the merged attributes of an update or patch. It behaves like ValidateAttributes but skips the reference-existence check for any reference attribute whose value is unchanged from the original entity.
A patch is only responsible for the references it sets. A reference that was already present and valid, whose target has since been deleted out from under the entity, must not block an unrelated change such as a status-only patch. Otherwise an entity holding a now-dangling reference becomes impossible to patch at all, and any controller reconciling it spins forever. See MIR-1320.
type Value ¶
type Value struct {
// contains filtered or unexported fields
}
func AnyValue ¶
AnyValue returns a Value for the supplied value.
If the supplied value is of type Value, it is returned unmodified.
Given a value of one of Go's predeclared string, bool, or (non-complex) numeric types, AnyValue returns a Value of kind KindString, KindBool, KindUint64, KindInt64, or KindFloat64. The width of the original numeric type is not preserved.
Given a time.Time or time.Duration value, AnyValue returns a Value of kind KindTime or KindDuration. The monotonic time is not preserved.
For nil, or values of all other types, including named types whose underlying type is numeric, AnyValue returns a value of kind KindAny.
func ArrayValue ¶
func BytesValue ¶
func ComponentValue ¶
func DurationValue ¶
DurationValue returns a Value for a time.Duration.
func Float64Value ¶
Float64Value returns a Value for a floating-point number.
func KeywordValue ¶
func KeywordValue[T Keywordable](v T) Value
func LabelValue ¶
func StringValue ¶
StringValue returns a new Value for a string.
func (Value) Clone ¶
Clone returns a deep copy of the Value. For bytes, arrays, and components, this creates new copies of the underlying data. For other types (strings, primitives, immutable types), a shallow copy is sufficient.
func (Value) Component ¶
func (v Value) Component() *EntityComponent
func (Value) Duration ¶
Duration returns v's value as a time.Duration. It panics if v is not a time.Duration.
func (*Value) MarshalCBOR ¶
func (Value) MarshalJSON ¶
func (Value) String ¶
String returns Value's value as a string, formatted like fmt.Sprint. Unlike the methods Int64, Float64, and so on, which panic if v is of the wrong kind, String never panics.
func (Value) Uint64 ¶
Uint64 returns v's value as a uint64. It panics if v is not an unsigned integer.
func (*Value) UnmarshalCBOR ¶
func (*Value) UnmarshalJSON ¶
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
schemagen
command
|
|
|
Package export defines the generated contract for entities that may leave the runtime.
|
Package export defines the generated contract for entities that may leave the runtime. |
|
Package indexwatch provides a robust, self-healing abstraction around the entity server's WatchIndex API.
|
Package indexwatch provides a robust, self-healing abstraction around the entity server's WatchIndex API. |