tuwunel_database/engine/
context.rs1use std::{
2 collections::BTreeMap,
3 fs::remove_dir_all,
4 path::Path,
5 sync::{Arc, Mutex},
6};
7
8use rocksdb::{Cache, LruCacheOptions};
9use tuwunel_core::{
10 Result, Server, debug,
11 utils::{math::usize_from_f64, result::LogErr},
12};
13
14use super::env::Env;
15use crate::pool::Pool;
16
17pub(crate) struct ColCache {
23 pub(crate) cache: Cache,
24
25 pub(crate) participants: Vec<&'static str>,
26}
27
28pub(crate) struct Context {
34 pub(crate) pool: Arc<Pool>,
35
36 pub(crate) row_cache_capacity: usize,
38
39 pub(crate) row_cache: Mutex<Cache>,
40
41 pub(crate) col_cache: Mutex<ColCaches>,
42
43 pub(crate) server: Arc<Server>,
44
45 pub(super) env: Arc<Env>,
46}
47
48pub(crate) type ColCaches = BTreeMap<&'static str, ColCache>;
51
52pub(crate) const SHARED_POOL: &str = "Shared";
55
56impl Context {
57 pub(crate) fn new(server: &Arc<Server>) -> Result<Arc<Self>> {
58 let config = &server.config;
59 let cache_capacity_bytes = config.db_cache_capacity_mb * 1024.0 * 1024.0;
60
61 let col_cache_shards: i32 = 128;
62 let col_shard_bits = col_cache_shards.ilog2().try_into()?;
63 let col_cache_capacity_bytes = usize_from_f64(cache_capacity_bytes * 0.50)?;
64
65 let row_cache_shards: i32 = 128;
66 let row_shard_bits = row_cache_shards.ilog2().try_into()?;
67 let row_cache_capacity_bytes = usize_from_f64(cache_capacity_bytes * 0.50)?;
68
69 let mut row_cache_opts = LruCacheOptions::default();
70 row_cache_opts.set_num_shard_bits(row_shard_bits);
71 row_cache_opts.set_capacity(row_cache_capacity_bytes);
72 let row_cache = Cache::new_lru_cache_opts(&row_cache_opts);
73
74 let mut col_cache_opts = LruCacheOptions::default();
75 col_cache_opts.set_num_shard_bits(col_shard_bits);
76 col_cache_opts.set_capacity(col_cache_capacity_bytes);
77 let col_cache = Cache::new_lru_cache_opts(&col_cache_opts);
78 let shared = ColCache {
79 cache: col_cache,
80 participants: Vec::new(),
81 };
82 let col_cache: ColCaches = [(SHARED_POOL, shared)].into();
83
84 Ok(Arc::new(Self {
85 pool: Pool::new(server)?,
86 row_cache_capacity: row_cache_capacity_bytes,
87 row_cache: row_cache.into(),
88 col_cache: col_cache.into(),
89 server: server.clone(),
90 env: Env::acquire(server)?,
91 }))
92 }
93}
94
95impl Drop for Context {
96 #[cold]
97 fn drop(&mut self) {
98 debug!("Closing frontend pool");
99 self.pool.close();
100
101 after_close(self, &self.server.config.database_path)
102 .expect("Failed to execute after_close handler");
103 }
104}
105
106pub(super) fn before_open(ctx: &Arc<Context>, path: &Path) -> Result {
108 if ctx.server.config.test.contains("fresh") {
109 match delete_database_for_testing(ctx, path) {
110 | Err(e) if !e.is_not_found() => return Err(e),
111 | _ => (),
112 }
113 }
114
115 Ok(())
116}
117
118fn after_close(ctx: &Context, path: &Path) -> Result {
121 if ctx.server.config.test.contains("cleanup") {
122 delete_database_for_testing(ctx, path)
123 .log_err()
124 .ok();
125 }
126
127 Ok(())
128}
129
130#[tracing::instrument(level = "debug", skip_all)]
134fn delete_database_for_testing(ctx: &Context, path: &Path) -> Result {
135 let config = &ctx.server.config;
136 let localhost = config
137 .server_name
138 .as_str()
139 .starts_with("localhost");
140
141 if !cfg!(test) && !localhost {
142 return Ok(());
143 }
144
145 debug_assert!(
146 config.test.contains("cleanup") | config.test.contains("fresh"),
147 "missing any test directive legitimating this call.",
148 );
149
150 remove_dir_all(path).map_err(Into::into)
151}