skip to content
==> tests/out.c <==
1/* durable: persists before sends and answers, one sync per run of persists, and fail-stop (jobs.rs's
2 * `nothing_leaves_without_its_persist`, the durable half; the node half is in @ajax/mesh-jobs-node). */
3#include "mesh/jobs/out.h"
4#include "mesh/abi/peer_frames.h"
5#include "mesh/check.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10typedef struct {
11    int appends, syncs, fail_append, fail_sync;
12    char trace[64];
13} disk;
14
15static bool app(void *c, const mj_log *l, char **err) {
16    disk *d = c;
17    (void)l;
18    d->appends++;
19    strcat(d->trace, "a");
20    if (d->appends == d->fail_append) {
21        *err = mstrdup("ENOSPC");
22        return false;
23    }
24    return true;
25}
26
27static bool syn(void *c, char **err) {
28    disk *d = c;
29    d->syncs++;
30    strcat(d->trace, "s");
31    if (d->fail_sync) {
32        *err = mstrdup("EIO");
33        return false;
34    }
35    return true;
36}
37
38static mj_outs sample(void) {
39    mj_outs v = {0};
40    mj_outs_push(&v, mj_out_persist(mj_log_lid(1)));
41    mj_outs_push(&v, mj_out_persist(mj_log_commit(1, 2)));
42    mj_outs_push(&v, mj_out_log(mstrdup("x")));
43    mj_outs_push(&v, mj_out_send(0, mj_frame_ack(1, 2, false)));
44    mj_outs_push(&v, mj_out_answer(7, true, mstrdup("ok")));
45    mj_outs_push(&v, mj_out_persist(mj_log_lid(2)));
46    mj_outs_push(&v, mj_out_cut(5, 0));
47    return v;
48}
49
50int main(void) {
51    disk d = {0};
52    mj_disk k = {&d, app, syn};
53    mj_outs in = sample();
54    bool ok;
55    char *err = NULL;
56    mj_outs got = mj_durable(&in, &k, &ok, &err);
57    CHECK(ok && got.n == 4);
58    CHECK_S(d.trace, "aasa" "s"); /* two appends, a sync before the send, the last append, a sync at the end */
59    CHECK(got.v[0].kind == MJ_OUT_LOG && got.v[1].kind == MJ_OUT_SEND && got.v[2].kind == MJ_OUT_ANSWER && got.v[3].kind == MJ_OUT_CUT);
60    CHECK(got.v[1].frame.ty == MESH_F_JOB_ACK && got.v[1].frame.n == 2);
61    mj_outs_free(&got);
62    /* The second append fails: nothing after the first append is acted on. */
63    disk d2 = {.fail_append = 2};
64    k.ctx = &d2;
65    in = sample();
66    got = mj_durable(&in, &k, &ok, &err);
67    CHECK(!ok && got.n == 0);
68    CHECK_S(err, "ENOSPC");
69    free(err);
70    mj_outs_free(&got);
71    /* The sync before the send fails: the log line before it went, the send did not. */
72    disk d3 = {.fail_sync = 1};
73    k.ctx = &d3;
74    in = sample();
75    got = mj_durable(&in, &k, &ok, &err);
76    CHECK(!ok && got.n == 1 && got.v[0].kind == MJ_OUT_LOG);
77    CHECK_S(err, "EIO");
78    free(err);
79    mj_outs_free(&got);
80    return mesh_check_done();
81}