incremental_query/
lib.rs

1#![feature(macro_metavar_expr)]
2#![doc = include_str!("../README.md")]
3
4use siphasher::sip128::SipHasher13;
5
6mod context;
7mod generation;
8mod query;
9mod query_parameter;
10mod storage;
11
12pub use incremental_query_macros::*;
13
14pub use context::Context;
15pub use query_parameter::QueryParameter;
16pub use storage::Storage;
17
18// used in macros, hidden to the user
19#[doc(hidden)]
20pub use query::{ErasedQueryRun, Query, QueryMode};
21#[doc(hidden)]
22pub use query_parameter::TypeErasedQueryParam;
23
24pub type QueryHasher = SipHasher13;
25
26#[cfg(test)]
27pub fn log() {
28    use tracing_subscriber::EnvFilter;
29
30    let format = tracing_subscriber::fmt::format()
31        .without_time()
32        .with_source_location(false);
33
34    tracing_subscriber::fmt()
35        .event_format(format)
36        .with_env_filter(EnvFilter::from_default_env())
37        .try_init()
38        .ok();
39}
40
41#[cfg(test)]
42mod tests {
43    use crate::log;
44
45    // #[test]
46    // fn query_mode() {
47    //     define_query! {
48    //         #[rerun(always)]
49    //         fn always<'cx>(_cx: &Context<'cx>, _inp: &())  {}
50    //         fn cache<'cx>(_cx: &Context<'cx>, _inp: &())  {}
51    //         #[rerun(generation)]
52    //         fn generation<'cx>(_cx: &Context<'cx>, _inp: &())  {}
53    //     }
54    //     log();
55    //
56    //     assert_eq!(always.mode(), QueryMode::Always);
57    //     assert_eq!(generation.mode(), QueryMode::Generation);
58    //     assert_eq!(cache.mode(), QueryMode::Cache);
59    // }
60
61    use std::{
62        cell::{Cell, RefCell},
63        collections::VecDeque,
64        hash::Hash,
65        ops::Deref,
66        rc::Rc,
67    };
68
69    use crate::{
70        query, query_parameter::QueryParameter, rerun, storage::Storage, Context, QueryHasher,
71    };
72    use rand::{thread_rng, Rng};
73
74    pub mod incremental_query {
75        pub use crate::*;
76    }
77
78    #[derive(Clone)]
79    struct Counter(pub u64, Rc<Cell<usize>>);
80    impl Counter {
81        // create a new counter that counts how many times it has been add() ed to
82        // The metric itself is not hashed and is thus not compared for equality
83        // while calling queries
84        pub fn new(i: u64) -> Self {
85            Self(i, Default::default())
86        }
87
88        pub fn add(&self) {
89            self.1.set(self.1.get() + 1);
90        }
91
92        pub fn get(&self) -> usize {
93            self.1.get()
94        }
95    }
96
97    #[derive(Clone)]
98    struct ReturnInOrder<T> {
99        data: Rc<RefCell<VecDeque<T>>>,
100        id: u64,
101    }
102
103    impl<T> ReturnInOrder<T> {
104        fn new(inp: impl IntoIterator<Item = T>, id: u64) -> Self {
105            Self {
106                data: Rc::new(RefCell::new(inp.into_iter().collect())),
107                id,
108            }
109        }
110
111        fn is_empty(&self) -> bool {
112            self.data.borrow().len() == 0
113        }
114
115        fn next(&self) -> T {
116            self.data.borrow_mut().pop_front().expect("empty")
117        }
118    }
119
120    impl QueryParameter for Counter {
121        fn hash_stable(&self, hasher: &mut QueryHasher) {
122            self.0.hash(hasher);
123        }
124    }
125    impl<T: Clone + 'static> QueryParameter for ReturnInOrder<T> {
126        fn hash_stable(&self, hasher: &mut QueryHasher) {
127            self.id.hash(hasher);
128        }
129    }
130
131    #[test]
132    fn call_once() {
133        #[query]
134        fn test<'cx>(_cx: &Context<'cx>, input: &Counter) {
135            input.add();
136        }
137
138        log();
139        let storage = Storage::new();
140
141        let ctr = Counter::new(0);
142        let cx = Context::new(&storage);
143        test(&cx, ctr.clone());
144        assert_eq!(ctr.get(), 1);
145    }
146
147    #[test]
148    fn call_twice() {
149        #[query]
150        fn called_twice<#[lt] 'a>(_cx: &Context<'a>, input: &Counter) {
151            input.add();
152        }
153        log();
154
155        let storage = Storage::new();
156
157        let ctr = Counter::new(0);
158        let cx = Context::new(&storage);
159
160        called_twice(&cx, ctr.clone());
161        called_twice(&cx, ctr.clone());
162        assert_eq!(ctr.get(), 1);
163    }
164
165    #[test]
166    fn impure_rerun() {
167        #[query]
168        #[rerun(always)]
169        fn random<'cx>(_cx: &Context<'cx>) -> u64 {
170            thread_rng().gen_range(0..u64::MAX)
171        }
172
173        #[query]
174        fn depends_on_impure<'cx>(cx: &Context<'cx>, inp: &Counter) {
175            inp.add();
176            let _dep = random(cx);
177        }
178
179        log();
180
181        let storage = Storage::new();
182
183        let ctr = Counter::new(0);
184        let cx = Context::new(&storage);
185        depends_on_impure(&cx, ctr.clone());
186        depends_on_impure(&cx, ctr.clone());
187
188        assert_eq!(ctr.get(), 2);
189    }
190
191    #[test]
192    fn test_intvalue() {
193        #[query]
194        #[rerun(always)]
195        fn intvalue<'cx>(_cx: &Context<'cx>, r: &ReturnInOrder<i64>) -> i64 {
196            r.next()
197        }
198
199        #[query]
200        fn sign_of<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<i64>, c2: &Counter) -> bool {
201            let v = intvalue(cx, r.clone());
202            c2.add();
203
204            v.is_positive()
205        }
206
207        #[query]
208        fn some_other_query<'cx>(
209            cx: &Context<'cx>,
210            r: &ReturnInOrder<i64>,
211            c1: &Counter,
212            c2: &Counter,
213        ) {
214            c1.add();
215            sign_of(cx, r.clone(), c2.clone());
216        }
217        log();
218
219        let storage = Storage::new();
220
221        // note: 2000 is here twice since initvalue is actuall run three times in total.
222        // * once for some_other_query's first invocation
223        // * once to figure out that some_other_query's second invocation
224        // * once to actually run `sign_of` a second time after we figured out that we have to.
225        // TODO: can this be optimized?
226        let order = ReturnInOrder::new(vec![1000, 2000, 2000], 1);
227        let counter1 = Counter::new(1);
228        let counter2 = Counter::new(2);
229        let cx = Context::new(&storage);
230
231        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
232        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
233
234        assert!(order.is_empty());
235        assert_eq!(counter1.get(), 1);
236        assert_eq!(counter2.get(), 2);
237    }
238
239    #[test]
240    fn test_intvalue_generational() {
241        #[query]
242        #[rerun(generation)]
243        fn intvalue<'cx>(_cx: &Context<'cx>, r: &ReturnInOrder<i64>) -> i64 {
244            r.next()
245        }
246
247        #[query]
248        fn sign_of<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<i64>, c2: &Counter) -> bool {
249            let v = intvalue(cx, r.clone());
250            c2.add();
251
252            v.is_positive()
253        }
254
255        #[query]
256        fn some_other_query<'cx>(
257            cx: &Context<'cx>,
258            r: &ReturnInOrder<i64>,
259            c1: &Counter,
260            c2: &Counter,
261        ) {
262            c1.add();
263            sign_of(cx, r.clone(), c2.clone());
264        }
265        log();
266
267        let storage = Storage::new();
268
269        // now 2000 is there only once because the query is generational, and the first run in the
270        // 2nd generation can be cached.
271        let order = ReturnInOrder::new(vec![1000, 2000], 1);
272        let counter1 = Counter::new(1);
273        let counter2 = Counter::new(2);
274        let cx = Context::new(&storage);
275
276        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
277        cx.next_generation();
278        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
279
280        assert!(order.is_empty());
281        assert_eq!(counter1.get(), 1);
282        assert_eq!(counter2.get(), 2);
283    }
284
285    #[test]
286    fn test_intvalue_many_generation() {
287        #[query]
288        #[rerun(generation)]
289        fn intvalue<'cx>(_cx: &Context<'cx>, r: &ReturnInOrder<i64>) -> i64 {
290            r.next()
291        }
292
293        #[query]
294        fn sign_of<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<i64>, c2: &Counter) -> bool {
295            let v = intvalue(cx, r.clone());
296            c2.add();
297
298            v.is_positive()
299        }
300
301        #[query]
302        fn some_other_query<'cx>(
303            cx: &Context<'cx>,
304            r: &ReturnInOrder<i64>,
305            c1: &Counter,
306            c2: &Counter,
307        ) {
308            c1.add();
309            sign_of(cx, r.clone(), c2.clone());
310        }
311        log();
312
313        let storage = Storage::new();
314
315        // now 2000 is there only once because the query is generational, and the first run in the
316        // 2nd generation can be cached.
317        let order = ReturnInOrder::new(vec![1000, 2000, 3000], 1);
318        let counter1 = Counter::new(1);
319        let counter2 = Counter::new(2);
320        let cx = Context::new(&storage);
321
322        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
323        cx.next_generation();
324        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
325        cx.next_generation();
326        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
327
328        assert!(order.is_empty());
329        assert_eq!(counter1.get(), 1);
330        assert_eq!(counter2.get(), 3);
331    }
332
333    #[test]
334    fn test_intvalue_assume_pure_wrong() {
335        #[query]
336        fn intvalue<'cx>(_cx: &Context<'cx>, r: &ReturnInOrder<i64>) -> i64 {
337            r.next()
338        }
339
340        #[query]
341        fn sign_of<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<i64>, c2: &Counter) -> bool {
342            let v = intvalue(cx, r.clone());
343            c2.add();
344
345            v.is_positive()
346        }
347
348        #[query]
349        fn some_other_query<'cx>(
350            cx: &Context<'cx>,
351            r: &ReturnInOrder<i64>,
352            c1: &Counter,
353            c2: &Counter,
354        ) {
355            c1.add();
356            sign_of(cx, r.clone(), c2.clone());
357        }
358        log();
359
360        let storage = Storage::new();
361
362        // now 2000 is there only once because the query is generational, and the first run in the
363        // 2nd generation can be cached.
364        let order = ReturnInOrder::new(vec![1000, 2000], 1);
365        let counter1 = Counter::new(1);
366        let counter2 = Counter::new(2);
367        let cx = Context::new(&storage);
368
369        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
370        some_other_query(&cx, order.clone(), counter1.clone(), counter2.clone());
371
372        assert!(!order.is_empty());
373        assert_eq!(counter1.get(), 1);
374        assert_eq!(counter2.get(), 1);
375    }
376
377    #[test]
378    fn conditional_query() {
379        #[query]
380        #[rerun(generation)]
381        fn boolean_query<'cx>(_cx: &Context<'cx>, r: &ReturnInOrder<bool>) -> bool {
382            r.next()
383        }
384
385        #[query]
386        fn one<'cx>(_cx: &Context<'cx>) -> u64 {
387            1
388        }
389        #[query]
390        fn two<'cx>(_cx: &Context<'cx>, c: &Counter) -> u64 {
391            c.add();
392            2
393        }
394
395        #[query]
396        fn conditional<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<bool>, c: &Counter) -> u64 {
397            if *boolean_query(cx, r.clone()) {
398                *one(cx)
399            } else {
400                *two(cx, c.clone())
401            }
402        }
403
404        log();
405
406        let storage = Storage::new();
407        let order = ReturnInOrder::new(vec![true, false], 1);
408        let counter = Counter::new(0);
409        let cx = Context::new(&storage);
410
411        assert_eq!(conditional(&cx, order.clone(), counter.clone()), &1);
412        cx.next_generation();
413        assert_eq!(conditional(&cx, order.clone(), counter.clone()), &2);
414        assert_eq!(conditional(&cx, order.clone(), counter.clone()), &2);
415        assert_eq!(conditional(&cx, order.clone(), counter.clone()), &2);
416        assert_eq!(counter.get(), 1);
417        assert!(order.is_empty())
418    }
419
420    #[test]
421    #[should_panic]
422    fn cycle() {
423        #[query]
424        fn cyclic<'cx>(cx: &Context<'cx>, r: &u64) -> bool {
425            *cyclic(cx, *r)
426        }
427
428        log();
429
430        let storage = Storage::new();
431        let cx = Context::new(&storage);
432        cyclic(&cx, 10);
433    }
434
435    #[test]
436    #[should_panic]
437    fn long_cycle() {
438        #[query]
439        fn e<'cx>(cx: &Context<'cx, i64>, r: &u64) -> bool {
440            *a(cx, *r)
441        }
442
443        #[query]
444        fn d<'cx>(cx: &Context<'cx, i64>, r: &u64) -> bool {
445            *e(cx, *r)
446        }
447
448        #[query]
449        fn c<'cx>(cx: &Context<'cx, i64>, r: &u64) -> bool {
450            *d(cx, *r)
451        }
452
453        #[query]
454        fn b<'cx>(cx: &Context<'cx, i64>, r: &u64) -> bool {
455            *c(cx, *r)
456        }
457
458        #[query]
459        fn a<'cx>(cx: &Context<'cx, i64>, r: &u64) -> bool {
460            *b(cx, *r)
461        }
462        log();
463
464        let storage = Storage::new();
465        let cx = Context::with_data(&storage, 12);
466        a(&cx, 10);
467        assert_eq!(cx.deref(), &12);
468    }
469
470    #[test]
471    fn garbage_collect() {
472        #[query]
473        fn value_dependent<'cx>(_cx: &Context<'cx>, r: &i64) -> i64 {
474            r * 2
475        }
476
477        #[query]
478        #[rerun(generation)]
479        fn intvalue<'cx>(_cx: &Context<'cx>, r: &ReturnInOrder<i64>) -> i64 {
480            r.next()
481        }
482
483        #[query]
484        fn sign_of<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<i64>) -> bool {
485            let v = intvalue(cx, r.clone());
486            value_dependent(cx, *v).is_positive()
487        }
488
489        #[query]
490        fn some_other_query<'cx>(cx: &Context<'cx>, r: &ReturnInOrder<i64>) {
491            sign_of(cx, r.clone());
492        }
493        log();
494
495        const N: i64 = 1000;
496
497        let storage = Storage::new();
498
499        let mut data = Vec::new();
500        for i in 0..N {
501            data.push(i * 1000);
502        }
503
504        // now 2000 is there only once because the query is generational, and the first run in the
505        // 2nd generation can be cached.
506        let order = ReturnInOrder::new(data, 1);
507        let cx = Context::new(&storage);
508
509        for _ in 0..N {
510            some_other_query(&cx, order.clone());
511            cx.next_generation();
512        }
513        assert!(order.is_empty());
514
515        let new_storage = Storage::new();
516
517        tracing::info!("{}", cx.size());
518        let res = cx.gc(&new_storage);
519        drop(storage);
520        tracing::info!("{}", res.size());
521    }
522
523    #[test]
524    fn impure_cache_rerun() {
525        #[query]
526        #[rerun(always)]
527        fn random<'cx>(_cx: &Context<'cx>) -> u64 {
528            thread_rng().gen_range(0..u64::MAX)
529        }
530
531        #[query]
532        fn depends_on_impure<'cx>(cx: &Context<'cx>, inp: &Counter) {
533            inp.add();
534            let _dep = random(&cx);
535        }
536
537        log();
538
539        let storage = Storage::new();
540
541        let ctr = Counter::new(0);
542        let cx = Context::new(&storage);
543        depends_on_impure(&cx, ctr.clone());
544        depends_on_impure(&cx, ctr.clone());
545        depends_on_impure(&cx, ctr.clone());
546        depends_on_impure(&cx, ctr.clone());
547        depends_on_impure(&cx, ctr.clone());
548
549        assert_eq!(ctr.get(), 5);
550    }
551
552    #[test]
553    fn cache_off() {
554        /// Look! this query is even documented!
555        #[query]
556        #[inline(always)] // that's not rerun!
557        fn random<'cx>(_cx: &Context<'cx>) -> u64 {
558            thread_rng().gen_range(0..u64::MAX)
559        }
560
561        #[query]
562        fn depends_on_impure<'cx>(cx: &Context<'cx>, inp: &Counter) {
563            inp.add();
564            let _dep = random(cx);
565        }
566
567        log();
568
569        let storage = Storage::new();
570
571        let ctr = Counter::new(0);
572        let cx = Context::new(&storage);
573        cx.set_cache_enabled(false);
574        depends_on_impure(&cx, ctr.clone());
575        depends_on_impure(&cx, ctr.clone());
576        depends_on_impure(&cx, ctr.clone());
577        depends_on_impure(&cx, ctr.clone());
578        depends_on_impure(&cx, ctr.clone());
579
580        assert_eq!(ctr.get(), 5);
581
582        let ctr = Counter::new(0);
583        let cx = Context::new(&storage);
584        cx.set_cache_enabled(true);
585        depends_on_impure(&cx, ctr.clone());
586        depends_on_impure(&cx, ctr.clone());
587        depends_on_impure(&cx, ctr.clone());
588        depends_on_impure(&cx, ctr.clone());
589        depends_on_impure(&cx, ctr.clone());
590
591        assert_eq!(ctr.get(), 1);
592    }
593}