ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
CircuitCache.cpp
Go to the documentation of this file.
1/**
2 * @file CircuitCache.cpp
3 * @brief LRU circuit-gate cache implementation and C-linkage wrappers.
4 *
5 * Implements @c CircuitCache::insert() and @c CircuitCache::get(), and
6 * the three C-linkage wrapper functions declared in @c circuit_cache.h:
7 * - @c circuit_cache_create_gate()
8 * - @c circuit_cache_get_children()
9 * - @c circuit_cache_get_type()
10 *
11 * The cache is a process-local Boost multi-index container bounded by
12 * @c provsql.gate_cache_size kilobytes. On overflow, the oldest (FIFO) entry
13 * is evicted. The C wrappers manage a singleton @c CircuitCache instance
14 * and translate between @c pg_uuid_t / @c gate_type (C types) and the
15 * C++ @c CircuitCacheInfos structure.
16 */
17#include <src/CircuitCache.h>
18
19extern "C" {
20#include "circuit_cache.h"
21}
22
23/** @brief Byte budget of the cache: @c provsql.gate_cache_size, in kB. */
24static inline unsigned long max_cache_size()
25{
26 return static_cast<unsigned long>(provsql_gate_cache_size) * 1024UL;
27}
28
30{
31 std::pair<iterator,bool> p=il.push_front(infos);
32
33 if(!p.second) {
34 /* Key collision: an entry for this token already exists. If the
35 * incoming entry carries more information (i.e. a real type
36 * replacing a placeholder gate_invalid stored by get_children),
37 * overwrite it; otherwise just touch the LRU position. The
38 * eviction loop is not re-run here: a replace can only grow an
39 * entry by (delta children count) * sizeof(pg_uuid_t), which is
40 * negligible against the budget and self-corrects on
41 * the next true insert. */
42 auto current_size_delta = static_cast<long>(infos.size())
43 - static_cast<long>(p.first->size());
44 bool replace = (p.first->type == gate_invalid && infos.type != gate_invalid)
45 || (p.first->children.empty() && !infos.children.empty());
46 if(replace) {
47 il.replace(p.first, infos);
48 current_size = static_cast<unsigned>(
49 static_cast<long>(current_size) + current_size_delta);
50 }
51 il.relocate(il.begin(),p.first);
52 return false;
53 } else {
54 current_size+=infos.size();
55 while(current_size>max_cache_size() && !il.empty()) {
56 /* Evict the LRU tail. Use back() rather than *il.end() to avoid
57 * dereferencing the past-the-end iterator (undefined behaviour). */
58 current_size -= il.back().size();
59 il.pop_back();
60 }
61 return true;
62 }
63}
64
65std::optional<CircuitCacheInfos> CircuitCache::get(pg_uuid_t token) const
66{
67 auto it = il.get<1>().find(token);
68 if(it!=il.get<1>().end())
69 return *it;
70 else
71 return {};
72}
73
74/** @brief Process-local singleton circuit gate cache. */
76
77extern "C" void circuit_cache_reset(void)
78{
79 cache.clear();
80}
81
82bool circuit_cache_create_gate(pg_uuid_t token, gate_type type, unsigned nb_children, const pg_uuid_t *children)
83{
84 return cache.insert({token, type, std::vector<pg_uuid_t>(children, children+nb_children)});
85}
86
88{
89 auto opt = cache.get(token);
90
91 if(opt) {
92 auto nb_children = opt.value().children.size();
93 /* Avoid calloc(0, ...): on glibc this returns a non-null pointer,
94 * which would defeat the caller's `if(!children)` cache-miss
95 * check. Treat zero-children cache entries as nullptr/0. */
96 if(nb_children == 0) {
97 *children = nullptr;
98 return 0;
99 }
100 *children=reinterpret_cast<pg_uuid_t*>(calloc(nb_children, sizeof(pg_uuid_t)));
101 for(unsigned i=0; i<nb_children; ++i)
102 (*children)[i] = opt.value().children[i];
103 return nb_children;
104 } else {
105 *children = nullptr;
106 return 0;
107 }
108}
109
111{
112 auto opt = cache.get(token);
113 if(opt)
114 return opt.value().type;
115 else
116 return gate_invalid;
117}
gate_type circuit_cache_get_type(pg_uuid_t token)
Retrieve the type of a cached gate.
unsigned circuit_cache_get_children(pg_uuid_t token, pg_uuid_t **children)
Retrieve the children of a cached gate.
bool circuit_cache_create_gate(pg_uuid_t token, gate_type type, unsigned nb_children, const pg_uuid_t *children)
Insert a new gate into the circuit cache.
void circuit_cache_reset(void)
Forget every cached gate.
static unsigned long max_cache_size()
Byte budget of the cache: provsql.gate_cache_size, in kB.
static CircuitCache cache
Process-local singleton circuit gate cache.
LRU in-process cache for recently created provenance circuit gates.
C-linkage interface to the in-process provenance circuit cache.
Bounded LRU cache mapping gate UUIDs to their CircuitCacheInfos.
item_list il
The container holding cached entries.
unsigned current_size
Current total byte usage of cached entries.
bool insert(const CircuitCacheInfos &infos)
Insert a new gate into the cache, evicting the oldest if necessary.
std::optional< CircuitCacheInfos > get(pg_uuid_t token) const
Look up a gate by UUID.
int provsql_gate_cache_size
Byte budget, in kB, of the per-backend gate cache; provsql.gate_cache_size GUC.
Definition provsql.c:128
All information stored for a single gate in the circuit cache.
std::vector< pg_uuid_t > children
Ordered list of child gate UUIDs.
unsigned size() const
Estimated memory footprint of this entry in bytes.
gate_type type
Kind of gate (input, plus, times…).