From 2afbfaae3078cdb48a0bb7b3376be5fa994f4bb5 Mon Sep 17 00:00:00 2001 From: idrojone Date: Sun, 26 Apr 2026 21:35:02 +0200 Subject: [PATCH 1/2] fix(cloud): invalidate dashboard read model cache on mutation insert This ensures the dashboard reflects new memories arriving via autosync push without requiring a server restart. --- .gitignore | 3 +++ docker-compose.cloud.yml | 29 +++++++++++++------------ internal/cloud/cloudstore/cloudstore.go | 1 + 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 31f14ed2..40f8e072 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ Thumbs.db engram-dev .atl/ .release-notes-beta.md + +# ENV +.env \ No newline at end of file diff --git a/docker-compose.cloud.yml b/docker-compose.cloud.yml index 5db97918..1626d8ce 100644 --- a/docker-compose.cloud.yml +++ b/docker-compose.cloud.yml @@ -1,15 +1,15 @@ services: postgres: - image: postgres:16-alpine + image: ${POSTGRES_IMAGE} container_name: engram-cloud-postgres environment: - POSTGRES_USER: engram - POSTGRES_PASSWORD: engram_dev - POSTGRES_DB: engram_cloud + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} ports: - - "127.0.0.1:5433:5432" + - "0.0.0.0:5433:5432" healthcheck: - test: ["CMD-SHELL", "pg_isready -U engram -d engram_cloud"] + test: [ "CMD-SHELL", "pg_isready -U engram -d engram_cloud" ] interval: 5s timeout: 3s retries: 10 @@ -25,15 +25,16 @@ services: postgres: condition: service_healthy environment: - ENGRAM_DATABASE_URL: postgres://engram:engram_dev@postgres:5432/engram_cloud?sslmode=disable - ENGRAM_JWT_SECRET: engram-dev-jwt-secret-for-local-smoke-1234 - ENGRAM_CLOUD_INSECURE_NO_AUTH: "1" - ENGRAM_CLOUD_ALLOWED_PROJECTS: smoke-project - ENGRAM_CLOUD_HOST: 0.0.0.0 - ENGRAM_PORT: "18080" + ENGRAM_DATABASE_URL: ${ENGRAM_DATABASE_URL} + ENGRAM_JWT_SECRET: ${ENGRAM_JWT_SECRET} + ENGRAM_CLOUD_ALLOWED_PROJECTS: ${ENGRAM_CLOUD_ALLOWED_PROJECTS} + ENGRAM_CLOUD_HOST: ${ENGRAM_CLOUD_HOST} + ENGRAM_PORT: ${ENGRAM_PORT} + ENGRAM_CLOUD_ADMIN: ${ENGRAM_CLOUD_ADMIN} + ENGRAM_CLOUD_TOKEN: ${ENGRAM_CLOUD_TOKEN} ports: - - "127.0.0.1:18080:18080" - command: ["cloud", "serve"] + - "0.0.0.0:${ENGRAM_PORT}:${ENGRAM_PORT}" + command: [ "cloud", "serve" ] volumes: engram-cloud-pg: diff --git a/internal/cloud/cloudstore/cloudstore.go b/internal/cloud/cloudstore/cloudstore.go index 5a495e10..32648948 100644 --- a/internal/cloud/cloudstore/cloudstore.go +++ b/internal/cloud/cloudstore/cloudstore.go @@ -588,6 +588,7 @@ func (cs *CloudStore) InsertMutationBatch(ctx context.Context, batch []MutationE return nil, fmt.Errorf("cloudstore: commit mutation batch: %w", err) } tx = nil // mark committed so deferred Rollback is a no-op + cs.invalidateDashboardReadModel() return seqs, nil } From b5a3e86fa9832db072a033e5bb50c619a833b758 Mon Sep 17 00:00:00 2001 From: idrojone Date: Wed, 29 Apr 2026 23:03:41 +0200 Subject: [PATCH 2/2] fix(cloudstore): scope cache invalidation fix for #251 --- .gitignore | 3 -- docker-compose.cloud.yml | 29 +++++++++---------- .../cloud/cloudstore/project_controls_test.go | 29 +++++++++++++++++++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 40f8e072..31f14ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,3 @@ Thumbs.db engram-dev .atl/ .release-notes-beta.md - -# ENV -.env \ No newline at end of file diff --git a/docker-compose.cloud.yml b/docker-compose.cloud.yml index 1626d8ce..5db97918 100644 --- a/docker-compose.cloud.yml +++ b/docker-compose.cloud.yml @@ -1,15 +1,15 @@ services: postgres: - image: ${POSTGRES_IMAGE} + image: postgres:16-alpine container_name: engram-cloud-postgres environment: - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: engram + POSTGRES_PASSWORD: engram_dev + POSTGRES_DB: engram_cloud ports: - - "0.0.0.0:5433:5432" + - "127.0.0.1:5433:5432" healthcheck: - test: [ "CMD-SHELL", "pg_isready -U engram -d engram_cloud" ] + test: ["CMD-SHELL", "pg_isready -U engram -d engram_cloud"] interval: 5s timeout: 3s retries: 10 @@ -25,16 +25,15 @@ services: postgres: condition: service_healthy environment: - ENGRAM_DATABASE_URL: ${ENGRAM_DATABASE_URL} - ENGRAM_JWT_SECRET: ${ENGRAM_JWT_SECRET} - ENGRAM_CLOUD_ALLOWED_PROJECTS: ${ENGRAM_CLOUD_ALLOWED_PROJECTS} - ENGRAM_CLOUD_HOST: ${ENGRAM_CLOUD_HOST} - ENGRAM_PORT: ${ENGRAM_PORT} - ENGRAM_CLOUD_ADMIN: ${ENGRAM_CLOUD_ADMIN} - ENGRAM_CLOUD_TOKEN: ${ENGRAM_CLOUD_TOKEN} + ENGRAM_DATABASE_URL: postgres://engram:engram_dev@postgres:5432/engram_cloud?sslmode=disable + ENGRAM_JWT_SECRET: engram-dev-jwt-secret-for-local-smoke-1234 + ENGRAM_CLOUD_INSECURE_NO_AUTH: "1" + ENGRAM_CLOUD_ALLOWED_PROJECTS: smoke-project + ENGRAM_CLOUD_HOST: 0.0.0.0 + ENGRAM_PORT: "18080" ports: - - "0.0.0.0:${ENGRAM_PORT}:${ENGRAM_PORT}" - command: [ "cloud", "serve" ] + - "127.0.0.1:18080:18080" + command: ["cloud", "serve"] volumes: engram-cloud-pg: diff --git a/internal/cloud/cloudstore/project_controls_test.go b/internal/cloud/cloudstore/project_controls_test.go index e9b5ee3d..6a033282 100644 --- a/internal/cloud/cloudstore/project_controls_test.go +++ b/internal/cloud/cloudstore/project_controls_test.go @@ -295,6 +295,35 @@ func TestInsertMutationBatchIsAtomicOnFailure(t *testing.T) { } } +func TestInsertMutationBatchInvalidatesDashboardReadModel(t *testing.T) { + driverName := "cloudstore-partial-fail-driver" + resetPartialFailDriver(10) + + db, err := sql.Open(driverName, "dsn") + if err != nil { + t.Fatalf("open: %v", err) + } + t.Cleanup(func() { _ = db.Close() }) + + cs := &CloudStore{db: db} + cs.dashboardReadModel = dashboardReadModel{ + projects: []DashboardProjectRow{{Project: "proj-a", Chunks: 1}}, + } + cs.dashboardReadModelOK = true + + batch := []MutationEntry{{Project: "proj-a", Entity: "obs", EntityKey: "k1", Op: "upsert", Payload: json.RawMessage(`{}`)}} + if _, err := cs.InsertMutationBatch(context.Background(), batch); err != nil { + t.Fatalf("InsertMutationBatch: %v", err) + } + + if cs.dashboardReadModelOK { + t.Fatal("expected InsertMutationBatch to invalidate dashboard read model cache") + } + if len(cs.dashboardReadModel.projects) != 0 { + t.Fatalf("expected read model cache cleared, got %+v", cs.dashboardReadModel.projects) + } +} + // TestProjectSyncControlListIncludesKnownChunkProjects asserts that // ListProjectSyncControls returns projects that appear in cloud_chunks // even if they have no explicit control row. Satisfies REQ-104.