skip to content
==> 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}