stratus/eth/storage/permanent/rocks/
rocks_db.rs

1use std::collections::BTreeMap;
2use std::path::Path;
3use std::sync::Arc;
4use std::time::Instant;
5
6use anyhow::Context;
7use rocksdb::DB;
8use rocksdb::Options;
9
10use super::rocks_config::CacheSetting;
11use super::rocks_config::DbConfig;
12#[cfg(feature = "metrics")]
13use crate::infra::metrics;
14
15/// Open (or create) the Database with the configs applied to all column families.
16///
17/// This function creates all the CFs in the database.
18///
19/// The returned `Options` **need** to be stored to refer to the DB metrics!
20#[tracing::instrument(skip_all, fields(path = ?path.as_ref()))]
21pub fn create_or_open_db(path: impl AsRef<Path>, cf_configs: &BTreeMap<&'static str, Options>) -> anyhow::Result<(&'static Arc<DB>, Options)> {
22    let path = path.as_ref();
23
24    tracing::debug!("creating settings for each column family");
25    let cf_config_iter = cf_configs.iter().map(|(name, opts)| (*name, opts.clone()));
26
27    tracing::debug!("generating options for column families");
28    let db_opts = DbConfig::Default.to_options(CacheSetting::Disabled);
29
30    if !path.exists() {
31        tracing::warn!(?path, "RocksDB at path doesn't exist, creating a new one there instead");
32    }
33
34    let open_db = || DB::open_cf_with_opts(&db_opts, path, cf_config_iter.clone());
35
36    tracing::debug!("attempting to open RocksDB");
37    let instant = Instant::now();
38    let db = match open_db() {
39        Ok(db) => db,
40        Err(e) => {
41            tracing::error!(reason = ?e, "failed to open RocksDB, trying to repair it to open again...");
42            DB::repair(&db_opts, path).context("attempting to repair RocksDB cause it failed to open")?;
43            open_db().context("trying to open RocksDB a second time, after repairing")?
44        }
45    };
46
47    let waited_for = instant.elapsed();
48    tracing::info!(?waited_for, db_path = ?path, "successfully opened RocksDB");
49
50    #[cfg(feature = "metrics")]
51    {
52        let db_name = path.file_name().with_context(|| format!("invalid db path without name '{path:?}'"))?.to_str();
53        metrics::set_rocks_last_startup_delay_millis(waited_for.as_millis() as u64, db_name);
54    }
55
56    // we leak here to create a 'static reference to the db. This is safe because this function is only called once
57    // and the db is used until the program exits.
58    Ok((Box::leak(Box::new(Arc::new(db))), db_opts))
59}