==> tests/select.c <==1/* select without a fleet (a single meshd), and with a scheduler stub: verdicts, the owner policy's
2 * backoff, the order. (With the fleet's own scheduler: @ajax/mesh-jobs-sched's test, the Rust's
3 * `select_is_the_schedulers_filter_and_score`.) */
4#include "mesh/jobs/select.h"
5#include "mesh/check.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10static mj_prog *one(const char *name) {
11 mj_prog *p = mxalloc(sizeof *p);
12 *p = (mj_prog){mstrdup(name), 0, 8};
13 return p;
14}
15
16static mj_spec job(uint32_t members) {
17 mj_spec s = {.id = 1, .program = mstrdup("p"), .op = 254, .items = mj_ranges_of(0, 100), .attempts = 3, .members = members};
18 return s;
19}
20
21static void oks(const mj_picks *v, const char *want) {
22 char got[64] = "";
23 for (size_t i = 0; i < v->n; i++)
24 if (v->v[i].ok) {
25 char b[8];
26 snprintf(b, sizeof b, "%s%u", got[0] ? "," : "", v->v[i].member);
27 strcat(got, b);
28 }
29 CHECK_S(got, want);
30}
31
32static const char *why(const mj_picks *v, uint32_t k) {
33 for (size_t i = 0; i < v->n; i++)
34 if (v->v[i].member == k) return v->v[i].why ? v->v[i].why : "";
35 return "";
36}
37
38/* A stub scheduler: member 1 cordoned, member 2 busy, scores by member (0 lowest). */
39static double score_of[4] = {1.0, 2.0, 3.0, 0.5};
40static int judge(void *ctx, uint32_t k, const char *prog, mj_score *s, bool *busy, char **w) {
41 (void)ctx;
42 (void)prog;
43 if (k == 1) {
44 *w = mstrdup("cordoned");
45 return 0;
46 }
47 if (k == 3) return -1;
48 *s = (mj_score){score_of[k], mfmt("%.3f = stub", score_of[k])};
49 *busy = k == 2;
50 return 1;
51}
52static char *busy_why(void *ctx, uint32_t k) {
53 (void)ctx;
54 return k == 2 ? mstrdup("cpu psi 0.40") : NULL;
55}
56
57int main(void) {
58 mj_member_progs progs = {0};
59 for (uint32_t k = 0; k < 4; k++) mj_member_progs_set(&progs, k, one(k == 3 ? "q" : "p"), 1);
60 mj_spec j = job(0);
61 /* No fleet (a single meshd): alive and running it. */
62 mj_picks v = mj_select(&j, 0xf, &progs, NULL);
63 oks(&v, "0,1,2");
64 CHECK(strstr(why(&v, 3), "does not run there") != NULL);
65 char *l = mj_pick_line(&v.v[0]);
66 CHECK_S(l, "pick system 0 eligible " MJ_SCORE_DEFAULT_EXPLAIN);
67 free(l);
68 mj_picks_free(&v);
69 v = mj_select(&j, 0xb, &progs, NULL);
70 CHECK_S(why(&v, 2), "down");
71 mj_picks_free(&v);
72 mj_spec_free(&j);
73 j = job(0x2);
74 v = mj_select(&j, 0xf, &progs, NULL);
75 oks(&v, "1");
76 CHECK_S(why(&v, 0), "not in the job's members");
77 mj_picks_free(&v);
78 mj_spec_free(&j);
79 /* With a scheduler: cordoned rejected, busy backed off while 0 is idle, best first. */
80 mj_sched cx = {NULL, 4, judge, busy_why, NULL};
81 mj_member_progs_set(&progs, 3, one("p"), 1);
82 j = job(0);
83 v = mj_select(&j, 0xf, &progs, &cx);
84 oks(&v, "0,3");
85 CHECK_S(why(&v, 1), "cordoned");
86 CHECK_S(why(&v, 2), "busy (owner policy: cpu psi 0.40): leases go to the idle members");
87 CHECK(v.v[0].member == 0 && v.v[1].member == 3); /* 1.0 then the unscored 0.0 */
88 l = mj_pick_line(&v.v[2]);
89 CHECK_S(l, "pick system 1 rejected: cordoned");
90 free(l);
91 mj_picks c = mj_picks_clone(&v);
92 CHECK(mj_picks_ok(&c, 3) != NULL && mj_picks_ok(&c, 2) == NULL);
93 mj_picks_free(&c);
94 mj_picks_free(&v);
95 /* The busy member alone (the others down): eligible, floor-sized leases. */
96 v = mj_select(&j, 0x4, &progs, &cx);
97 oks(&v, "2");
98 CHECK(v.v[0].busy);
99 l = mj_pick_line(&v.v[0]);
100 CHECK_S(l, "pick system 2 eligible 3.000 = stub busy: floor-sized leases (the only member)");
101 free(l);
102 mj_picks_free(&v);
103 mj_spec_free(&j);
104 mj_member_progs_free(&progs);
105 return mesh_check_done();
106}