store.h (6373B)
1 #ifndef KIT_BUILD_STORE_H 2 #define KIT_BUILD_STORE_H 3 4 #include <kit/build_coord.h> /* KitBuildStoreIo */ 5 #include <kit/cas.h> 6 #include <kit/core.h> 7 #include <stddef.h> 8 #include <stdint.h> 9 10 #include "build.h" 11 #include "trace.h" 12 13 /* 14 * The on-disk build store and its crash-safe storage state machine. Sits beside 15 * (or contains) a CAS: 16 * 17 * <store>/ 18 * cas/ # shared content store (KitCas), per 19 * DISTRIBUTE.md build/ trace/<pp>/<trace-id> # IMMUTABLE canonical trace 20 * bodies target/<pp>/<target-key> # MUTABLE per-target trace set (the only 21 * mutable state) cache/<pp>/<tree-id>/ # materialized output directories 22 * tmp/ # staging for atomic writes + recipe 23 * sandboxes 24 * 25 * Invariant: anything reachable under a content key (cas blob/tree, 26 * build/trace) is complete and matches its key — content objects are written 27 * into tmp/, fsync'd, then atomically renamed into place (idempotent, so a 28 * racing producer is harmless). The target record is the only 29 * ordering-sensitive state, updated by read-modify-write into tmp/ then atomic 30 * rename; a torn or unparseable record is treated as empty. The store degrades 31 * toward "rebuild," never toward "wrong answer." 32 * 33 * Config maps, argv vectors, deepset closure nodes, and source bytes are all 34 * ordinary CAS blobs (written via kit_cas_add_blob, read back by id via 35 * kit_cas_get_blob — a deepset is self-verifying content, so it needs no 36 * build-specific path); only trace/ (claims), target/ (the mutable record), and 37 * cache/ are build-specific. This module owns those paths, atomicity, and the 38 * record RMW; it does not interpret trace bodies (that is trace.h). 39 */ 40 41 typedef struct BuildStore { 42 const KitContext* ctx; 43 KitCas* cas; /* borrowed: the shared content store */ 44 const KitBuildStoreIo* io; /* borrowed: fs atomicity extras */ 45 const KitCasHost* cas_host; /* borrowed: file_io for trace/record reads */ 46 char root[BUILD_PATH_MAX]; /* the <store>/build root */ 47 } BuildStore; 48 49 /* Bind a store view over an open CAS and the build root. Borrows all pointers 50 * for the store's lifetime; creates the build/ skeleton if absent. */ 51 int build_store_open(const KitContext*, KitCas* cas, const KitBuildStoreIo*, 52 const KitCasHost*, KitSlice store_root, BuildStore* out); 53 54 /* Final content-keyed paths within the store. Each writes a NUL-terminated path 55 * (cap >= BUILD_PATH_MAX) and returns BUILD_OK/BUILD_ERR. */ 56 int build_store_trace_path(const BuildStore*, 57 const uint8_t trace_id[BUILD_HASH_LEN], char* out, 58 size_t cap); 59 int build_store_target_path(const BuildStore*, 60 const uint8_t target_key[BUILD_HASH_LEN], char* out, 61 size_t cap); 62 int build_store_cache_path(const BuildStore*, 63 const uint8_t tree_id[BUILD_HASH_LEN], char* out, 64 size_t cap); 65 66 /* ---- Trace bodies (immutable, content-addressed) ---------------------- */ 67 68 /* Store a canonical trace body: write to tmp/, fsync, atomic-rename to 69 * trace/<pp>/<trace-id>. Idempotent. *out_trace_id = BLAKE2b(body). */ 70 int build_store_put_trace(BuildStore*, const uint8_t* body, size_t len, 71 uint8_t out_trace_id[BUILD_HASH_LEN]); 72 73 /* Load a trace body's bytes by id (borrowed via file_io; release with 74 * build_store_release). Returns BUILD_ERR (absent) on a miss — never fatal. */ 75 int build_store_get_trace(BuildStore*, const uint8_t trace_id[BUILD_HASH_LEN], 76 KitFileData* out); 77 void build_store_release(BuildStore*, KitFileData*); 78 79 /* ---- Target record (mutable, the only ordering-sensitive object) ------ */ 80 81 /* Load a target's record by key. A missing or unparseable record yields an 82 * empty record (BUILD_OK with n_rows == 0) — fail safe. */ 83 int build_store_record_load(BuildStore*, 84 const uint8_t target_key[BUILD_HASH_LEN], 85 KitSlice target_name, BuildTargetRecord* out); 86 87 /* Prepend (kind, trace_id) to the target's record and commit it: read-modify- 88 * write into tmp/ then atomic rename, dedup + truncate to KIT_BUILD_RECORD_CAP. 89 * Takes the optional advisory lock around the RMW when the host provides one. 90 */ 91 int build_store_record_update(BuildStore*, 92 const uint8_t target_key[BUILD_HASH_LEN], 93 KitSlice target_name, BuildTraceKind kind, 94 const uint8_t trace_id[BUILD_HASH_LEN]); 95 96 /* ---- Tree cache (materialized output directories) --------------------- */ 97 98 /* If tree-id is already materialized under cache/, write its path and return 99 * BUILD_OK; otherwise return BUILD_ERR (caller climbs the materialization 100 * ladder). */ 101 int build_store_cache_lookup(const BuildStore*, 102 const uint8_t tree_id[BUILD_HASH_LEN], 103 char* path_out, size_t cap); 104 105 /* Materialize a stored CAS tree into cache/<pp>/<tree-id>/ (verifying each 106 * blob), staged in tmp/ then atomically renamed; write its path. Returns 107 * BUILD_ERR if the tree's bytes are not in the CAS (caller may fetch/rebuild). 108 */ 109 int build_store_cache_materialize(BuildStore*, 110 const uint8_t tree_id[BUILD_HASH_LEN], 111 char* path_out, size_t cap); 112 113 /* Ingest a recipe's output directory into the CAS as a tree, then install it 114 * into the cache; *out_tree_id gets the tree id and `path_out` its cache path. 115 * Wraps kit_cas_add_tree_from_dir + cache install (atomic rename). */ 116 int build_store_ingest_output(BuildStore*, const char* out_dir, 117 uint8_t out_tree_id[BUILD_HASH_LEN], 118 char* path_out, size_t cap); 119 120 /* ---- Recipe sandboxes -------------------------------------------------- */ 121 122 /* Create a fresh sandbox under build/tmp/run-<n>/ with an empty out/ subdir; 123 * write the sandbox root and the out/ path. Removed by build_store_sandbox_done 124 * on success; orphaned (and swept later) on failure/crash. */ 125 int build_store_sandbox_new(BuildStore*, char* sandbox_out, size_t sandbox_cap, 126 char* out_dir_out, size_t out_cap); 127 void build_store_sandbox_done(BuildStore*, const char* sandbox); 128 129 #endif