skip to content
==> tests/log.c <==
1/* The job log: random record sequences replayed here and in the Rust to the same replica; the Rust
2 * test's log (what is committed and what is not, a torn tail); review finding 6's snapshot; and
3 * replay's answers to corrupt records. */
4#include "mesh/jobs/log.h"
5#include "mesh/check.h"
6#include "gen.h"
7#include "gold_log.h"
8
9static uint64_t fnv(const void *p, size_t n) {
10    const uint8_t *b = p;
11    uint64_t h = 0xcbf29ce484222325u;
12    for (size_t i = 0; i < n; i++) {
13        h ^= b[i];
14        h *= 0x100000001b3u;
15    }
16    return h;
17}
18
19static size_t unhexs(const char *h, uint8_t *out) {
20    size_t n = strlen(h) / 2;
21    for (size_t i = 0; i < n; i++) {
22        unsigned v;
23        sscanf(h + 2 * i, "%2x", &v);
24        out[i] = (uint8_t)v;
25    }
26    return n;
27}
28
29static void encode_logs(const mj_logs *v, msb *out) {
30    for (size_t i = 0; i < v->n; i++) mj_log_encode(&v->v[i], out);
31}
32
33static void random_sequences(void) {
34    size_t g = 0, ng = sizeof GOLD_SEQ / sizeof *GOLD_SEQ;
35    char line[256];
36    for (uint64_t seeds[] = {3, 5, 11, 99}, *sd = seeds; sd < seeds + 4; sd++) {
37        rs = *sd * 0x9e3779b97f4a7c15u | 1;
38        mj_replica rep;
39        mj_replica_init(&rep);
40        msb bytes = {0};
41        for (int i = 1; i <= 400; i++) {
42            uint64_t hi = 1 + rnd(3);
43            uint64_t lid = hi << 40 | rnd(3);
44            mj_log l;
45            switch (rnd(8)) {
46            case 0: l = mj_log_lid(lid); break;
47            case 1: {
48                uint64_t n = rnd(6), k = rnd(4);
49                mj_ops ops = {0};
50                for (uint64_t j = 0; j < k; j++) mj_ops_push(&ops, gen());
51                l = mj_log_snapshot(lid, n, ops);
52                break;
53            }
54            case 2:
55            case 3:
56            case 4: {
57                uint64_t n = rnd(8);
58                bool replace = rnd(4) == 0;
59                uint64_t k = rnd(4);
60                mj_ops ops = {0};
61                for (uint64_t j = 0; j < k; j++) mj_ops_push(&ops, gen());
62                l = mj_log_entry(lid, n, replace, ops);
63                break;
64            }
65            default: l = mj_log_commit(lid, rnd(8));
66            }
67            mj_log_encode(&l, &bytes);
68            mj_replica_apply(&rep, &l);
69            mj_log_free(&l);
70            if (i % 40 == 0) {
71                mj_logs lg = mj_replica_log(&rep);
72                msb c = {0};
73                encode_logs(&lg, &c);
74                snprintf(line, sizeof line, "%llu %d %016llx %zu lid %llx commit %llx,%llu pending %zu last %llu", (unsigned long long)*sd, i,
75                         (unsigned long long)fnv(c.p, c.n), c.n, (unsigned long long)rep.lid, (unsigned long long)rep.commit_lid,
76                         (unsigned long long)rep.commit_n, rep.npending, (unsigned long long)mj_replica_last(&rep, (uint64_t)1 << 40));
77                if (CHECK(g < ng)) CHECK_S(line, GOLD_SEQ[g]);
78                g++;
79                /* The compacted log replays to the same replica. */
80                mj_replica again;
81                CHECK(mj_replay(c.p, c.n, &again, NULL, NULL, NULL) && mj_replica_eq(&again, &rep));
82                mj_replica_free(&again);
83                mj_logs_free(&lg);
84                msb_free(&c);
85            }
86        }
87        mj_replica rr;
88        size_t n = 0, at = 0;
89        CHECK(mj_replay(bytes.p, bytes.n, &rr, &n, &at, NULL) && mj_replica_eq(&rr, &rep));
90        snprintf(line, sizeof line, "%llu replay %zu %zu %016llx", (unsigned long long)*sd, n, at, (unsigned long long)fnv(bytes.p, bytes.n));
91        if (CHECK(g < ng)) CHECK_S(line, GOLD_SEQ[g]);
92        g++;
93        mj_replica_free(&rr);
94        mj_replica_free(&rep);
95        msb_free(&bytes);
96    }
97    CHECK_U(g, ng);
98}
99
100static mj_res res(uint64_t lease) {
101    mj_res r = {.lease = lease, .member = 2, .result = 9, .out = mxalloc(3), .out_len = 3};
102    memcpy(r.out, (uint8_t[]){1, 2, 3}, 3);
103    return r;
104}
105
106static mj_ops one(mj_op o) {
107    mj_ops v = {0};
108    mj_ops_push(&v, o);
109    return v;
110}
111
112/* The log section of the Rust's ops_frames_and_the_merge. */
113static void the_rust_log(void) {
114    mj_spec s = gspec(0);
115    uint64_t l = (uint64_t)1 << 40;
116    mj_res r1 = res(1), r3 = res(3);
117    mj_table x;
118    mj_table_init(&x);
119    mj_op sub = mj_op_submit(&s), d1 = mj_op_done(s.id, 1, &r1);
120    mj_table_apply(&x, &sub);
121    mj_table_apply(&x, &d1);
122    msb log = {0};
123    mj_log a = mj_log_lid(l);
124    mj_log_encode(&a, &log);
125    mj_ops xo = {0};
126    mj_table_ops(&x, &xo);
127    mj_log snap = mj_log_snapshot(l, 3, xo);
128    mj_log_encode(&snap, &log);
129    mj_log e4 = mj_log_entry(l, 4, false, one(mj_op_done(s.id, 7, &r3)));
130    mj_log e5 = mj_log_entry(l, 5, false, one(mj_op_done(s.id, 6, &r3)));
131    mj_log c4 = mj_log_commit(l, 4);
132    mj_log_encode(&e4, &log);
133    mj_log_encode(&e5, &log);
134    mj_log_encode(&c4, &log);
135    static uint8_t want[8192];
136    size_t wn = unhexs(GOLD_TESTLOG, want);
137    CHECK(log.n == wn && memcmp(log.p, want, wn) == 0);
138    size_t whole = log.n;
139    mj_log e6 = mj_log_entry(l, 6, false, one(mj_op_done(s.id, 8, &r3)));
140    msb e6b = {0};
141    mj_log_encode(&e6, &e6b);
142    msb_put(&log, e6b.p, 20);
143    mj_replica r;
144    size_t n, at;
145    CHECK(mj_replay(log.p, log.n, &r, &n, &at, NULL));
146    CHECK_U(n, 5);
147    CHECK_U(at, whole);
148    CHECK_U(r.lid, l);
149    CHECK(r.commit_lid == l && r.commit_n == 4);
150    char *w = mj_ranges_word_new(&mj_table_get(&r.committed, s.id)->done);
151    CHECK_S(w, "1-2,7-8");
152    free(w);
153    CHECK_U(r.npending, 1); /* item 6's entry is not committed: pending, not in the table */
154    /* The replica's own log replays to it (a compaction). */
155    mj_logs lg = mj_replica_log(&r);
156    msb c = {0};
157    encode_logs(&lg, &c);
158    mj_replica again;
159    CHECK(mj_replay(c.p, c.n, &again, NULL, NULL, NULL) && mj_replica_eq(&again, &r));
160    mj_replica_free(&again);
161    mj_logs_free(&lg);
162    msb_free(&c);
163    mj_replica_free(&r);
164    for (mj_log *p[] = {&a, &snap, &e4, &e5, &c4, &e6}, **q = p; q < p + 6; q++) mj_log_free(*q);
165    msb_free(&e6b);
166    msb_free(&log);
167    mj_op_free(&sub);
168    mj_op_free(&d1);
169    mj_table_free(&x);
170    mj_res_free(&r1);
171    mj_res_free(&r3);
172    mj_spec_free(&s);
173    /* Corrupt records: the Rust's answers. */
174    for (size_t i = 0; i < sizeof GOLD_BAD / sizeof *GOLD_BAD; i++) {
175        uint8_t b[256];
176        size_t bn = unhexs(GOLD_BAD[i][0], b);
177        mj_replica q;
178        char *err = NULL;
179        if (mj_replay(b, bn, &q, &n, &at, &err)) {
180            char got[64];
181            snprintf(got, sizeof got, "ok %zu %zu", n, at);
182            CHECK_S(got, GOLD_BAD[i][1]);
183            mj_replica_free(&q);
184        } else {
185            CHECK_S(err, GOLD_BAD[i][1]);
186            free(err);
187        }
188    }
189}
190
191/* Review finding 6 (the Rust's a_snapshot_keeps_the_entries_acknowledged_past_it, replica half). */
192static void snapshot_keeps_entries(void) {
193    uint64_t l = (uint64_t)3 << 40;
194    mj_replica r;
195    mj_replica_init(&r);
196    mj_log seq[4] = {mj_log_lid(l), mj_log_snapshot(l, 8, (mj_ops){0}), mj_log_entry(l, 9, false, one(mj_op_fails(1, 9, 1))),
197                     mj_log_entry(l, 10, false, one(mj_op_fails(1, 10, 1)))};
198    for (int i = 0; i < 4; i++) {
199        mj_replica_apply(&r, &seq[i]);
200        mj_log_free(&seq[i]);
201    }
202    mj_spec s = gspec(0);
203    mj_log s8 = mj_log_snapshot(l, 8, one(mj_op_submit(&s)));
204    mj_replica_apply(&r, &s8);
205    CHECK(mj_replica_last(&r, l) == 10 && r.npending == 2); /* entries 9 and 10 stay */
206    mj_log s9 = mj_log_snapshot(l, 9, one(mj_op_submit(&s)));
207    mj_replica_apply(&r, &s9);
208    CHECK(mj_replica_last(&r, l) == 10 && r.npending == 1);
209    mj_log_free(&s8);
210    mj_log_free(&s9);
211    mj_spec_free(&s);
212    mj_replica_free(&r);
213}
214
215int main(void) {
216    random_sequences();
217    the_rust_log();
218    snapshot_keeps_entries();
219    return mesh_check_done();
220}