1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::convert::TryInto;
4use std::ops::DerefMut;
5use std::sync::Arc;
6use std::time::Duration;
7
8use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn, ARCacheWriteTxn};
9use concread::cowcell::*;
10use hashbrown::HashMap;
11use idlset::v2::IDLBitRange;
12use idlset::AndNot;
13use kanidm_proto::internal::{ConsistencyError, OperationError};
14use tracing::trace;
15use uuid::Uuid;
16
17use crate::be::idl_sqlite::{
18    IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction,
19};
20use crate::be::idxkey::{
21    IdlCacheKey, IdlCacheKeyRef, IdlCacheKeyToRef, IdxKey, IdxKeyRef, IdxKeyToRef, IdxNameKey,
22    IdxSlope,
23};
24use crate::be::keystorage::{KeyHandle, KeyHandleId};
25use crate::be::{BackendConfig, IdList, IdRawEntry};
26use crate::entry::{Entry, EntryCommitted, EntrySealed};
27use crate::prelude::*;
28use crate::value::{IndexType, Value};
29
30const DEFAULT_CACHE_TARGET: usize = 2048;
34const DEFAULT_IDL_CACHE_RATIO: usize = 32;
35const DEFAULT_NAME_CACHE_RATIO: usize = 8;
36const DEFAULT_CACHE_RMISS: usize = 0;
37const DEFAULT_CACHE_WMISS: usize = 0;
38
39const DEFAULT_IDX_CACHE_RMISS: usize = 8;
40const DEFAULT_IDX_CACHE_WMISS: usize = 16;
41const DEFAULT_IDX_EXISTS_TARGET: usize = 256;
42
43#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
44enum NameCacheKey {
45    Name2Uuid(String),
46    ExternalId2Uuid(String),
47    Uuid2Rdn(Uuid),
48    Uuid2Spn(Uuid),
49}
50
51#[derive(Debug, Clone)]
52enum NameCacheValue {
53    U(Uuid),
54    R(String),
55    S(Box<Value>),
56}
57
58pub struct IdlArcSqlite {
59    db: IdlSqlite,
60    entry_cache: ARCache<u64, Arc<EntrySealedCommitted>>,
61    idl_cache: ARCache<IdlCacheKey, Box<IDLBitRange>>,
62    name_cache: ARCache<NameCacheKey, NameCacheValue>,
63
64    idx_exists_cache: ARCache<IdxNameKey, bool>,
65
66    op_ts_max: CowCell<Option<Duration>>,
67    allids: CowCell<IDLBitRange>,
68    maxid: CowCell<u64>,
69    keyhandles: CowCell<HashMap<KeyHandleId, KeyHandle>>,
70}
71
72pub struct IdlArcSqliteReadTransaction<'a> {
73    db: IdlSqliteReadTransaction,
74    entry_cache: ARCacheReadTxn<'a, u64, Arc<EntrySealedCommitted>, ()>,
75    idl_cache: ARCacheReadTxn<'a, IdlCacheKey, Box<IDLBitRange>, ()>,
76    name_cache: ARCacheReadTxn<'a, NameCacheKey, NameCacheValue, ()>,
77
78    idx_exists_cache: ARCacheReadTxn<'a, IdxNameKey, bool, ()>,
79    allids: CowCellReadTxn<IDLBitRange>,
80}
81
82pub struct IdlArcSqliteWriteTransaction<'a> {
83    pub(super) db: IdlSqliteWriteTransaction,
84    entry_cache: ARCacheWriteTxn<'a, u64, Arc<EntrySealedCommitted>, ()>,
85    idl_cache: ARCacheWriteTxn<'a, IdlCacheKey, Box<IDLBitRange>, ()>,
86    name_cache: ARCacheWriteTxn<'a, NameCacheKey, NameCacheValue, ()>,
87
88    idx_exists_cache: ARCacheWriteTxn<'a, IdxNameKey, bool, ()>,
89
90    op_ts_max: CowCellWriteTxn<'a, Option<Duration>>,
91    allids: CowCellWriteTxn<'a, IDLBitRange>,
92    maxid: CowCellWriteTxn<'a, u64>,
93    pub(super) keyhandles: CowCellWriteTxn<'a, HashMap<KeyHandleId, KeyHandle>>,
94}
95
96macro_rules! get_identry {
97    (
98        $self:expr,
99        $idl:expr,
100        $is_read_op:expr
101    ) => {{
102        let mut result: Vec<Arc<EntrySealedCommitted>> = Vec::with_capacity(0);
103        match $idl {
104            IdList::Partial(idli) | IdList::PartialThreshold(idli) | IdList::Indexed(idli) => {
105                let mut nidl = IDLBitRange::new();
106
107                idli.into_iter().for_each(|i| {
108                    match $self.entry_cache.get(&i) {
111                        Some(eref) => result.push(eref.clone()),
112                        None => unsafe { nidl.push_id(i) },
113                    }
114                });
115
116                if !nidl.is_empty() {
117                    let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
119                    if $is_read_op {
121                        db_result.iter().for_each(|e| {
122                            $self.entry_cache.insert(e.get_id(), e.clone());
123                        });
124                    }
125                    result.append(&mut db_result);
127                }
128            }
129            IdList::AllIds => {
130                let idli = (*$self.allids).clone();
133                let mut nidl = IDLBitRange::new();
134
135                (&idli)
136                    .into_iter()
137                    .for_each(|i| match $self.entry_cache.get(&i) {
138                        Some(eref) => result.push(eref.clone()),
139                        None => unsafe { nidl.push_id(i) },
140                    });
141
142                if !nidl.is_empty() {
143                    let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
145                    result.append(&mut db_result);
147                }
148            }
149        };
150        Ok(result)
152    }};
153}
154
155macro_rules! get_identry_raw {
156    (
157        $self:expr,
158        $idl:expr
159    ) => {{
160        $self.db.get_identry_raw($idl)
162    }};
163}
164
165macro_rules! get_idl {
166    (
167        $self:expr,
168        $attr:expr,
169        $itype:expr,
170        $idx_key:expr
171    ) => {{
172        let cache_key = IdlCacheKeyRef {
186            a: $attr,
187            i: $itype,
188            k: $idx_key,
189        };
190        let cache_r = $self.idl_cache.get(&cache_key as &dyn IdlCacheKeyToRef);
191        if let Some(ref data) = cache_r {
193            trace!(
194                cached_index = ?$itype,
195                attr = ?$attr,
196                idl = %data,
197            );
198            return Ok(Some(data.as_ref().clone()));
199        }
200
201        let idx_key = IdxNameKey {
203            a: $attr.clone(),
204            i: $itype,
205        };
206        let idx_r = $self.idx_exists_cache.get(&idx_key);
207        if idx_r == Some(&false) {
208            return Ok(None)
210        }
211
212        let db_r = $self.db.get_idl($attr, $itype, $idx_key)?;
217
218        if let Some(ref idl) = db_r {
219            if idx_r == None {
220                $self.idx_exists_cache.insert(idx_key, true)
223            }
224
225            let ncache_key = IdlCacheKey {
226                a: $attr.clone(),
227                i: $itype.clone(),
228                k: $idx_key.into(),
229            };
230            $self.idl_cache.insert(ncache_key, Box::new(idl.clone()))
231        } else {
232            $self.idx_exists_cache.insert(idx_key, false)
240        };
241        Ok(db_r)
242    }};
243}
244
245macro_rules! name2uuid {
246    (
247        $self:expr,
248        $name:expr
249    ) => {{
250        let cache_key = NameCacheKey::Name2Uuid($name.to_string());
251        let cache_r = $self.name_cache.get(&cache_key);
252        if let Some(NameCacheValue::U(uuid)) = cache_r {
253            trace!(?uuid, "Got cached name2uuid");
254            return Ok(Some(uuid.clone()));
255        } else {
256            trace!("Cache miss uuid for name2uuid");
257        }
258
259        let db_r = $self.db.name2uuid($name)?;
260        if let Some(uuid) = db_r {
261            $self
262                .name_cache
263                .insert(cache_key, NameCacheValue::U(uuid.clone()))
264        }
265        Ok(db_r)
266    }};
267}
268
269macro_rules! externalid2uuid {
270    (
271        $self:expr,
272        $name:expr
273    ) => {{
274        let cache_key = NameCacheKey::ExternalId2Uuid($name.to_string());
275        let cache_r = $self.name_cache.get(&cache_key);
276        if let Some(NameCacheValue::U(uuid)) = cache_r {
277            trace!(?uuid, "Got cached externalid2uuid");
278            return Ok(Some(uuid.clone()));
279        } else {
280            trace!("Cache miss uuid for externalid2uuid");
281        }
282
283        let db_r = $self.db.externalid2uuid($name)?;
284        if let Some(uuid) = db_r {
285            $self
286                .name_cache
287                .insert(cache_key, NameCacheValue::U(uuid.clone()))
288        }
289        Ok(db_r)
290    }};
291}
292
293macro_rules! uuid2spn {
294    (
295        $self:expr,
296        $uuid:expr
297    ) => {{
298        let cache_key = NameCacheKey::Uuid2Spn($uuid);
299        let cache_r = $self.name_cache.get(&cache_key);
300        if let Some(NameCacheValue::S(ref spn)) = cache_r {
301            trace!(?spn, "Got cached uuid2spn");
302            return Ok(Some(spn.as_ref().clone()));
303        } else {
304            trace!("Cache miss spn for uuid2spn");
305        }
306
307        let db_r = $self.db.uuid2spn($uuid)?;
308        if let Some(ref data) = db_r {
309            $self
310                .name_cache
311                .insert(cache_key, NameCacheValue::S(Box::new(data.clone())))
312        }
313        Ok(db_r)
314    }};
315}
316
317macro_rules! uuid2rdn {
318    (
319        $self:expr,
320        $uuid:expr
321    ) => {{
322        let cache_key = NameCacheKey::Uuid2Rdn($uuid);
323        let cache_r = $self.name_cache.get(&cache_key);
324        if let Some(NameCacheValue::R(ref rdn)) = cache_r {
325            return Ok(Some(rdn.clone()));
326        } else {
327            trace!("Cache miss rdn for uuid2rdn");
328        }
329
330        let db_r = $self.db.uuid2rdn($uuid)?;
331        if let Some(ref data) = db_r {
332            $self
333                .name_cache
334                .insert(cache_key, NameCacheValue::R(data.clone()))
335        }
336        Ok(db_r)
337    }};
338}
339
340macro_rules! verify {
341    (
342        $self:expr
343    ) => {{
344        let mut r = $self.db.verify();
345        if r.is_empty() && !$self.is_dirty() {
346            match $self.db.get_allids() {
348                Ok(db_allids) => {
349                    if !db_allids.is_compressed() || !(*($self).allids).is_compressed() {
350                        admin_warn!("Inconsistent ALLIDS compression state");
351                        r.push(Err(ConsistencyError::BackendAllIdsSync))
352                    }
353                    if db_allids != (*($self).allids) {
354                        admin_warn!(
356                            db_allids = ?(&db_allids).andnot(&($self).allids),
357                            arc_allids = ?(&(*($self).allids)).andnot(&db_allids),
358                            "Inconsistent ALLIDS set"
359                        );
360                        r.push(Err(ConsistencyError::BackendAllIdsSync))
361                    }
362                }
363                Err(_) => r.push(Err(ConsistencyError::Unknown)),
364            };
365        };
366        r
367    }};
368}
369
370pub trait IdlArcSqliteTransaction {
371    fn get_identry(
372        &mut self,
373        idl: &IdList,
374    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError>;
375
376    fn get_identry_raw(&self, idl: &IdList) -> Result<Vec<IdRawEntry>, OperationError>;
377
378    fn get_idl(
381        &mut self,
382        attr: &Attribute,
383        itype: IndexType,
384        idx_key: &str,
385    ) -> Result<Option<IDLBitRange>, OperationError>;
386
387    fn get_db_s_uuid(&self) -> Result<Option<Uuid>, OperationError>;
388
389    fn get_db_d_uuid(&self) -> Result<Option<Uuid>, OperationError>;
390
391    fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError>;
392
393    fn get_key_handles(&mut self) -> Result<BTreeMap<KeyHandleId, KeyHandle>, OperationError>;
394
395    fn verify(&self) -> Vec<Result<(), ConsistencyError>>;
396
397    fn is_dirty(&self) -> bool;
398
399    fn name2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError>;
400
401    fn externalid2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError>;
402
403    fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError>;
404
405    fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError>;
406
407    fn list_idxs(&self) -> Result<Vec<String>, OperationError>;
408
409    fn list_id2entry(&self) -> Result<Vec<(u64, String)>, OperationError>;
410
411    fn list_quarantined(&self) -> Result<Vec<(u64, String)>, OperationError>;
412
413    fn list_index_content(
414        &self,
415        index_name: &str,
416    ) -> Result<Vec<(String, IDLBitRange)>, OperationError>;
417
418    fn get_id2entry(&self, id: u64) -> Result<(u64, String), OperationError>;
419}
420
421impl IdlArcSqliteTransaction for IdlArcSqliteReadTransaction<'_> {
422    fn get_identry(
423        &mut self,
424        idl: &IdList,
425    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
426        get_identry!(self, idl, true)
427    }
428
429    fn get_identry_raw(&self, idl: &IdList) -> Result<Vec<IdRawEntry>, OperationError> {
430        get_identry_raw!(self, idl)
431    }
432
433    #[instrument(level = "trace", skip_all)]
438    fn get_idl(
439        &mut self,
440        attr: &Attribute,
441        itype: IndexType,
442        idx_key: &str,
443    ) -> Result<Option<IDLBitRange>, OperationError> {
444        get_idl!(self, attr, itype, idx_key)
445    }
446
447    fn get_db_s_uuid(&self) -> Result<Option<Uuid>, OperationError> {
448        self.db.get_db_s_uuid()
449    }
450
451    fn get_db_d_uuid(&self) -> Result<Option<Uuid>, OperationError> {
452        self.db.get_db_d_uuid()
453    }
454
455    fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
456        self.db.get_db_ts_max()
457    }
458
459    fn get_key_handles(&mut self) -> Result<BTreeMap<KeyHandleId, KeyHandle>, OperationError> {
460        self.db.get_key_handles()
461    }
462
463    fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
464        verify!(self)
465    }
466
467    fn is_dirty(&self) -> bool {
468        false
469    }
470
471    fn name2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError> {
472        name2uuid!(self, name)
473    }
474
475    fn externalid2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError> {
476        externalid2uuid!(self, name)
477    }
478
479    fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
480        uuid2spn!(self, uuid)
481    }
482
483    fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
484        uuid2rdn!(self, uuid)
485    }
486
487    fn list_idxs(&self) -> Result<Vec<String>, OperationError> {
488        self.db.list_idxs()
490    }
491
492    fn list_id2entry(&self) -> Result<Vec<(u64, String)>, OperationError> {
493        self.db.list_id2entry()
495    }
496
497    fn list_quarantined(&self) -> Result<Vec<(u64, String)>, OperationError> {
498        self.db.list_quarantined()
500    }
501
502    fn list_index_content(
503        &self,
504        index_name: &str,
505    ) -> Result<Vec<(String, IDLBitRange)>, OperationError> {
506        self.db.list_index_content(index_name)
508    }
509
510    fn get_id2entry(&self, id: u64) -> Result<(u64, String), OperationError> {
511        self.db.get_id2entry(id)
513    }
514}
515
516impl IdlArcSqliteTransaction for IdlArcSqliteWriteTransaction<'_> {
517    fn get_identry(
518        &mut self,
519        idl: &IdList,
520    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
521        get_identry!(self, idl, false)
522    }
523
524    fn get_identry_raw(&self, idl: &IdList) -> Result<Vec<IdRawEntry>, OperationError> {
525        get_identry_raw!(self, idl)
526    }
527
528    #[instrument(level = "trace", skip_all)]
533    fn get_idl(
534        &mut self,
535        attr: &Attribute,
536        itype: IndexType,
537        idx_key: &str,
538    ) -> Result<Option<IDLBitRange>, OperationError> {
539        get_idl!(self, attr, itype, idx_key)
540    }
541
542    fn get_db_s_uuid(&self) -> Result<Option<Uuid>, OperationError> {
543        self.db.get_db_s_uuid()
544    }
545
546    fn get_db_d_uuid(&self) -> Result<Option<Uuid>, OperationError> {
547        self.db.get_db_d_uuid()
548    }
549
550    fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
551        match *self.op_ts_max {
552            Some(ts) => Ok(Some(ts)),
553            None => self.db.get_db_ts_max(),
554        }
555    }
556
557    fn get_key_handles(&mut self) -> Result<BTreeMap<KeyHandleId, KeyHandle>, OperationError> {
558        self.db.get_key_handles()
559    }
560
561    fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
562        verify!(self)
563    }
564
565    fn is_dirty(&self) -> bool {
566        self.entry_cache.is_dirty()
567    }
568
569    fn name2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError> {
570        name2uuid!(self, name)
571    }
572
573    fn externalid2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError> {
574        externalid2uuid!(self, name)
575    }
576
577    fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
578        uuid2spn!(self, uuid)
579    }
580
581    fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
582        uuid2rdn!(self, uuid)
583    }
584
585    fn list_idxs(&self) -> Result<Vec<String>, OperationError> {
586        self.db.list_idxs()
588    }
589
590    fn list_id2entry(&self) -> Result<Vec<(u64, String)>, OperationError> {
591        self.db.list_id2entry()
593    }
594
595    fn list_quarantined(&self) -> Result<Vec<(u64, String)>, OperationError> {
596        self.db.list_quarantined()
598    }
599
600    fn list_index_content(
601        &self,
602        index_name: &str,
603    ) -> Result<Vec<(String, IDLBitRange)>, OperationError> {
604        self.db.list_index_content(index_name)
606    }
607
608    fn get_id2entry(&self, id: u64) -> Result<(u64, String), OperationError> {
609        self.db.get_id2entry(id)
611    }
612}
613
614impl IdlArcSqliteWriteTransaction<'_> {
615    #[cfg(any(test, debug_assertions))]
616    #[instrument(level = "debug", name = "idl_arc_sqlite::clear_cache", skip_all)]
617    pub fn clear_cache(&mut self) -> Result<(), OperationError> {
618        self.entry_cache.clear();
628        self.idl_cache.clear();
629        self.idx_exists_cache.clear();
630        self.name_cache.clear();
631        Ok(())
632    }
633
634    #[instrument(level = "debug", name = "idl_arc_sqlite::commit", skip_all)]
635    pub fn commit(self) -> Result<(), OperationError> {
636        let IdlArcSqliteWriteTransaction {
637            db,
638            mut entry_cache,
639            mut idl_cache,
640            mut name_cache,
641            idx_exists_cache,
642            op_ts_max,
643            allids,
644            maxid,
645            keyhandles,
646        } = self;
647
648        entry_cache
650            .iter_mut_mark_clean()
651            .try_for_each(|(k, v)| match v {
652                Some(e) => db.write_identry(e),
653                None => db.delete_identry(*k),
654            })
655            .map_err(|e| {
656                admin_error!(?e, "Failed to sync entry cache to sqlite");
657                e
658            })?;
659
660        idl_cache
661            .iter_mut_mark_clean()
662            .try_for_each(|(k, v)| {
663                match v {
664                    Some(idl) => db.write_idl(&k.a, k.i, k.k.as_str(), idl),
665                    #[allow(clippy::unreachable)]
666                    None => {
667                        unreachable!();
674                    }
675                }
676            })
677            .map_err(|e| {
678                admin_error!(?e, "Failed to sync idl cache to sqlite");
679                e
680            })?;
681
682        name_cache
683            .iter_mut_mark_clean()
684            .try_for_each(|(k, v)| match (k, v) {
685                (NameCacheKey::Name2Uuid(k), Some(NameCacheValue::U(v))) => {
686                    db.write_name2uuid_add(k, *v)
687                }
688                (NameCacheKey::Name2Uuid(k), None) => db.write_name2uuid_rem(k),
689                (NameCacheKey::ExternalId2Uuid(k), Some(NameCacheValue::U(v))) => {
690                    db.write_externalid2uuid_add(k, *v)
691                }
692                (NameCacheKey::ExternalId2Uuid(k), None) => db.write_externalid2uuid_rem(k),
693                (NameCacheKey::Uuid2Spn(uuid), Some(NameCacheValue::S(v))) => {
694                    db.write_uuid2spn(*uuid, Some(v))
695                }
696                (NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(*uuid, None),
697                (NameCacheKey::Uuid2Rdn(uuid), Some(NameCacheValue::R(v))) => {
698                    db.write_uuid2rdn(*uuid, Some(v))
699                }
700                (NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(*uuid, None),
701
702                _ => Err(OperationError::InvalidCacheState),
703            })
704            .map_err(|e| {
705                admin_error!(?e, "Failed to sync name cache to sqlite");
706                e
707            })?;
708
709        db.commit()?;
711
712        op_ts_max.commit();
714        name_cache.commit();
715        idx_exists_cache.commit();
716        idl_cache.commit();
717        allids.commit();
718        maxid.commit();
719        keyhandles.commit();
720        entry_cache.commit();
722
723        Ok(())
724    }
725
726    pub fn get_db_ruv(&self) -> Result<BTreeSet<Cid>, OperationError> {
727        self.db.get_db_ruv()
728    }
729
730    pub fn write_db_ruv<I, J>(&mut self, added: I, removed: J) -> Result<(), OperationError>
731    where
732        I: Iterator<Item = Cid>,
733        J: Iterator<Item = Cid>,
734    {
735        self.db.write_db_ruv(added, removed)
736    }
737
738    pub fn get_id2entry_max_id(&self) -> Result<u64, OperationError> {
739        Ok(*self.maxid)
740    }
741
742    pub fn set_id2entry_max_id(&mut self, mid: u64) {
743        assert!(mid > *self.maxid);
744        *self.maxid = mid;
745    }
746
747    #[instrument(level = "trace", skip_all)]
748    pub fn write_identries<'b, I>(&'b mut self, mut entries: I) -> Result<(), OperationError>
749    where
750        I: Iterator<Item = &'b Entry<EntrySealed, EntryCommitted>>,
751    {
752        entries.try_for_each(|e| {
753            trace!("Inserting {:?} to cache", e.get_id());
754            if e.get_id() == 0 {
755                Err(OperationError::InvalidEntryId)
756            } else {
757                (*self.allids).insert_id(e.get_id());
758                self.entry_cache
759                    .insert_dirty(e.get_id(), Arc::new(e.clone()));
760                Ok(())
761            }
762        })
763    }
764
765    pub fn write_identries_raw<I>(&mut self, entries: I) -> Result<(), OperationError>
766    where
767        I: Iterator<Item = IdRawEntry>,
768    {
769        self.entry_cache.clear();
771        self.db
773            .write_identries_raw(entries)
774            .and_then(|()| self.db.get_allids())
775            .map(|mut ids| {
776                std::mem::swap(self.allids.deref_mut(), &mut ids);
778            })
779    }
780
781    pub fn delete_identry<I>(&mut self, mut idl: I) -> Result<(), OperationError>
782    where
783        I: Iterator<Item = u64>,
784    {
785        idl.try_for_each(|i| {
786            trace!("Removing {:?} from cache", i);
787            if i == 0 {
788                Err(OperationError::InvalidEntryId)
789            } else {
790                (*self.allids).remove_id(i);
791                self.entry_cache.remove_dirty(i);
792                Ok(())
793            }
794        })
795    }
796
797    #[instrument(level = "trace", skip_all)]
798    pub fn write_idl(
799        &mut self,
800        attr: &Attribute,
801        itype: IndexType,
802        idx_key: &str,
803        idl: &IDLBitRange,
804    ) -> Result<(), OperationError> {
805        let cache_key = IdlCacheKey {
806            a: attr.clone(),
807            i: itype,
808            k: idx_key.into(),
809        };
810        if idl.is_empty() {
814            self.idl_cache
815                .insert_dirty(cache_key, Box::new(IDLBitRange::new()));
816        } else {
817            self.idl_cache
818                .insert_dirty(cache_key, Box::new(idl.clone()));
819        }
820        Ok(())
822    }
823
824    pub fn optimise_dirty_idls(&mut self) {
825        self.idl_cache.iter_mut_dirty().for_each(|(k, maybe_idl)| {
826            if let Some(idl) = maybe_idl {
827                if idl.maybe_compress() {
828                    trace!(?k, "Compressed idl");
829                }
830            }
831        })
832    }
833
834    pub fn is_idx_slopeyness_generated(&self) -> Result<bool, OperationError> {
835        self.db.is_idx_slopeyness_generated()
836    }
837
838    pub fn get_idx_slope(&self, ikey: &IdxKey) -> Result<Option<IdxSlope>, OperationError> {
839        self.db.get_idx_slope(ikey)
840    }
841
842    pub fn analyse_idx_slopes(&mut self) -> Result<(), OperationError> {
850        let mut data: HashMap<IdxKey, Vec<f64>> = HashMap::new();
890        self.idl_cache.iter_dirty().for_each(|(k, maybe_idl)| {
891            if let Some(idl) = maybe_idl {
892                let idl_len: u32 = idl.len().try_into().unwrap_or(u32::MAX);
893                let idl_len = f64::from(idl_len);
895
896                let kref = IdxKeyRef::new(&k.a, &k.i);
897                if idl_len > 0.0 {
898                    if let Some(lens) = data.get_mut(&kref as &dyn IdxKeyToRef) {
900                        lens.push(idl_len)
901                    } else {
902                        data.insert(kref.as_key(), vec![idl_len]);
903                    }
904                }
905            }
906        });
907
908        let slopes: HashMap<_, _> = data
1020            .into_iter()
1021            .filter_map(|(k, lens)| {
1022                let slope_factor = Self::calculate_sd_slope(&lens);
1023                if slope_factor == 0 || slope_factor == IdxSlope::MAX {
1024                    None
1025                } else {
1026                    Some((k, slope_factor))
1027                }
1028            })
1029            .collect();
1030        trace!(?slopes, "Generated slopes");
1031        self.db.store_idx_slope_analysis(&slopes)
1033    }
1034
1035    fn calculate_sd_slope(data: &[f64]) -> IdxSlope {
1036        let (n_keys, sd_1) = if data.len() >= 2 {
1037            let l: u32 = data.len().try_into().unwrap_or(u32::MAX);
1039            let c = f64::from(l);
1040            let mean = data.iter().take(u32::MAX as usize).sum::<f64>() / c;
1041            let variance: f64 = data
1042                .iter()
1043                .take(u32::MAX as usize)
1044                .map(|len| {
1045                    let delta = mean - len;
1046                    delta * delta
1047                })
1048                .sum::<f64>()
1049                / (c - 1.0);
1050
1051            let sd = variance.sqrt();
1052
1053            let sd_1 = mean + sd;
1055            (c, sd_1)
1056        } else if data.len() == 1 {
1057            (1.0, data[0])
1058        } else {
1059            return IdxSlope::MAX;
1061        };
1062
1063        let sf = (sd_1 / n_keys).atan().to_degrees() * 2.8;
1069
1070        let sf = sf.clamp(1.0, 254.0);
1073        if !sf.is_finite() {
1074            IdxSlope::MAX
1075        } else {
1076            unsafe { sf.to_int_unchecked::<IdxSlope>() }
1080        }
1081    }
1082
1083    pub fn quarantine_entry(&self, id: u64) -> Result<(), OperationError> {
1084        self.db.quarantine_entry(id)
1085    }
1086
1087    pub fn restore_quarantined(&self, id: u64) -> Result<(), OperationError> {
1088        self.db.restore_quarantined(id)
1089    }
1090
1091    pub fn create_name2uuid(&self) -> Result<(), OperationError> {
1092        self.db.create_name2uuid()
1093    }
1094
1095    pub fn write_name2uuid_add(
1096        &mut self,
1097        uuid: Uuid,
1098        add: BTreeSet<String>,
1099    ) -> Result<(), OperationError> {
1100        add.into_iter().for_each(|k| {
1101            let cache_key = NameCacheKey::Name2Uuid(k);
1102            let cache_value = NameCacheValue::U(uuid);
1103            self.name_cache.insert_dirty(cache_key, cache_value)
1104        });
1105        Ok(())
1106    }
1107
1108    pub fn write_name2uuid_rem(&mut self, rem: BTreeSet<String>) -> Result<(), OperationError> {
1109        rem.into_iter().for_each(|k| {
1110            let cache_key = NameCacheKey::Name2Uuid(k);
1112            self.name_cache.remove_dirty(cache_key)
1113        });
1114        Ok(())
1115    }
1116
1117    pub fn create_externalid2uuid(&self) -> Result<(), OperationError> {
1118        self.db.create_externalid2uuid()
1119    }
1120
1121    pub fn write_externalid2uuid_add(
1122        &mut self,
1123        uuid: Uuid,
1124        add: String,
1125    ) -> Result<(), OperationError> {
1126        let cache_key = NameCacheKey::ExternalId2Uuid(add);
1127        let cache_value = NameCacheValue::U(uuid);
1128        self.name_cache.insert_dirty(cache_key, cache_value);
1129        Ok(())
1130    }
1131
1132    pub fn write_externalid2uuid_rem(&mut self, rem: String) -> Result<(), OperationError> {
1133        let cache_key = NameCacheKey::ExternalId2Uuid(rem);
1134        self.name_cache.remove_dirty(cache_key);
1135        Ok(())
1136    }
1137
1138    pub fn create_uuid2spn(&self) -> Result<(), OperationError> {
1139        self.db.create_uuid2spn()
1140    }
1141
1142    pub fn write_uuid2spn(&mut self, uuid: Uuid, k: Option<Value>) -> Result<(), OperationError> {
1143        let cache_key = NameCacheKey::Uuid2Spn(uuid);
1144        match k {
1145            Some(v) => self
1146                .name_cache
1147                .insert_dirty(cache_key, NameCacheValue::S(Box::new(v))),
1148            None => self.name_cache.remove_dirty(cache_key),
1149        }
1150        Ok(())
1151    }
1152
1153    pub fn create_uuid2rdn(&self) -> Result<(), OperationError> {
1154        self.db.create_uuid2rdn()
1155    }
1156
1157    pub fn write_uuid2rdn(&mut self, uuid: Uuid, k: Option<String>) -> Result<(), OperationError> {
1158        let cache_key = NameCacheKey::Uuid2Rdn(uuid);
1159        match k {
1160            Some(s) => self
1161                .name_cache
1162                .insert_dirty(cache_key, NameCacheValue::R(s)),
1163            None => self.name_cache.remove_dirty(cache_key),
1164        }
1165        Ok(())
1166    }
1167
1168    pub fn create_idx(&mut self, attr: &Attribute, itype: IndexType) -> Result<(), OperationError> {
1169        self.db.create_idx(attr, itype)?;
1173
1174        let idx_key = IdxNameKey {
1176            a: attr.clone(),
1177            i: itype,
1178        };
1179        self.idx_exists_cache.insert(idx_key, true);
1180
1181        Ok(())
1182    }
1183
1184    #[instrument(level = "trace", skip_all)]
1189    pub fn danger_purge_idxs(&mut self) -> Result<(), OperationError> {
1190        debug!("CLEARING CACHE");
1191        self.db.danger_purge_idxs().map(|()| {
1192            self.idl_cache.clear();
1193            self.idx_exists_cache.clear();
1194            self.name_cache.clear();
1195        })
1196    }
1197
1198    #[instrument(level = "trace", skip_all)]
1203    pub fn danger_purge_id2entry(&mut self) -> Result<(), OperationError> {
1204        self.db.danger_purge_id2entry().map(|()| {
1205            let mut ids = IDLBitRange::new();
1206            ids.compress();
1207            std::mem::swap(self.allids.deref_mut(), &mut ids);
1208            self.entry_cache.clear();
1209        })
1210    }
1211
1212    pub fn write_db_s_uuid(&self, nsid: Uuid) -> Result<(), OperationError> {
1213        self.db.write_db_s_uuid(nsid)
1214    }
1215
1216    pub fn write_db_d_uuid(&self, nsid: Uuid) -> Result<(), OperationError> {
1217        self.db.write_db_d_uuid(nsid)
1218    }
1219
1220    pub fn set_db_ts_max(&mut self, ts: Duration) -> Result<(), OperationError> {
1221        *self.op_ts_max = Some(ts);
1222        self.db.set_db_ts_max(ts)
1223    }
1224
1225    pub(crate) fn get_db_index_version(&self) -> Result<i64, OperationError> {
1226        self.db.get_db_index_version()
1227    }
1228
1229    pub(crate) fn set_db_index_version(&self, v: i64) -> Result<(), OperationError> {
1230        self.db.set_db_index_version(v)
1231    }
1232
1233    pub fn setup(&mut self) -> Result<(), OperationError> {
1234        self.db
1235            .setup()
1236            .and_then(|()| self.db.get_allids())
1237            .map(|mut ids| {
1238                std::mem::swap(self.allids.deref_mut(), &mut ids);
1239            })
1240            .and_then(|()| self.db.get_id2entry_max_id())
1241            .map(|mid| {
1242                *self.maxid = mid;
1243            })
1244    }
1245}
1246
1247impl IdlArcSqlite {
1248    pub fn new(cfg: &BackendConfig, vacuum: bool) -> Result<Self, OperationError> {
1249        let db = IdlSqlite::new(cfg, vacuum)?;
1250
1251        let mut cache_size = cfg.arcsize.unwrap_or_else(|| {
1253            db.get_allids_count()
1256                .map(|c| {
1257                    let tmpsize = ((c / 5) as usize) * 6;
1258                    std::cmp::max(tmpsize, DEFAULT_CACHE_TARGET)
1260                })
1261                .unwrap_or(DEFAULT_CACHE_TARGET)
1262        });
1263
1264        if cache_size < DEFAULT_CACHE_TARGET {
1265            admin_warn!(
1266                old = cache_size,
1267                new = DEFAULT_CACHE_TARGET,
1268                "Configured Arc Cache size too low, increasing..."
1269            );
1270            cache_size = DEFAULT_CACHE_TARGET; }
1272
1273        let entry_cache = ARCacheBuilder::new()
1274            .set_expected_workload(
1275                cache_size,
1276                cfg.pool_size as usize,
1277                DEFAULT_CACHE_RMISS,
1278                DEFAULT_CACHE_WMISS,
1279                false,
1280            )
1281            .set_reader_quiesce(true)
1282            .build()
1283            .ok_or_else(|| {
1284                admin_error!("Failed to construct entry_cache");
1285                OperationError::InvalidState
1286            })?;
1287        let idl_cache = ARCacheBuilder::new()
1290            .set_expected_workload(
1291                cache_size * DEFAULT_IDL_CACHE_RATIO,
1292                cfg.pool_size as usize,
1293                DEFAULT_CACHE_RMISS,
1294                DEFAULT_CACHE_WMISS,
1295                false,
1296            )
1297            .set_reader_quiesce(true)
1298            .build()
1299            .ok_or_else(|| {
1300                admin_error!("Failed to construct idl_cache");
1301                OperationError::InvalidState
1302            })?;
1303
1304        let name_cache = ARCacheBuilder::new()
1305            .set_expected_workload(
1306                cache_size * DEFAULT_NAME_CACHE_RATIO,
1307                cfg.pool_size as usize,
1308                DEFAULT_CACHE_RMISS,
1309                DEFAULT_CACHE_WMISS,
1310                true,
1311            )
1312            .set_reader_quiesce(true)
1313            .build()
1314            .ok_or_else(|| {
1315                admin_error!("Failed to construct name_cache");
1316                OperationError::InvalidState
1317            })?;
1318
1319        let idx_exists_cache = ARCacheBuilder::new()
1320            .set_expected_workload(
1321                DEFAULT_IDX_EXISTS_TARGET,
1322                cfg.pool_size as usize,
1323                DEFAULT_IDX_CACHE_RMISS,
1324                DEFAULT_IDX_CACHE_WMISS,
1325                true,
1326            )
1327            .set_reader_quiesce(true)
1328            .build()
1329            .ok_or_else(|| {
1330                admin_error!("Failed to construct idx_exists_cache");
1331                OperationError::InvalidState
1332            })?;
1333
1334        let allids = CowCell::new(IDLBitRange::new());
1335
1336        let maxid = CowCell::new(0);
1337
1338        let keyhandles = CowCell::new(HashMap::default());
1339
1340        let op_ts_max = CowCell::new(None);
1341
1342        Ok(IdlArcSqlite {
1343            db,
1344            entry_cache,
1345            idl_cache,
1346            name_cache,
1347            idx_exists_cache,
1348            op_ts_max,
1349            allids,
1350            maxid,
1351            keyhandles,
1352        })
1353    }
1354
1355    pub fn try_quiesce(&self) {
1356        self.entry_cache.try_quiesce();
1357        self.idl_cache.try_quiesce();
1358        self.name_cache.try_quiesce();
1359    }
1360
1361    pub fn read(&self) -> Result<IdlArcSqliteReadTransaction<'_>, OperationError> {
1362        let entry_cache_read = self.entry_cache.read();
1364        let db_read = self.db.read()?;
1365        let idl_cache_read = self.idl_cache.read();
1366        let name_cache_read = self.name_cache.read();
1367        let idx_exists_cache_read = self.idx_exists_cache.read();
1368        let allids_read = self.allids.read();
1369
1370        Ok(IdlArcSqliteReadTransaction {
1371            db: db_read,
1372            entry_cache: entry_cache_read,
1373            idl_cache: idl_cache_read,
1374            name_cache: name_cache_read,
1375            idx_exists_cache: idx_exists_cache_read,
1376            allids: allids_read,
1377        })
1378    }
1379
1380    pub fn write(&self) -> Result<IdlArcSqliteWriteTransaction<'_>, OperationError> {
1381        let entry_cache_write = self.entry_cache.write();
1383        let db_write = self.db.write()?;
1384        let idl_cache_write = self.idl_cache.write();
1385        let name_cache_write = self.name_cache.write();
1386        let idx_exists_cache_write = self.idx_exists_cache.write();
1387        let op_ts_max_write = self.op_ts_max.write();
1388        let allids_write = self.allids.write();
1389        let maxid_write = self.maxid.write();
1390        let keyhandles_write = self.keyhandles.write();
1391
1392        Ok(IdlArcSqliteWriteTransaction {
1393            db: db_write,
1394            entry_cache: entry_cache_write,
1395            idl_cache: idl_cache_write,
1396            name_cache: name_cache_write,
1397            idx_exists_cache: idx_exists_cache_write,
1398            op_ts_max: op_ts_max_write,
1399            allids: allids_write,
1400            maxid: maxid_write,
1401            keyhandles: keyhandles_write,
1402        })
1403    }
1404
1405    }