Metadata Passthrough
ConvergeDB supports attaching opaque metadata to ASSERT, PATCH, and RETRACT operations. Metadata describes the cause of a mutation (for example, which user triggered it or which reducer produced it), not the result.
Key properties
Section titled “Key properties”- Transient. Metadata is not stored in the entity table or WAL. It lives only in the accumulator during the coalescing window and is forwarded to subscribers in the resulting notification.
- Last-write-wins. If multiple writes to the same entity within a coalescing window carry metadata, only the metadata from the last write is forwarded.
- Not in queries. Metadata is absent from QUERY responses, bootstrap snapshots, and scan results. It is only available in live subscriber notifications.
- Size limit. Maximum 65,535 bytes per operation (u16 length prefix). See Limits.
Sending metadata
Section titled “Sending metadata”Attach metadata to any write operation. The EntityMetadata constructor accepts key-value tuples for concise inline usage:
var metadata = new EntityMetadata(("ci", "player-123"), ("rd", "FillOrder"));
await players.AssertAsync(player, metadata: metadata);await players.PatchAsync(patch, metadata: metadata);await players.RetractAsync(entityId, metadata: metadata);// Coming soonFor dynamic metadata or binary values, use MetadataBuilder:
var metadata = new MetadataBuilder() .Set("ci", callerIdentityBytes) .Set("rd", "UpdatePlayerPosition") .Build();// Coming soonReceiving metadata in subscribers
Section titled “Receiving metadata in subscribers”Metadata is available on notifications and events via the Metadata property. Parse it with MetadataReader to extract key-value pairs:
await foreach (var change in players.SubscribeAsync(bootstrap: false)){ if (change.Metadata.Length > 0) { var meta = MetadataReader.Parse(change.Metadata); string? caller = meta.GetString("ci"); string? reducer = meta.GetString("rd");
// Use metadata for routing, filtering, or audit logging. if (reducer == "TradeExecuted") await RecordTradeAsync(change, caller); }}// Coming soonMetadata works the same way on event stream notifications (EntityEvent<T>.Metadata).
Wire format
Section titled “Wire format”Metadata is encoded as a trailing metadata_len(u16) + metadata(bytes) on ASSERT, PATCH, and RETRACT frames. Old clients that do not send metadata simply omit the trailing bytes. Old subscribers that do not understand metadata ignore the trailing bytes in STATE notifications. This makes metadata backward-compatible in both directions.
When to use metadata
Section titled “When to use metadata”Metadata is useful when subscribers need to know why an entity changed, not just what changed:
- Audit trails. Attach the caller identity and the operation that triggered the mutation.
- Reducer attribution. In game server architectures, attach the reducer name so subscribers can distinguish between different game logic paths that produced the same entity state.
- Correlation IDs. Attach a request ID for distributed tracing across services.
If you only need to know what changed, use ChangedFields instead.