mirror of
https://github.com/logos-messaging/nim-sds.git
synced 2026-05-18 07:59:54 +00:00
127 lines
5.3 KiB
Nim
127 lines
5.3 KiB
Nim
import ./sds_message_id
|
|
import ./sds_message
|
|
import ./unacknowledged_message
|
|
import ./incoming_message
|
|
import ./repair_entry
|
|
export
|
|
sds_message_id, sds_message, unacknowledged_message, incoming_message, repair_entry
|
|
|
|
## SDS state persistence interface (issue #64).
|
|
##
|
|
## Defines WHAT operations a persistence backend must provide. The actual
|
|
## storage technology (SQLite, encrypted file, in-memory) is supplied by the
|
|
## caller — nim-sds knows nothing about it. Every state-mutating proc in the
|
|
## protocol calls into one of these procs immediately after the in-memory
|
|
## change, so on-disk state stays in lockstep with in-memory state.
|
|
##
|
|
## Bloom filter is intentionally not persisted: it is rebuilt from the local
|
|
## history log on bootstrap. Async timers are likewise recomputed from the
|
|
## absolute timestamps stored in the repair buffer entries.
|
|
|
|
type
|
|
ChannelSnapshot* = object
|
|
## Returned by `loadAllForChannel` on bootstrap. Carries the entire
|
|
## per-channel state needed to repopulate a `ChannelContext`. The bloom
|
|
## filter is NOT in the snapshot — callers rebuild it from `messageHistory`.
|
|
lamportTimestamp*: int64
|
|
messageHistory*: seq[SdsMessage]
|
|
## MUST be ordered oldest-first. FIFO eviction relies on insertion order;
|
|
## skipping ORDER BY corrupts the log across restarts.
|
|
outgoingBuffer*: seq[UnacknowledgedMessage]
|
|
incomingBuffer*: seq[IncomingMessage]
|
|
outgoingRepairBuffer*: seq[(SdsMessageID, OutgoingRepairEntry)]
|
|
incomingRepairBuffer*: seq[(SdsMessageID, IncomingRepairEntry)]
|
|
|
|
Persistence* = object
|
|
## Pluggable persistence contract. The caller supplies an instance of this
|
|
## type at `newReliabilityManager` construction time. Each proc field is
|
|
## invoked by nim-sds at the corresponding state-mutation point.
|
|
|
|
# Per-channel lamport clock
|
|
saveLamport*:
|
|
proc(channelId: SdsChannelID, lamport: int64) {.gcsafe, raises: [].}
|
|
|
|
# Local log (delivered messages)
|
|
appendLogEntry*:
|
|
proc(channelId: SdsChannelID, msg: SdsMessage) {.gcsafe, raises: [].}
|
|
removeLogEntry*:
|
|
proc(channelId: SdsChannelID, msgId: SdsMessageID) {.gcsafe, raises: [].}
|
|
setRetrievalHint*:
|
|
proc(msgId: SdsMessageID, hint: seq[byte]) {.gcsafe, raises: [].}
|
|
|
|
# Outgoing unacknowledged buffer
|
|
saveOutgoing*:
|
|
proc(channelId: SdsChannelID, msg: UnacknowledgedMessage) {.gcsafe, raises: [].}
|
|
removeOutgoing*:
|
|
proc(channelId: SdsChannelID, msgId: SdsMessageID) {.gcsafe, raises: [].}
|
|
|
|
# Incoming dependency-waiting buffer
|
|
saveIncoming*:
|
|
proc(channelId: SdsChannelID, msg: IncomingMessage) {.gcsafe, raises: [].}
|
|
removeIncoming*:
|
|
proc(channelId: SdsChannelID, msgId: SdsMessageID) {.gcsafe, raises: [].}
|
|
|
|
# SDS-R outgoing repair buffer
|
|
saveOutgoingRepair*: proc(
|
|
channelId: SdsChannelID, msgId: SdsMessageID, entry: OutgoingRepairEntry
|
|
) {.gcsafe, raises: [].}
|
|
removeOutgoingRepair*:
|
|
proc(channelId: SdsChannelID, msgId: SdsMessageID) {.gcsafe, raises: [].}
|
|
|
|
# SDS-R incoming repair buffer
|
|
saveIncomingRepair*: proc(
|
|
channelId: SdsChannelID, msgId: SdsMessageID, entry: IncomingRepairEntry
|
|
) {.gcsafe, raises: [].}
|
|
removeIncomingRepair*:
|
|
proc(channelId: SdsChannelID, msgId: SdsMessageID) {.gcsafe, raises: [].}
|
|
|
|
# Wipe all persisted state for a channel in one transactional call.
|
|
# Called by removeChannel / resetReliabilityManager. Backends should
|
|
# implement this atomically (e.g. one BEGIN/COMMIT) — a per-row loop on
|
|
# the nim-sds side would mean N fsyncs per drop.
|
|
dropChannel*:
|
|
proc(channelId: SdsChannelID) {.gcsafe, raises: [].}
|
|
|
|
# Bootstrap on `addChannel` / `getOrCreateChannel`.
|
|
loadAllForChannel*:
|
|
proc(channelId: SdsChannelID): ChannelSnapshot {.gcsafe, raises: [].}
|
|
|
|
proc noOpPersistence*(): Persistence =
|
|
## Default backend that discards every write and returns an empty snapshot.
|
|
## Used so existing callers (and tests) that don't care about durability
|
|
## keep working without supplying a real backend.
|
|
Persistence(
|
|
saveLamport: proc(channelId: SdsChannelID, lamport: int64) =
|
|
discard,
|
|
appendLogEntry: proc(channelId: SdsChannelID, msg: SdsMessage) =
|
|
discard,
|
|
removeLogEntry: proc(channelId: SdsChannelID, msgId: SdsMessageID) =
|
|
discard,
|
|
setRetrievalHint: proc(msgId: SdsMessageID, hint: seq[byte]) =
|
|
discard,
|
|
saveOutgoing: proc(channelId: SdsChannelID, msg: UnacknowledgedMessage) =
|
|
discard,
|
|
removeOutgoing: proc(channelId: SdsChannelID, msgId: SdsMessageID) =
|
|
discard,
|
|
saveIncoming: proc(channelId: SdsChannelID, msg: IncomingMessage) =
|
|
discard,
|
|
removeIncoming: proc(channelId: SdsChannelID, msgId: SdsMessageID) =
|
|
discard,
|
|
saveOutgoingRepair: proc(
|
|
channelId: SdsChannelID, msgId: SdsMessageID, entry: OutgoingRepairEntry
|
|
) =
|
|
discard,
|
|
removeOutgoingRepair: proc(channelId: SdsChannelID, msgId: SdsMessageID) =
|
|
discard,
|
|
saveIncomingRepair: proc(
|
|
channelId: SdsChannelID, msgId: SdsMessageID, entry: IncomingRepairEntry
|
|
) =
|
|
discard,
|
|
removeIncomingRepair: proc(channelId: SdsChannelID, msgId: SdsMessageID) =
|
|
discard,
|
|
dropChannel: proc(channelId: SdsChannelID) =
|
|
discard,
|
|
loadAllForChannel: proc(channelId: SdsChannelID): ChannelSnapshot =
|
|
ChannelSnapshot(),
|
|
)
|