change(state): Allow opening the database in a read-only mode (#8079)

* Allow opening the database read-only mode

* Update zebra-scan/src/storage/db.rs

Co-authored-by: teor <teor@riseup.net>

* Refactor skipping database upgrades

* Fix errors caused by the merge

* Add new argument in new_test_storage()

* Simplify test storage code

* Fix importing the `new_test_storate` fn

---------

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Marek 2023-12-13 22:54:00 +01:00 committed by GitHub
parent 92758a0d9f
commit 22852bc81d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 33 additions and 14 deletions

View File

@ -33,7 +33,7 @@ pub async fn init(
state: scan::State,
chain_tip_change: ChainTipChange,
) -> Result<(), Report> {
let storage = tokio::task::spawn_blocking(move || Storage::new(&config, network))
let storage = tokio::task::spawn_blocking(move || Storage::new(&config, network, false))
.wait_for_panics()
.await;

View File

@ -59,8 +59,8 @@ impl Storage {
///
/// This method can block while creating or reading database files, so it must be inside
/// spawn_blocking() in async code.
pub fn new(config: &Config, network: Network) -> Self {
let mut storage = Self::new_db(config, network);
pub fn new(config: &Config, network: Network, read_only: bool) -> Self {
let mut storage = Self::new_db(config, network, read_only);
for (sapling_key, birthday) in config.sapling_keys_to_scan.iter() {
storage.add_sapling_key(sapling_key, Some(zebra_chain::block::Height(*birthday)));

View File

@ -49,11 +49,11 @@ impl Storage {
/// If there is no existing database, creates a new database on disk.
///
/// New keys in `config` are not inserted into the database.
pub(crate) fn new_db(config: &Config, network: Network) -> Self {
pub(crate) fn new_db(config: &Config, network: Network, read_only: bool) -> Self {
Self::new_with_debug(
config, network,
// TODO: make format upgrades work with any database, then change this to `false`
true,
// TODO: make format upgrades work with any database, then change debug_skip_format_upgrades to `false`
true, read_only,
)
}
@ -67,6 +67,7 @@ impl Storage {
config: &Config,
network: Network,
debug_skip_format_upgrades: bool,
read_only: bool,
) -> Self {
let db = ScannerDb::new(
config.db_config(),
@ -77,6 +78,7 @@ impl Storage {
SCANNER_COLUMN_FAMILIES_IN_CODE
.iter()
.map(ToString::to_string),
read_only,
);
let new_storage = Self { db };

View File

@ -20,7 +20,7 @@ mod snapshot;
/// Returns an empty `Storage` suitable for testing.
pub fn new_test_storage(network: Network) -> Storage {
Storage::new(&Config::ephemeral(), network)
Storage::new(&Config::ephemeral(), network, false)
}
/// Add fake keys to `storage` for testing purposes.

View File

@ -22,8 +22,8 @@ use zebra_chain::{
use zebra_state::{SaplingScannedResult, TransactionIndex};
use crate::{
config::Config,
scan::{block_to_compact, scan_block},
storage::db::tests::new_test_storage,
tests::{fake_block, ZECPAGES_SAPLING_VIEWING_KEY},
};
@ -157,7 +157,7 @@ fn scanning_fake_blocks_store_key_and_results() -> Result<()> {
zcash_client_backend::encoding::encode_extended_full_viewing_key("zxviews", &extfvk);
// Create a database
let mut s = crate::storage::Storage::new(&Config::ephemeral(), Network::Mainnet);
let mut s = new_test_storage(Network::Mainnet);
// Insert the generated key to the database
s.add_sapling_key(&key_to_be_stored, None);

View File

@ -146,6 +146,7 @@ impl FinalizedState {
false,
#[cfg(feature = "elasticsearch")]
elastic_db,
false,
)
}
@ -158,6 +159,7 @@ impl FinalizedState {
network: Network,
debug_skip_format_upgrades: bool,
#[cfg(feature = "elasticsearch")] elastic_db: Option<elasticsearch::Elasticsearch>,
read_only: bool,
) -> Self {
let db = ZebraDb::new(
config,
@ -168,6 +170,7 @@ impl FinalizedState {
STATE_COLUMN_FAMILIES_IN_CODE
.iter()
.map(ToString::to_string),
read_only,
);
#[cfg(feature = "elasticsearch")]

View File

@ -658,6 +658,7 @@ impl DiskDb {
format_version_in_code: &Version,
network: Network,
column_families_in_code: impl IntoIterator<Item = String>,
read_only: bool,
) -> DiskDb {
let db_kind = db_kind.as_ref();
let path = config.db_path(db_kind, format_version_in_code.major, network);
@ -680,7 +681,11 @@ impl DiskDb {
.unique()
.map(|cf_name| rocksdb::ColumnFamilyDescriptor::new(cf_name, db_options.clone()));
let db_result = DB::open_cf_descriptors(&db_options, &path, column_families);
let db_result = if read_only {
DB::open_cf_descriptors_read_only(&db_options, &path, column_families, false)
} else {
DB::open_cf_descriptors(&db_options, &path, column_families)
};
match db_result {
Ok(db) => {

View File

@ -96,6 +96,7 @@ impl ZebraDb {
network: Network,
debug_skip_format_upgrades: bool,
column_families_in_code: impl IntoIterator<Item = String>,
read_only: bool,
) -> ZebraDb {
let disk_version = database_format_version_on_disk(
config,
@ -108,16 +109,20 @@ impl ZebraDb {
// Log any format changes before opening the database, in case opening fails.
let format_change = DbFormatChange::open_database(format_version_in_code, disk_version);
// Always do format upgrades in production, but allow them to be skipped by the scanner
// (because it doesn't support them yet).
// Format upgrades try to write to the database, so we always skip them if `read_only` is
// `true`.
//
// We allow skipping the upgrades by the scanner because it doesn't support them yet and we
// also allow skipping them when we are running tests.
//
// TODO: Make scanner support format upgrades, then remove `shielded-scan` here.
let can_skip_format_upgrades = cfg!(test) || cfg!(feature = "shielded-scan");
let debug_skip_format_upgrades = read_only
|| ((cfg!(test) || cfg!(feature = "shielded-scan")) && debug_skip_format_upgrades);
// Open the database and do initial checks.
let mut db = ZebraDb {
config: Arc::new(config.clone()),
debug_skip_format_upgrades: can_skip_format_upgrades && debug_skip_format_upgrades,
debug_skip_format_upgrades,
format_change_handle: None,
// After the database directory is created, a newly created database temporarily
// changes to the default database version. Then we set the correct version in the
@ -129,6 +134,7 @@ impl ZebraDb {
format_version_in_code,
network,
column_families_in_code,
read_only,
),
};

View File

@ -89,6 +89,7 @@ fn test_block_db_round_trip_with(
STATE_COLUMN_FAMILIES_IN_CODE
.iter()
.map(ToString::to_string),
false,
);
// Check that each block round-trips to the database

View File

@ -377,5 +377,6 @@ fn new_ephemeral_db() -> ZebraDb {
STATE_COLUMN_FAMILIES_IN_CODE
.iter()
.map(ToString::to_string),
false,
)
}

View File

@ -99,6 +99,7 @@ pub(crate) fn new_state_with_mainnet_genesis(
true,
#[cfg(feature = "elasticsearch")]
None,
false,
);
let non_finalized_state = NonFinalizedState::new(network);