1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Cumulative counters describing how the engine spent its work.
use fmt;
/// A snapshot of how a [`Database`](crate::Database) resolved its queries.
///
/// Incremental compilation is only worth its complexity if it actually avoids
/// work, and the only way to know it does is to count. Every derived query
/// resolution takes exactly one of three paths, and `Stats` counts each:
///
/// - **`computed`** — the query ran its [`compute`](crate::System::compute)
/// function. This is the expensive path: a cache miss, or a dependency that
/// genuinely changed and forced a recomputation.
/// - **`validated`** — the query was stale (something changed since it was last
/// checked) but re-examining its dependencies proved none of them actually
/// changed its inputs, so the cached value was reused without recomputing.
/// This is *early cutoff*, the property that makes the engine fast: a change
/// that does not alter a query's inputs does not recompute it.
/// - **`hits`** — the query was already verified at the current revision and
/// returned immediately, without even checking dependencies.
///
/// The counters are cumulative over the life of the database and only ever
/// increase. Snapshot them with [`Database::stats`](crate::Database::stats)
/// before and after an operation to measure exactly what that operation cost.
///
/// # Examples
///
/// After a run, most re-queries of an unchanged graph are hits, and a targeted
/// input change recomputes only what depends on it:
///
/// ```
/// use query_lang::{Database, System, QueryError};
///
/// struct Doubler;
/// impl System for Doubler {
/// type Key = u32;
/// type Value = u32;
/// fn compute(&self, db: &Database<Self>, key: &u32) -> Result<u32, QueryError> {
/// Ok(db.get(&(key + 100))? * 2) // reads input (key + 100), doubles it
/// }
/// }
///
/// let mut db = Database::new(Doubler);
/// db.set(101, 5);
///
/// assert_eq!(db.get(&1)?, 10);
/// assert_eq!(db.stats().computed, 1); // first resolution computes
///
/// assert_eq!(db.get(&1)?, 10);
/// assert_eq!(db.stats().hits, 1); // second is a free hit
/// # Ok::<(), QueryError>(())
/// ```