Skip to content

Local durable log

DurableLog is a local append-only persistence substrate. It supplies atomic batches, stream-scoped offsets, deterministic replay and recovery checks. It does not itself replicate records, deliver queue messages or coordinate distributed transactions. Higher-level distributed patterns add their own protocols.

Append and replay

This host fragment uses DurableLogBuilder from org.j7m.middleware.durablelog and the API types in org.j7m.middleware.api.durablelog. dataDirectory is a host-owned Path; the example owns the log and cursor.

try (DurableLog log = new DurableLogBuilder()
        .directory(dataDirectory)
        .fsyncPolicy(FsyncPolicy.PER_APPEND)
        .build()) {
    LogStreamId stream = new LogStreamId("orders");
    LogRecord record = new LogRecord(
            LogRecordHeader.data(Map.of("kind", "accepted")),
            "order-42".getBytes(StandardCharsets.UTF_8));
    log.append(stream, List.of(record), AppendOptions.defaults());

    try (LogCursor cursor = log.openCursor(
            stream, LogOffset.beginning(), CursorOptions.defaults())) {
        LogRecord replayed = cursor.next().orElseThrow();
        String order = new String(replayed.payload(), StandardCharsets.UTF_8);
        // order is "order-42" in a newly created directory.
    }
}

The fragment is checked against the current API; it is not a standalone executable example. A reused directory can contain earlier records. Each successful batch receives ordered offsets atomically; do not infer transactionality for external effects from this append result. Close cursors promptly: the implementation bounds open cursors, record sizes and records per append.

Choose the durability window

FsyncPolicy Storage behavior
PER_APPEND Force storage sync after every append
GROUP_COMMIT Share sync across appends according to the group-commit count/time policy
ON_CLOSE Force sync when closing the log
OS_BUFFERED Rely on OS buffering; intended for development

Group commit counts appends rather than individual records. Segment rotation and close flush pending work. Document the accepted loss window for the selected policy and storage device; a successful buffered append is not proof that a power loss cannot lose it.

Recovery, snapshots and retention

Construction performs recovery. The default recovery policy fails on corrupt or incomplete data. Explicit tail truncation is destructive: it removes the first damaged/incomplete batch and the following tail. Use it as a deliberate recovery decision, inspect the safe recovery report and preserve the original storage when investigation is needed.

createSnapshot writes a replay-visible marker. Applications own state serialization and restoration; the marker does not install an application snapshot or consensus snapshot. Retention can be disabled or constrained by size/age. It preserves the active segment and protected snapshot offsets. Replay from an old removed offset begins at the earliest retained eligible record, so applications must retain the state needed to interpret that replay.

LogCursor.next() returns empty at end of the available log. CursorOptions.allowMissingStream explicitly controls missing-stream behavior. Optional bounded findLatestOffsetByMetadata returns a hint that retention can invalidate before a cursor opens; its default API implementation returns empty.

Keep payloads and credentials out of header metadata. Status and recovery summaries are redacted; application code deliberately reading records remains responsible for their confidentiality. See local coordination and work queues.