BoltArchiveFormat

class BoltArchiveFormat<Id : Any, V, Op : Any>(classifier: OpLogCrdt<Id, V, Op>, vSerializer: KSerializer<V>)

How one op-log CRDT's operations are classified and encoded into an archive.

Every Bolt takes one of these. It binds three things a bolt cannot know for itself: which ops are content and which are records of forgetting (classifyOp), which causal dot an id belongs to (dotOf), and how an op turns into bytes (encode / decode).

Why it holds a CRDT instance

OpLogCrdt.classify and dotOf are instance methods, so classification needs a live CRDT in hand. classifier is that hand — a witness, used only for its three contract methods and never read for content. The companion factories construct an empty one, so there is no state to leak even in principle. (If a future replay-side validator wants free-standing classification, OpLogCrdt can grow it source-compatibly; this parameter is why that is worth doing.)

Why the serializer is not a parameter

You pass the element serializer; the op serializer comes from OpLogCrdt.opSerializer(vSerializer) and cannot be overridden. That is deliberate. The compiler-generated sealed serializer for RgaOp/FugueOp writes a different wire format — class-discriminator polymorphism instead of the canonical leading t tag — and defaults its element type to PolymorphicSerializer(Any::class), which CBOR cannot encode at all. Bytes written that way sit outside the golden vectors that pin this format across versions, and an archive exists precisely to be read by a later build. FugueOpSerializer is internal besides, so opSerializer is the only way to canonically encode a FugueOp.

Type Parameters

Id

the element-identity type (RgaId / FugueId).

V

the element type carried by inserts.

Op

the operation type (RgaOp<V> / FugueOp<V>).

Samples

val server = ReplicaId("server-uuid-abc123")

// You pass the ELEMENT serializer. The op serializer comes from the CRDT's own
// `opSerializer` and cannot be overridden — the compiler-generated one for `RgaOp`
// writes a different wire format, and an archive exists to be read by a later build.
val format = BoltArchiveFormat.rga(String.serializer())
val bolt = InMemoryBolt(format, Clock.System)

// Feed it the OPERATIONS a replica applied, never a state fragment. A `Compact` among
// them is dropped and the ops it suppresses are kept — which is what lets this archive
// outlive the replica that fed it.
var live = Rga.empty<String>()
val ops = List(3) { index ->
    val (next, op) = live.insertAt(server, live.size, "record-$index")
    live = next
    op
}
bolt.append(ops)

Constructors

Link copied to clipboard
constructor(classifier: OpLogCrdt<Id, V, Op>, vSerializer: KSerializer<V>)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

The serialName of the element serializer, likewise written into the segment header.

Link copied to clipboard

The serialName of the canonical op serializer — written into every segment header so an archive says what it holds rather than relying on the reader guessing.