#if !defined(_SCHED_ALLOC_CACHE_H_) #define _SCHED_ALLOC_CACHE_H_ 1 /* * alloc_cache.h - session-level unsatisfiable-resreq cache (scheduler- * allocation spec, task 2.2, Req 7.1, 7.3, 7.4, 7.5). * * A per-session memoization of resreqs this session already proved * unsatisfiable: a job whose fingerprint + identity matches a previous * exhausted allocation attempt can be pended immediately, without paying * full plan/host iteration cost again. * * Key shape: xce_resreq_fingerprint_t (sched/types.h, already populated on * xce_primary_schedobj_t.resreq_fp at job-ingest time -- see task 1.2) * composed with job identity (user/queue/project). Value: the ORIGINAL * exhausted attempt's pend-reason category (an int code), not a bare * present/absent marker -- Req 7.5 requires a cache hit to report the same * reason category the original miss determined, so operators are not * misled about why a later job was skipped. * * Backed by this codebase's existing keyed-table infrastructure (kv.h's * kvtab_type/key_find()/key_add()) via a THIRD kv_key_ops_t instance * (cache_key_ops, below), alongside kv.c's int_key_ops/string_key_ops -- * neither of those handles a composite struct key. See kv.c's * kvtab_find()/kvtab_add(): key_find() returns >0 on a hit, -ENOENT on a * miss, and does not allocate; key_add() allocates the heap-owned key copy * (cache_key_assign_(), below) and returns >=0 on success, -EEXIST if the * exact key is already present. * * This header intentionally exposes only what design.md's Service * Interface specifies (cache_lookup/cache_insert/cache_clear) plus the * key type and key ops -- no separate cache-construct/-destroy wrapper. * xce_scheduler_t.unsatisfiable_cache (scheduler.h) is constructed and * destroyed directly by sched_init()/sched_fini() (scheduler.c) via * makeInstance(&kvtab_type, ¶ms)/destroy(), using the exported * cache_key_ops below and the XCE_UNSAT_CACHE_NSLOTS slot count -- the * exact same convention xce_select.c's sel_node_table_init() already * establishes for its own composite-struct-keyed kvtab_t * (sel_node_key_ops). This cache's own unit test (test_alloc_cache.c) * constructs/destroys its fixture's table identically, so the two never * drift. */ #include #include "scheduler.h" /* * xce_cache_key_t - full cache key: the pre-computed resreq fingerprint * (xce_resreq_fingerprint_t, sched/types.h) plus job identity. Identity * fields are xstring-interned pointers (xce_sched_obj_t.user/queue/ * project) -- not owned, never freed by this cache; pointer equality * already implies content equality for xstring-interned strings (see * xstring.c), so no strcmp() is needed or performed. */ typedef struct { xce_resreq_fingerprint_t resreq; const char* user; /* xstring pointer, borrowed */ const char* queue; /* xstring pointer, borrowed */ const char* project; /* xstring pointer, borrowed */ } xce_cache_key_t; /* * cache_key_ops - the kv_key_ops_t (kv.h) binding hash/assign/cmp/destroy * for xce_cache_key_t. Exported so scheduler.c's sched_init()/sched_fini() * (the real construct/destroy call sites for xce_scheduler_t. * unsatisfiable_cache) and this cache's own test can build an identical * kvtab_t independently. */ extern kv_key_ops_t cache_key_ops; /* * XCE_UNSAT_CACHE_NSLOTS - slot count for the kvtab_t backing * xce_scheduler_t.unsatisfiable_cache. Shared by every construction call * site (sched_init(), cache_clear()'s destroy+recreate, and this cache's * own test) so they never drift apart. kv.c's kvtab_init() requires * nslots > 1. */ #define XCE_UNSAT_CACHE_NSLOTS 257 /* * cache_lookup - Req 7.4: before starting full allocation for a job, look * up its resreq fingerprint + identity in the cache. * * Preconditions: obj->tag == XCE_RESREQ_PRIMARY; obj->u.primary->resreq_fp * already populated (job-ingest time, core_build_primary_schedobj(), * core_events.c -- task 1.2). * * Returns: * 1 - hit; *out_reason_category is set to the reason category the * ORIGINAL exhausted attempt stored at cache_insert() time (Req * 7.5). Caller pends the job using this category, without * invoking the plan/host iterators or preemption for this attempt. * 0 - miss (including: cache not yet constructed). * <0 - error (NULL argument, or obj is not a populated primary resreq * scheduling object). */ extern int cache_lookup(xce_scheduler_t* sched, xce_sched_obj_t* obj, int* out_reason_category); /* * cache_insert - Req 7.3: when a job's primary resreq's plan iterator is * exhausted without a successful allocation, populate the cache with that * resreq's fingerprint + identity and the exhausted attempt's own final * pend-reason category (the exhausted plan iterator's own final pend * reason's main_reason), so a later cache hit for the same key reports the * same reason (Req 7.5). * * Returns 0 on success, <0 on error (NULL argument; obj is not a populated * primary resreq scheduling object; -EEXIST if this exact key is already * cached; the cache is not yet constructed). */ extern int cache_insert(xce_scheduler_t* sched, xce_sched_obj_t* obj, int reason_category); /* * cache_clear - Req 7.2 (wiring into core_begin_session_internal() is a * later task -- tasks.md 5.2, not this one): empty every entry from the * cache. Called once at the start of every scheduling session. * * kv.h's intf_kv_t exposes only add_key/del_key/find_key -- no batch * clear-all primitive exists on kvtab_t. This implementation destroys the * existing backing table and constructs a fresh, empty one with the same * cache_key_ops/XCE_UNSAT_CACHE_NSLOTS in its place, which is functionally * equivalent to clearing every entry. If reconstruction fails (e.g. * makeInstance() returns NULL on -ENOMEM), the cache is left NULL: * cache_lookup() treats a NULL cache as an unconditional miss and * cache_insert() returns an error, so a failed clear degrades to "cache * always misses" rather than crashing or operating on a stale table. */ extern void cache_clear(xce_scheduler_t* sched); /* * xce_alloc_cache_clear_cb - the xce_core_cache_clear_cb_t (core.h) * adapter for cache_clear() above (scheduler-allocation spec, task 5.2 * round 2, Req 7.2). Registered on the real xce_scheduler_t's core via * xce_core_set_cache_clear_cb() (main.c). See alloc_cache.c's own doc * comment on this function for the full rationale, including why it lives * here rather than in driver.c. */ extern void xce_alloc_cache_clear_cb(xce_sched_core_t* core); /* * xce_alloc_cache_lookup_cb / xce_alloc_cache_populate_cb - the * xce_unsatisfiable_lookup_cb_t / xce_unsatisfiable_populate_cb_t (core.h) * adapters for cache_lookup()/cache_insert() above (scheduler-sim-live- * allocation spec, task 1.1, Req 5.2). Registered on the real * xce_scheduler_t's core via direct field assignment in main.c's * normal-mode-only startup branch, alongside xce_alloc_cache_clear_cb() * just above. See alloc_cache.c's own doc comment on these functions for * the full rationale, including why they live here rather than in * driver.c. */ extern int xce_alloc_cache_lookup_cb(xce_sched_core_t* core, xce_sched_obj_t* obj, int* out_reason_category); extern void xce_alloc_cache_populate_cb(xce_sched_core_t* core, xce_sched_obj_t* obj, int reason_category); #endif