feat(scheduler-allocation): implement the unsatisfiable-resreq cache
Adds alloc_cache.c: xce_cache_key_t, cache_key_ops (the corrected three-way, 0-means-equal .cmp -- an inverted-boolean version was a real bug found during design review), and cache_lookup/insert/clear against a real kvtab_t (constructed matching xce_select.c's sel_node_table_init() precedent). Cache values are a boxed reason- category int, not a bare hit/miss sentinel, satisfying Req 7 AC5's requirement that a hit reproduce the original pend-reason category. unsatisfiable_cache lives on xce_scheduler_t per Req 7 AC1. Task 2.2 (scheduler-allocation spec). Requirements 7.1, 7.3, 7.4, 7.5. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
This commit is contained in:
parent
d9831b0bc8
commit
86c86352d1
8 changed files with 871 additions and 1 deletions
134
src/scheduler/include/sched/alloc_cache.h
Normal file
134
src/scheduler/include/sched/alloc_cache.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#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 <kv.h>
|
||||
|
||||
#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);
|
||||
|
||||
#endif
|
||||
|
|
@ -138,6 +138,27 @@ typedef struct {
|
|||
typedef struct {
|
||||
xce_sched_core_t core;
|
||||
xcecli_ctx_t apictx;
|
||||
/* Req 7.1 (scheduler-allocation spec, task 2.2): the per-session
|
||||
* unsatisfiable-resreq fingerprint cache -- a kvtab_t (kv.h, RTTI `var`)
|
||||
* keyed by xce_cache_key_t (sched/alloc_cache.h), value = a pend-reason
|
||||
* category boxed as (void*)(intptr_t)category (Req 7.5). Constructed in
|
||||
* sched_init(), destroyed in sched_fini() (scheduler.c) -- both minimal
|
||||
* construct/destroy calls only, using the exported cache_key_ops
|
||||
* (sched/alloc_cache.h), mirroring xce_select.c's sel_node_table_init()
|
||||
* convention for a composite-struct-keyed kvtab_t. Lookup/insert
|
||||
* (cache_lookup()/cache_insert(), alloc_cache.c) are confined to the
|
||||
* allocator thread; NULL until sched_init() constructs it.
|
||||
*
|
||||
* NOTE (task 2.2 CONCERN, for a later task's attention): Req 7.2
|
||||
* requires cache_clear() to run inside core_begin_session_internal()
|
||||
* (core_session.c), which only receives xce_sched_core_t* core, not
|
||||
* xce_scheduler_t* -- this field lives on the outer driver struct per
|
||||
* this task's own scope, not on `core` above. Wiring AC 2's clear call
|
||||
* is explicitly a later task (tasks.md 5.2), not this one; whoever
|
||||
* implements it will need to either reach the owning xce_scheduler_t
|
||||
* from `core` (core is this struct's first member, so the standard
|
||||
* pointer-to-first-member idiom applies) or relocate this field. */
|
||||
var unsatisfiable_cache;
|
||||
xce_cluster_config_t
|
||||
cluster_cfg; /* HA candidate list. Empty (candidates_count==0) only
|
||||
* transiently before sched_init() runs, when no
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue