You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
runtime: TS-config boot registers a 'metadata' service without attachClusterPubSub — cross-node invalidation disabled; new object gives OBJECT_NOT_FOUND on non-writing replicas, never heals #13331
On a 3-replica objectos-ee cluster (framework pin 502ff8b5, redis cluster driver active — see #13330 for getting it active at all), every replica logs at boot:
WARN MetadataClusterBridgePlugin: metadata service does not expose attachClusterPubSub(); cross-node cache invalidation disabled
Measured consequence — create a new object through the load balancer, then immediately write data to it through the LB:
PUT /api/v1/meta/object/gadget → Saved (seq=3)
12 × POST /api/v1/data/gadget via LB → 3 ok, 9 OBJECT_NOT_FOUND
60s later, 9 more POSTs → 3 ok, 6 OBJECT_NOT_FOUND (no self-heal)
Only the replica that handled the metadata write registers the object with its ObjectQL engine; the other two keep answering OBJECT_NOT_FOUNDindefinitely (no TTL/poll fallback on this path). Restarting the replicas is the only recovery. GET /api/v1/meta/* list/read stays fresh everywhere (store-backed reads), which makes the drift worse to spot: the metadata API says the object exists while the data API on two of three nodes refuses it.
Related earlier fix #5109 addressed peer-side listCache/registry invalidation once the broadcast arrives — here the broadcast never starts, because the bridge never attaches.
Root cause direction
MetadataClusterBridgePlugin (service-cluster) resolves ctx.getService('metadata') at kernel:ready and requires attachClusterPubSub on it (metadata-cluster-bridge-plugin.ts:53-59). NodeMetadataManager extends MetadataManagerdoes carry that method (metadata-manager.ts:2969), and @objectstack/metadata's MetadataPlugin registers exactly that manager (plugin.ts:303).
But on this boot (objectos-ee, objectstack.config.ts via os start→serve), the kernel's plugin lineup contains no MetadataPlugin at all (boot banner lists ObjectQL, PlatformObjects, Dispatcher, … — no MetadataPlugin, and plugin.ts:304's registration log line never prints). Whatever registers the 'metadata' service on the TS-config path (runtime dispatcher/domain facade) does not expose attachClusterPubSub, so the bridge warns and bails — exactly the "protocol omission → stale caches" case content/docs/kernel/cluster.mdx §1 predicts. serve.ts:2296+ composes an artifact-sourced MetadataPlugin only on artifact boots.
Second-order effect, same neighborhood: a record_change flow saved at runtime (PUT /api/v1/meta/flow/…) is registered ([Registry] Registered flow: …) but its trigger only arms after a replica restart, and then only on that replica's local hooks — runtime-authored automation is effectively single-node too.
Loop POST /api/v1/data/<new> through the LB — two thirds fail OBJECT_NOT_FOUND, and keep failing.
Expected
Whatever object the TS-config boot registers as the 'metadata' service either exposes attachClusterPubSub() (delegating to the real manager underneath), or the bridge knows how to reach the manager behind the facade — so metadata.changed fans out and #5109's peer-side invalidation actually runs. Until then, the EE multi-node compose (ADR-0018) is safe only for deployments whose metadata is fixed at deploy time and whose replicas restart together after any schema change.
Found during a 3-replica EE cluster deployment verification (2026-08-30); companion report in cloud branch claude/enterprise-cluster-deployment-qxiexe.
Symptom
On a 3-replica objectos-ee cluster (framework pin
502ff8b5, redis cluster driver active — see #13330 for getting it active at all), every replica logs at boot:Measured consequence — create a new object through the load balancer, then immediately write data to it through the LB:
Only the replica that handled the metadata write registers the object with its ObjectQL engine; the other two keep answering
OBJECT_NOT_FOUNDindefinitely (no TTL/poll fallback on this path). Restarting the replicas is the only recovery.GET /api/v1/meta/*list/read stays fresh everywhere (store-backed reads), which makes the drift worse to spot: the metadata API says the object exists while the data API on two of three nodes refuses it.Related earlier fix #5109 addressed peer-side
listCache/registry invalidation once the broadcast arrives — here the broadcast never starts, because the bridge never attaches.Root cause direction
MetadataClusterBridgePlugin(service-cluster) resolvesctx.getService('metadata')atkernel:readyand requiresattachClusterPubSubon it (metadata-cluster-bridge-plugin.ts:53-59).NodeMetadataManager extends MetadataManagerdoes carry that method (metadata-manager.ts:2969), and@objectstack/metadata'sMetadataPluginregisters exactly that manager (plugin.ts:303).But on this boot (objectos-ee,
objectstack.config.tsviaos start→serve), the kernel's plugin lineup contains no MetadataPlugin at all (boot banner lists ObjectQL, PlatformObjects, Dispatcher, … — no MetadataPlugin, and plugin.ts:304's registration log line never prints). Whatever registers the'metadata'service on the TS-config path (runtime dispatcher/domain facade) does not exposeattachClusterPubSub, so the bridge warns and bails — exactly the "protocol omission → stale caches" casecontent/docs/kernel/cluster.mdx§1 predicts. serve.ts:2296+ composes an artifact-sourced MetadataPlugin only on artifact boots.Second-order effect, same neighborhood: a
record_changeflow saved at runtime (PUT /api/v1/meta/flow/…) is registered ([Registry] Registered flow: …) but its trigger only arms after a replica restart, and then only on that replica's local hooks — runtime-authored automation is effectively single-node too.Repro
cloud/apps/objectos-ee/docker-compose.ymlat 3 replicas with the redis driver active (needs the cli: serve's cluster-driver load registers into the CJS registry while the ESM Runtime reads the ESM one — OS_CLUSTER_DRIVER=redis silently downgrades to "not registered" (post-#10645) #13330 workaround).PUT /api/v1/meta/object/<new>through the LB.POST /api/v1/data/<new>through the LB — two thirds failOBJECT_NOT_FOUND, and keep failing.Expected
Whatever object the TS-config boot registers as the
'metadata'service either exposesattachClusterPubSub()(delegating to the real manager underneath), or the bridge knows how to reach the manager behind the facade — sometadata.changedfans out and #5109's peer-side invalidation actually runs. Until then, the EE multi-node compose (ADR-0018) is safe only for deployments whose metadata is fixed at deploy time and whose replicas restart together after any schema change.Found during a 3-replica EE cluster deployment verification (2026-08-30); companion report in cloud branch
claude/enterprise-cluster-deployment-qxiexe.