kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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