==> include/mesh/jobs/node.h <==1/* @ajax/mesh-jobs-node: a meshd's job node, sans IO (jobs.rs `Node`): its replica of the fleet's job
2 * table, the leader's dispatcher when it leads, and its side of the leases. jobd runs one per meshd
3 * over the peer links, the driver and a persisted log (@ajax/mesh-jobd); the tests run several over a
4 * simulated network.
5 *
6 * The node never does IO: every input (a view of the fleet, a frame, a finished item, a stored or
7 * acknowledged blob, a request, a tick) returns outputs (@ajax/mesh-jobs-out) for the caller to make
8 * durable (`mj_durable`) and act on, in order.
9 *
10 * The struct is open so jobd's views and the tests can read it; change it only through the
11 * functions. */
12#ifndef MESH_JOBS_NODE_H
13#define MESH_JOBS_NODE_H
14
15#include "mesh/jobs/frame.h"
16#include "mesh/jobs/id.h"
17#include "mesh/jobs/log.h"
18#include "mesh/jobs/merge.h"
19#include "mesh/jobs/out.h"
20#include "mesh/jobs/rate.h"
21#include "mesh/jobs/req.h"
22#include "mesh/jobs/select.h"
23#include "mesh/jobs/table.h"
24#include "mesh/omap.h"
25
26#include <stdbool.h>
27#include <stddef.h>
28#include <stdint.h>
29
30/* A member re-sends a result the table does not show yet after this long (ms). */
31#define MESH_JOBS_RESEND_MS 3000u
32/* A member's F_JOB_AVAIL every second (and on any change). */
33#define MESH_JOBS_AVAIL_MS 1000u
34/* A member whose lease expired gets no new lease of that program for this long (or 4 x the dead time). */
35#define MESH_JOBS_HOLDOFF_MS 10000u
36/* An expired lease's items each count a try only up to this many items. */
37#define MESH_JOBS_EXPIRY_TRIES_MAX 10000u
38/* `job results` lines per request, at most. */
39#define MESH_JOBS_RESULTS_MAX 100000u
40/* A submit for a program no member reports yet waits this long for one. */
41#define MESH_JOBS_SUBMIT_WAIT_MS 2000u
42
43typedef struct {
44 uint32_t me, n, quorum;
45 uint64_t dead_ms;
46} mj_cfg;
47
48/* The fleet as the jobs thread sees it: the fleet term, the leader, whom it hears (alive) and whom it
49 * is linked to. A single meshd: term 1, itself. */
50typedef struct {
51 uint64_t term;
52 bool has_leader;
53 uint32_t leader;
54 uint32_t alive, linked;
55} mj_view;
56
57static inline bool mj_view_eq(const mj_view *a, const mj_view *b) {
58 return a->term == b->term && a->has_leader == b->has_leader && (!a->has_leader || a->leader == b->leader) && a->alive == b->alive &&
59 a->linked == b->linked;
60}
61
62/* A lease, as the leader tracks it. */
63typedef struct {
64 uint64_t id;
65 mj_job_id job;
66 uint32_t member;
67 char *program;
68 mj_ranges out; /* items not reported yet */
69 uint64_t size, issued, deadline;
70} mj_lease;
71
72typedef struct {
73 mj_ranges fresh, retry;
74} mj_queue;
75
76/* A small ordered map keyed by (member, program): the leader's rates and hold-offs. */
77typedef struct {
78 uint32_t member;
79 char *program;
80 mj_rate rate; /* rates */
81 uint64_t until; /* holdoff */
82} mj_mp;
83
84typedef struct {
85 mj_mp *v;
86 size_t n, cap;
87} mj_mpmap;
88
89mj_mp *mj_mpmap_get(const mj_mpmap *m, uint32_t member, const char *program);
90mj_mp *mj_mpmap_put(mj_mpmap *m, uint32_t member, const char *program); /* zeroed if new */
91
92/* The leader's dispatcher (soft state: a new leader rebuilds it). */
93typedef struct {
94 momap *leases; /* id -> mj_lease */
95 momap *queues; /* job -> mj_queue */
96 mj_member_progs progs; /* per member: the job programs it runs (F_JOB_AVAIL) */
97 mj_mpmap rates; /* (member, program) -> rate */
98 mj_mpmap holdoff; /* (member, program) -> until: no new lease of it there */
99 uint64_t next_lease;
100 momap *picks; /* job -> mj_picks: the last select per job (for `job status`) */
101} mj_disp;
102
103typedef struct {
104 uint64_t n, token;
105 bool ok;
106 char *text;
107} mj_wait;
108
109typedef struct {
110 mj_submit s;
111 uint64_t token, until;
112} mj_held_submit;
113
114typedef struct {
115 uint32_t member;
116 mj_held h;
117} mj_adopt;
118
119typedef struct {
120 uint32_t from;
121 mj_item_res r;
122} mj_parked;
123
124typedef struct {
125 mj_parked *v;
126 size_t n, cap;
127} mj_parked_list;
128
129typedef struct {
130 uint64_t lid, term;
131 bool active;
132 /* Pulling: who answered (wholly), the answers, the leases members hold. */
133 uint32_t responded;
134 mj_answer *answers[MJ_MEMBERS];
135 mj_adopt *adopt;
136 size_t nadopt, capadopt;
137 uint64_t pull_at;
138 uint32_t heard_mask;
139 uint64_t heard[MJ_MEMBERS]; /* when a part of a member's answer last arrived */
140 mj_table working;
141 uint64_t n; /* the last entry number used */
142 uint64_t *acked; /* per member (cfg.n) */
143 uint64_t commit;
144 uint32_t snap_due;
145 mj_ops ops; /* ops of this tick, not in a delta yet */
146 mj_wait *waits;
147 size_t nwaits, capwaits;
148 mj_disp disp;
149 momap *dupes; /* job -> uint64_t duplicates counted here */
150 mj_held_submit *held;
151 size_t nheld, capheld;
152 momap *parked; /* (job, hash words) -> mj_parked_list: results waiting for their blob here */
153 momap *fetch_at; /* (job, hash words) -> uint64_t: when each parked blob was last asked for */
154} mj_lead;
155
156/* A payload result before its copies are durable. */
157typedef struct {
158 mj_item_res r;
159 uint32_t acked, refused;
160 uint32_t sent_mask;
161 uint64_t sent[MJ_MEMBERS]; /* when each member was last pushed to */
162} mj_staged;
163
164typedef struct {
165 mj_item_res r;
166 uint64_t at;
167} mj_outbox_entry;
168
169typedef struct {
170 mj_cfg cfg;
171 mj_view view;
172 mj_replica rep; /* this member's durable state (its log replays to it) */
173 bool has_lid_from;
174 uint32_t lid_from; /* the member that leads rep.lid */
175 uint64_t next;
176 bool has_staging;
177 uint64_t staging_n;
178 uint32_t staging_seq;
179 mj_table staging; /* a table arriving in parts */
180 uint64_t gap_at;
181 mj_lead *lead;
182 uint64_t behind;
183 /* The member side. */
184 momap *held; /* lease -> mj_held */
185 momap *outbox; /* (job, item, lease) -> mj_outbox_entry */
186 mj_prog *progs;
187 size_t nprogs;
188 uint64_t avail_at;
189 bool avail_dirty;
190 uint64_t prune_at;
191 mj_rate rate;
192 mj_frame *lo; /* frames to this member itself (the leader is a member too) */
193 size_t nlo, lo_head, caplo;
194 mj_sched *sched; /* the fleet scheduler's model (NULL: a single meshd); owned */
195 momap *staged; /* (job, item, lease) -> mj_staged */
196 momap *have; /* (job, hash words): the jobs' blobs this member's store holds durably */
197} mj_node;
198
199/* A node from its replayed log (takes rep). */
200mj_node *mj_node_new(mj_cfg cfg, mj_replica rep);
201void mj_node_free(mj_node *n);
202
203const mj_table *mj_node_committed(const mj_node *n);
204/* The fleet scheduler's model (every second, from the fleet thread); the node takes it. */
205void mj_node_set_sched(mj_node *n, mj_sched *cx);
206/* The leader's last member choice for a job (NULL: not leading, or nothing to lease). */
207const mj_picks *mj_node_picks(const mj_node *n, mj_job_id job);
208bool mj_node_leading(const mj_node *n);
209bool mj_node_active(const mj_node *n);
210uint64_t mj_node_lid(const mj_node *n);
211uint64_t mj_node_commit_n(const mj_node *n);
212/* The leader's working table (results not committed yet included), or NULL. */
213const mj_table *mj_node_working(const mj_node *n);
214/* The leader's leases (id -> mj_lease), or NULL. */
215const momap *mj_node_leases(const mj_node *n);
216const mj_mpmap *mj_node_rates(const mj_node *n);
217bool mj_node_queued(const mj_node *n, mj_job_id job, uint64_t *count);
218double mj_node_my_rate(const mj_node *n);
219size_t mj_node_unconfirmed(const mj_node *n);
220size_t mj_node_staged(const mj_node *n);
221size_t mj_node_parked(const mj_node *n);
222bool mj_node_has_blob(const mj_node *n, mj_job_id job, const mj_hash *h);
223
224/* The fleet's view changed (or the periodic refresh). */
225mj_outs mj_node_set_view(mj_node *n, mj_view v, uint64_t now, uint64_t unix_ms);
226/* A frame from member `from` (taken). */
227mj_outs mj_node_recv(mj_node *n, uint32_t from, mj_frame f, uint64_t now, uint64_t unix_ms);
228/* A finished item from the local driver (taken); a payload's blob already handed to blobd. */
229mj_outs mj_node_result(mj_node *n, mj_item_res r, uint64_t now);
230/* Job's blob `hash` is durable in this member's store. */
231mj_outs mj_node_blob_stored(mj_node *n, mj_job_id job, mj_hash hash, uint64_t now, uint64_t unix_ms);
232/* Job's blob `hash` left this member's store. */
233void mj_node_blob_gone(mj_node *n, mj_job_id job, const mj_hash *hash);
234/* Member `from` acknowledged job's blob durable (ok), or its store refused it. */
235void mj_node_blob_acked(mj_node *n, uint32_t from, mj_job_id job, mj_hash hash, bool ok);
236/* The job programs running here (from the driver; taken). */
237void mj_node_set_progs(mj_node *n, mj_prog *progs, size_t count);
238/* A change asked for (taken); answered by token after its commit. */
239mj_outs mj_node_request(mj_node *n, mj_req req, uint64_t token, uint64_t now, uint64_t unix_ms);
240/* The periodic work: the leader's dispatch, deltas and commits; a member's results and availability. */
241mj_outs mj_node_tick(mj_node *n, uint64_t now, uint64_t unix_ms);
242
243/* This member's answer to a pull (numbered F_JOB_HAVE parts), as frames (for tests). */
244void mj_node_answer(const mj_node *n, uint64_t lid, mj_frame **out, size_t *count);
245/* The leader's handling of results from `from` (taken; for tests: jobs.rs's tests call it directly). */
246void mj_node_on_results(mj_node *n, uint32_t from, mj_item_res *items, size_t count, uint64_t now, uint64_t unix_ms, mj_outs *out);
247
248#endif