cart: back projection cache with platform/catalog.ProjectionStore
Build and Publish / BuildAndDeployArm64 (push) Failing after 5s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 3s

Consumer side of the catalog-projection seam (C1):
- catalogProjectionCache now wraps ProjectionStore[catalog.Projection] (identity
  transform — the cart's overlay uses ~all fields); delivery handler routes bus
  frames via HandleFrame (snapshot begin|chunk|end + epoch-stamped deltas).
- Per-pod cold-start-ready .bin under CART_PROJECTION_DIR (pod-local, never
  shared) + IsDeleted tombstone passthrough; HTTP fetch demoted to last-resort.
- Tests: test-only Apply shim + mustCache helper keep existing cases green;
  new snapshot-framing integration test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 21:05:55 +02:00
co-authored by Claude Opus 4.8
parent a3eab70de8
commit d711348863
5 changed files with 177 additions and 93 deletions
+39
View File
@@ -0,0 +1,39 @@
package main
import (
"testing"
"git.k6n.net/mats/platform/catalog"
)
// mustCache opens a per-pod projection cache backed by a temp dir (pod-local).
func mustCache(t *testing.T) *catalogProjectionCache {
t.Helper()
c, err := newCatalogProjectionCache(t.TempDir())
if err != nil {
t.Fatalf("newCatalogProjectionCache: %v", err)
}
t.Cleanup(func() { _ = c.store.Close() })
return c
}
// Apply is a TEST-ONLY convenience that routes a delta batch through the store at
// the initial epoch (0), reproducing the pre-ProjectionStore cache API so the
// existing cart tests keep exercising Get/IsDeleted/Len. Production code feeds the
// store via HandleFrame with real bus framing (snapshot + epoch-stamped deltas).
func (c *catalogProjectionCache) Apply(updates []catalog.ProjectionUpdate) (upserts, deletes int) {
for _, u := range updates {
if u.Deleted {
deletes++
} else if u.SKU != "" {
upserts++
}
}
payload, err := catalog.EncodeUpdates(updates)
if err != nil {
return 0, 0
}
// No MetaSnapshotPhase ⇒ delta; epoch 0 matches a fresh store's base epoch.
_ = c.store.HandleFrame(map[string]string{catalog.MetaEpoch: "0"}, payload)
return upserts, deletes
}