100 lines
4.4 KiB
Go
100 lines
4.4 KiB
Go
package catalog
|
|
|
|
// Projection seam — catalog binding over the generic platform/projection
|
|
// package. The value-agnostic machinery (snapshot .bin format, mmap reader,
|
|
// race-free SnapshotHolder, the begin→chunk*→end consumer state machine with
|
|
// epoch gating + delta overlay) lives in platform/projection. This file is the
|
|
// thin catalog-specific binding: it fixes the WIRE type to catalog.Projection
|
|
// and supplies the decoder for the catalog wire schema, so cart / checkout /
|
|
// order / catalog-emit keep their existing catalog.* API unchanged.
|
|
//
|
|
// See docs/catalog-projection.md for the consumer contract and
|
|
// docs/plan-projectionstore-generalization.md for why the split exists.
|
|
|
|
import (
|
|
"time"
|
|
|
|
"git.k6n.net/mats/platform/projection"
|
|
)
|
|
|
|
// Generic machinery, re-exported under their historical catalog names. These
|
|
// are type aliases — catalog.SnapshotHolder[T] IS projection.SnapshotHolder[T],
|
|
// so values flow between the two packages without conversion.
|
|
type (
|
|
SnapshotWriter[T any] = projection.SnapshotWriter[T]
|
|
SnapshotReader[T any] = projection.SnapshotReader[T]
|
|
SnapshotHolder[T any] = projection.SnapshotHolder[T]
|
|
|
|
// ProjectionStore is a consumer's local, cold-start-ready cache of T, fed the
|
|
// catalog.projection_published frames off the bus. The wire type is fixed to
|
|
// catalog.Projection; T is the consumer's projected shape.
|
|
ProjectionStore[T any] = projection.Store[Projection, T]
|
|
|
|
// ProjectionTransform projects the wire Projection down to the consumer's
|
|
// stored type T (the one place a consumer declares which catalog facts it
|
|
// needs). MANDATORY.
|
|
ProjectionTransform[T any] = projection.Transform[Projection, T]
|
|
|
|
// ProjectionFallback resolves a SKU the local store doesn't have yet. May be nil.
|
|
ProjectionFallback = projection.Fallback[Projection]
|
|
)
|
|
|
|
// DefaultDrainGrace is the SnapshotHolder default drain period (re-exported).
|
|
const DefaultDrainGrace = projection.DefaultDrainGrace
|
|
|
|
// Snapshot file errors, re-exported so callers can errors.Is against the
|
|
// catalog.* names. Identity is preserved (these ARE the projection package's
|
|
// error values).
|
|
var (
|
|
ErrSnapshotMagic = projection.ErrSnapshotMagic
|
|
ErrSnapshotVersion = projection.ErrSnapshotVersion
|
|
ErrSnapshotTruncated = projection.ErrSnapshotTruncated
|
|
ErrSnapshotBadBlob = projection.ErrSnapshotBadBlob
|
|
ErrSnapshotOverflow = projection.ErrSnapshotOverflow
|
|
ErrSKUTooLong = projection.ErrSKUTooLong
|
|
ErrTenantTooLong = projection.ErrTenantTooLong
|
|
)
|
|
|
|
// NewSnapshotWriter returns an empty snapshot writer for value type T.
|
|
func NewSnapshotWriter[T any]() *SnapshotWriter[T] { return projection.NewSnapshotWriter[T]() }
|
|
|
|
// NewSnapshotReader wraps an already-loaded []byte as a reader of T.
|
|
func NewSnapshotReader[T any](data []byte) (*SnapshotReader[T], error) {
|
|
return projection.NewSnapshotReader[T](data)
|
|
}
|
|
|
|
// ReadSnapshotFile opens + parses a snapshot file as a reader of T (mmap on Unix).
|
|
func ReadSnapshotFile[T any](path string) (*SnapshotReader[T], error) {
|
|
return projection.ReadSnapshotFile[T](path)
|
|
}
|
|
|
|
// NewSnapshotHolder returns a holder of T with the given drain grace.
|
|
func NewSnapshotHolder[T any](grace time.Duration) *SnapshotHolder[T] {
|
|
return projection.NewSnapshotHolder[T](grace)
|
|
}
|
|
|
|
// NewProjectionStore opens a catalog ProjectionStore backed by dir/name (its own
|
|
// .bin). transform is REQUIRED (declares the stored shape); fallback may be nil.
|
|
// If a base from a previous run is present it is reopened immediately
|
|
// (cold-start ready). Close() when done.
|
|
func NewProjectionStore[T any](dir, name string, transform ProjectionTransform[T], fallback ProjectionFallback) (*ProjectionStore[T], error) {
|
|
return projection.NewStore[Projection, T](dir, name, decodeProjectionUpdates, transform, fallback)
|
|
}
|
|
|
|
// decodeProjectionUpdates bridges the catalog wire schema ([]ProjectionUpdate —
|
|
// a flat Projection plus a `deleted` flag) to the generic projection.Update the
|
|
// Store works with. The index key is the Projection's SKU. This is the one
|
|
// catalog-specific seam the generic Store can't know: the on-the-wire payload
|
|
// shape is a domain decision.
|
|
func decodeProjectionUpdates(payload []byte) ([]projection.Update[Projection], error) {
|
|
raw, err := DecodeUpdates(payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]projection.Update[Projection], len(raw))
|
|
for i, u := range raw {
|
|
out[i] = projection.Update[Projection]{Key: u.SKU, Value: u.Projection, Deleted: u.Deleted}
|
|
}
|
|
return out, nil
|
|
}
|