MongoSnapshotStore

class MongoSnapshotStore(client: MongoClient, databaseName: String, context: String, dispatcher: CoroutineDispatcher = VirtualThreads.dispatcher, ddl: Ddl = Ddl.AUTO, sharding: MongoSharding = MongoSharding.AUTO, operationTimeout: Duration = MongoTimeouts.OPERATION) : SnapshotStore, SnapshotRebuildStore

The SnapshotStore of one bounded context on MongoDB (documentation/designs/03-log-and-snapshot.md §3).

Each aggregate type has its own collection, wow_snapshot__<context>__<aggregate>, holding one document per aggregate, whose _id is its key {t: tenant, a: aggregate id}: no index besides _id_ keeps it unique, and the key's order is the tenant's, then the aggregate id's. The document also holds the two as tenant_id and aggregate_id, which the queries read, and every write's filter names them, the shard key of a sharded collection; it has no aggregate name, the collection is the aggregate type. An aggregate type's collection is created, with its key and format record, the first time this store touches the type, or up front by createCollection. Two partial indexes cover the predicates the query side adds to every query: {tenant_id, owner_id} and {tenant_id, space_id}, over the snapshots that are not deleted. The state and the access attributes are BSON documents, so the snapshot can be queried in place as the default read model; the state's decimals are Decimal128, exact (ExactBson). A save is one conditional upsert whose filter carries the version and state revision guard (version < new, the stored state_revision not above the new one): no transaction, and concurrent saves leave the highest version (see conditionalUpsert). A save refused for a higher state revision then marks the document stale (stale_version). The claimed state revision of each collection is a document of TABLES, keyed by the collection.

Calls run on dispatcher, virtual threads by default. A failure of the database is a defect and is thrown: a snapshot is a cache, so the caller may drop it and rebuild later.

Constructors

Link copied to clipboard
constructor(client: MongoClient, databaseName: String, context: String, dispatcher: CoroutineDispatcher = VirtualThreads.dispatcher, ddl: Ddl = Ddl.AUTO, sharding: MongoSharding = MongoSharding.AUTO, operationTimeout: Duration = MongoTimeouts.OPERATION)

Properties

Link copied to clipboard
open override val rebuilds: SnapshotRebuildStore

This store rewrites its snapshots in place: it is its own rebuild control plane.

Functions

Link copied to clipboard
open suspend override fun claim(type: AggregateType, revision: Int, lower: Boolean): SnapshotTable
Link copied to clipboard
fun createCollection(aggregateName: String)

Creates the key index of aggregateName's collection when missing, once per store; refuses snapshots recorded in another format version (MongoFormat).

Link copied to clipboard

Nothing to create up front: each aggregate type's collection is created on its first use (createCollection), since the store does not know the context's aggregate types. Kept for the host, which calls it at startup.

Link copied to clipboard
open suspend override fun load(ref: AggregateRef): SnapshotRecord?
Link copied to clipboard
open suspend override fun mark(type: AggregateType, revision: Int, rebuilding: Boolean?, backfilled: Boolean?): SnapshotTable?
Link copied to clipboard
open suspend override fun markStale(ref: AggregateRef): Boolean
Link copied to clipboard
open suspend override fun pending(type: AggregateType, revision: Int, after: AggregateRef?, limit: Int): List<AggregateRef>
Link copied to clipboard
open suspend override fun rewrite(snapshot: SnapshotRecord): Boolean
Link copied to clipboard
open suspend override fun save(snapshot: SnapshotRecord): Boolean
Link copied to clipboard
open suspend override fun saveAll(snapshots: List<SnapshotRecord>): List<SnapshotSaveOutcome>

Saves snapshots: in the rounds SnapshotSaveOutcome.rounds gives, each round's snapshots of one aggregate type with one unordered bulkWrite of conditional upserts, each guarded as save guards one. Each update is a pipeline that clears a stale mark of a version up to its own in the same write, so a batch that all lands is one round trip whatever marks it meets. A snapshot whose upsert met the key (the guard refused it, or a concurrent insert won the key) is saved again alone, as save would (conditionalUpsert), which also marks a higher state revision stale; one the server refused with another error, or whose state cannot be BSON, is SnapshotSaveOutcome.Failed alone.

Link copied to clipboard
open suspend override fun table(type: AggregateType): SnapshotTable?