1/* @ajax/mesh-jobs-log: what a member persists (jobs.rs `Log`, `Entry`, `Replica`, `replay`), in an
2 * append-only log of records:
3 * - LID: the highest leadership it followed or led (a restarted member never follows an older one
4 * again, and leads above it);
5 * - SNAPSHOT: the committed table at commit point (lid, n), which replaces what was before (a
6 * follower's entries of that leadership past n are kept);
7 * - ENTRY: an entry (lid, n) received or made, not committed yet: a delta, or a replace (a new
8 * leader's merge, which drops other leaderships' entries);
9 * - COMMIT: the entries of lid up to n are committed.
10 *
11 * Replaying the log rebuilds the member's replica exactly: entries that were not committed stay
12 * pending, never in the committed table (review finding 5). A torn tail (a crash mid-append, never
13 * acknowledged: review finding 4) ends the replay; a whole record that doesn't decode is corruption. */
14#ifndef MESH_JOBS_LOG_H
15#define MESH_JOBS_LOG_H
1617#include "mesh/jobs/op.h"
18#include "mesh/jobs/table.h"
19#include "mesh/strbuf.h"
2021#include <stdbool.h>
22#include <stddef.h>
23#include <stdint.h>
2425typedef enum { MJ_LOG_LID = 1, MJ_LOG_SNAPSHOT = 2, MJ_LOG_ENTRY = 3, MJ_LOG_COMMIT = 4 } mj_log_kind;
2627typedef struct {
28 mj_log_kind kind;
29 uint64_t lid;
30 uint64_t n; /* SNAPSHOT, ENTRY, COMMIT */
31 bool replace; /* ENTRY */
32 mj_ops ops; /* SNAPSHOT, ENTRY */
33} mj_log;
3435void mj_log_free(mj_log *l);
36mj_log mj_log_clone(const mj_log *l);
37static inline mj_log mj_log_lid(uint64_t lid) { return (mj_log){.kind = MJ_LOG_LID, .lid = lid}; }
38static inline mj_log mj_log_commit(uint64_t lid, uint64_t n) { return (mj_log){.kind = MJ_LOG_COMMIT, .lid = lid, .n = n}; }
39/* These take the ops. */
40static inline mj_log mj_log_snapshot(uint64_t lid, uint64_t n, mj_ops ops) { return (mj_log){.kind = MJ_LOG_SNAPSHOT, .lid = lid, .n = n, .ops = ops}; }
41static inline mj_log mj_log_entry(uint64_t lid, uint64_t n, bool replace, mj_ops ops) {
42 return (mj_log){.kind = MJ_LOG_ENTRY, .lid = lid, .n = n, .replace = replace, .ops = ops};
43}
4445/* A record: u8 kind, u64 length, the body. Appended to out. */
46void mj_log_encode(const mj_log *l, msb *out);
47bool mj_log_decode(uint8_t kind, const void *body, size_t n, mj_log *out, char **err);
4849/* A received or made entry, not committed yet. */
50typedef struct {
51 uint64_t lid; /* its leadership */
52 uint64_t n;
53 bool replace;
54 mj_ops ops; /* a delta's */
55 mj_table table; /* a replace's (initialised only when replace) */
56} mj_entry;
5758void mj_entry_free(mj_entry *e);
59mj_entry mj_entry_clone(const mj_entry *e);
60bool mj_entry_eq(const mj_entry *a, const mj_entry *b);
6162/* A member's durable job state: what its log replays to. */
63typedef struct {
64 uint64_t lid; /* the highest leadership followed or led */
65 uint64_t commit_lid, commit_n; /* the committed table's commit point */
66 mj_table committed;
67 mj_entry *pending; /* entries not committed yet, in order */
68 size_t npending, cappending;
69} mj_replica;
7071void mj_replica_init(mj_replica *r);
72void mj_replica_free(mj_replica *r);
73mj_replica mj_replica_clone(const mj_replica *r);
74bool mj_replica_eq(const mj_replica *a, const mj_replica *b);
75void mj_replica_apply(mj_replica *r, const mj_log *l);
76/* The last entry pending of leadership lid (0: none). */
77uint64_t mj_replica_last(const mj_replica *r, uint64_t lid);
7879typedef struct {
80 mj_log *v;
81 size_t n, cap;
82} mj_logs;
83void mj_logs_push(mj_logs *v, mj_log l);
84void mj_logs_free(mj_logs *v);
85/* The log that replays to r (a compaction). */
86mj_logs mj_replica_log(const mj_replica *r);
8788/* Replays a log file: the replica, the number of records and the bytes of whole records. A torn
89 * tail ends it there; a whole record that does not decode is an error (*err: "<why> (record n at
90 * byte b)"). */
91bool mj_replay(const void *bytes, size_t n, mj_replica *out, size_t *records, size_t *whole, char **err);
9293#endif