/** * @file circuit_cache.h * @brief C-linkage interface to the in-process provenance circuit cache. * * The circuit cache is a bounded, LRU-evicting in-memory store that * avoids repeated round-trips to the mmap-backed persistent circuit * storage for recently accessed gates. It is implemented in C++ (see * @c CircuitCache.h / @c CircuitCache.cpp) but exposed to the C parts of * the extension through this header. * * The cache is a pure read accelerator on top of a write-through design: * @c create_gate (in @c provsql_mmap.c) populates the cache and * unconditionally also forwards the IPC to the background worker, so the * worker is always at least as up-to-date as any backend's cache. * Evicted entries are therefore simply discarded (no flush hook is * needed: the worker already has them). Lookups that miss the cache * fall back to an IPC fetch from the worker. * * Two cache-poisoning hazards motivated the call-site skip rules in * @c provsql_mmap.c (search for "Skip caching" there): @c MMappedCircuit * returns @c gate_input as a default for unknown tokens, and reports * zero children for both real zero-child gates and unknown tokens, so * caching such ambiguous results would short-circuit subsequent real * @c create_gate calls in the same session. The cache itself is * agnostic to that policy. */ #ifndef CIRCUIT_CACHE_H #define CIRCUIT_CACHE_H #include "provsql_utils.h" /** * @brief Insert a new gate into the circuit cache. * * Records the gate identified by @p token with the given @p type and * @p nb_children children. If the cache is full the oldest entry is * dropped from memory to make room (the worker already holds it under * the write-through invariant, so no flush is performed). If an entry * for @p token is already present, its @p type and @p children are * overwritten when the incoming call carries strictly more information * (a real @c gate_type replacing a previously stored @c gate_invalid, * or a non-empty children list replacing an empty one); otherwise the * entry is left untouched and just refreshed in LRU order. * * @param token UUID identifying the new gate. * @param type Gate type (e.g. @c gate_input, @c gate_plus). * @param nb_children Number of child gates. * @param children Array of @p nb_children child UUIDs. * @return @c true if the gate was newly inserted, @c false if it was * already present in the cache (regardless of whether its * contents were upgraded). */ bool circuit_cache_create_gate(pg_uuid_t token, gate_type type, unsigned nb_children, const pg_uuid_t *children); /** * @brief Retrieve the children of a cached gate. * * Looks up @p token in the cache and, on a hit with at least one * child, allocates an array of child UUIDs (via @c calloc) and writes * its address to @p *children. * * @param token UUID of the gate to look up. * @param children Output parameter: set to a freshly @c calloc'd * array of child UUIDs on a hit with children; set to * @c NULL on a miss or on a hit for a zero-child * entry (the two are deliberately indistinguishable * to callers, see the file-level note). * @return Number of children found, or @c 0 on a miss or zero-child * hit. Callers should detect a miss via @c *children==NULL, * not via the return value. */ unsigned circuit_cache_get_children(pg_uuid_t token, pg_uuid_t **children); /** * @brief Retrieve the type of a cached gate. * * @param token UUID of the gate to look up. * @return The @c gate_type of the gate, or @c gate_invalid on a miss. */ gate_type circuit_cache_get_type(pg_uuid_t token); /** * @brief Forget every cached gate. * * A cache entry answers "this gate exists" without contacting the worker, * so it must be dropped before anything rebuilds the store under this * backend (see @c provsql.circuit_cleanup). */ void circuit_cache_reset(void); #endif /* CIRCUIT_CACHE_H */