Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864687
D13337.id38581.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
D13337.id38581.diff
View Options
diff --git a/chronik/chronik-db/src/db.rs b/chronik/chronik-db/src/db.rs
--- a/chronik/chronik-db/src/db.rs
+++ b/chronik/chronik-db/src/db.rs
@@ -13,10 +13,11 @@
use rocksdb::{ColumnFamilyDescriptor, IteratorMode};
use thiserror::Error;
-use crate::io::BlockWriter;
+use crate::io::{BlockWriter, MetadataWriter};
// All column family names used by Chronik should be defined here
pub(crate) const CF_BLK: &str = "blk";
+pub(crate) const CF_META: &str = "meta";
pub(crate) type CF = rocksdb::ColumnFamily;
@@ -47,6 +48,7 @@
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
let mut cfs = Vec::new();
BlockWriter::add_cfs(&mut cfs);
+ MetadataWriter::add_cfs(&mut cfs);
Self::open_with_cfs(path, cfs)
}
diff --git a/chronik/chronik-db/src/io/metadata.rs b/chronik/chronik-db/src/io/metadata.rs
new file mode 100644
--- /dev/null
+++ b/chronik/chronik-db/src/io/metadata.rs
@@ -0,0 +1,86 @@
+// Copyright (c) 2023 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+use abc_rust_error::Result;
+use rocksdb::ColumnFamilyDescriptor;
+
+use crate::{
+ db::{Db, CF, CF_META},
+ ser::{db_deserialize, db_serialize},
+};
+
+/// Type for the version of the database, to let us know when we're outdated.
+pub type SchemaVersion = u64;
+
+const FIELD_SCHEMA_VERSION: &[u8] = b"SCHEMA_VERSION";
+
+/// Write database metadata
+pub struct MetadataWriter<'a> {
+ cf: &'a CF,
+}
+
+/// Read database metadata
+pub struct MetadataReader<'a> {
+ db: &'a Db,
+ cf: &'a CF,
+}
+
+impl<'a> MetadataWriter<'a> {
+ /// Create a writer to the database for metadata
+ pub fn new(db: &'a Db) -> Result<Self> {
+ let cf = db.cf(CF_META)?;
+ Ok(MetadataWriter { cf })
+ }
+
+ /// Update the schema version of the database
+ pub fn update_schema_version(
+ &self,
+ batch: &mut rocksdb::WriteBatch,
+ schema_version: SchemaVersion,
+ ) -> Result<()> {
+ batch.put_cf(
+ self.cf,
+ FIELD_SCHEMA_VERSION,
+ db_serialize(&schema_version)?,
+ );
+ Ok(())
+ }
+
+ pub(crate) fn add_cfs(columns: &mut Vec<ColumnFamilyDescriptor>) {
+ columns.push(ColumnFamilyDescriptor::new(
+ CF_META,
+ rocksdb::Options::default(),
+ ));
+ }
+}
+
+impl std::fmt::Debug for MetadataWriter<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "MetadataWriter {{ ... }}")
+ }
+}
+
+impl<'a> MetadataReader<'a> {
+ /// Create a reader from the database for metadata
+ pub fn new(db: &'a Db) -> Result<Self> {
+ let cf = db.cf(CF_META)?;
+ Ok(MetadataReader { db, cf })
+ }
+
+ /// Read the schema version of the database
+ pub fn schema_version(&self) -> Result<Option<SchemaVersion>> {
+ match self.db.get(self.cf, FIELD_SCHEMA_VERSION)? {
+ Some(ser_schema_version) => {
+ Ok(Some(db_deserialize(&ser_schema_version)?))
+ }
+ None => Ok(None),
+ }
+ }
+}
+
+impl std::fmt::Debug for MetadataReader<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "MetadataReader {{ ... }}")
+ }
+}
diff --git a/chronik/chronik-db/src/io/mod.rs b/chronik/chronik-db/src/io/mod.rs
--- a/chronik/chronik-db/src/io/mod.rs
+++ b/chronik/chronik-db/src/io/mod.rs
@@ -5,5 +5,7 @@
//! Module containing readers and writers for the database used by Chronik.
mod blocks;
+mod metadata;
pub use self::blocks::*;
+pub use self::metadata::*;
diff --git a/chronik/chronik-indexer/src/indexer.rs b/chronik/chronik-indexer/src/indexer.rs
--- a/chronik/chronik-indexer/src/indexer.rs
+++ b/chronik/chronik-indexer/src/indexer.rs
@@ -11,11 +11,16 @@
use chronik_bridge::{ffi, util::expect_unique_ptr};
use chronik_db::{
db::{Db, WriteBatch},
- io::{BlockHeight, BlockReader, BlockWriter, DbBlock},
+ io::{
+ BlockHeight, BlockReader, BlockWriter, DbBlock, MetadataReader,
+ MetadataWriter, SchemaVersion,
+ },
};
use chronik_util::{log, log_chronik};
use thiserror::Error;
+const CURRENT_INDEXER_VERSION: SchemaVersion = 1;
+
/// Params for setting up a [`ChronikIndexer`] instance.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ChronikIndexerParams {
@@ -63,6 +68,23 @@
/// Higher height that exists
exists: BlockHeight,
},
+
+ /// This Chronik instance is outdated
+ #[error(
+ "Chronik outdated: Chronik has version {}, but the database has \
+ version {0}. Upgrade your node to the appropriate version.",
+ CURRENT_INDEXER_VERSION
+ )]
+ ChronikOutdated(SchemaVersion),
+
+ /// Database is outdated
+ #[error(
+ "DB outdated: Chronik has version {}, but the database has version \
+ {0}. -reindex/-chronikreindex to reindex the database to the new \
+ version.",
+ CURRENT_INDEXER_VERSION
+ )]
+ DatabaseOutdated(SchemaVersion),
}
use self::ChronikIndexerError::*;
@@ -83,6 +105,7 @@
}
log_chronik!("Opening Chronik at {}\n", db_path.to_string_lossy());
let db = Db::open(&db_path)?;
+ verify_schema_version(&db)?;
Ok(ChronikIndexer { db })
}
@@ -235,15 +258,42 @@
ChronikBlock { db_block }
}
+fn verify_schema_version(db: &Db) -> Result<()> {
+ let metadata_reader = MetadataReader::new(db)?;
+ let metadata_writer = MetadataWriter::new(db)?;
+ match metadata_reader.schema_version()? {
+ Some(schema_version) => {
+ if schema_version > CURRENT_INDEXER_VERSION {
+ return Err(ChronikOutdated(schema_version).into());
+ }
+ if schema_version < CURRENT_INDEXER_VERSION {
+ return Err(DatabaseOutdated(schema_version).into());
+ }
+ }
+ None => {
+ let mut batch = WriteBatch::default();
+ metadata_writer
+ .update_schema_version(&mut batch, CURRENT_INDEXER_VERSION)?;
+ db.write_batch(batch)?;
+ }
+ }
+ log!("Chronik has version {CURRENT_INDEXER_VERSION}\n");
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use abc_rust_error::Result;
use bitcoinsuite_core::block::BlockHash;
- use chronik_db::io::{BlockReader, DbBlock};
+ use chronik_db::{
+ db::{Db, WriteBatch},
+ io::{BlockReader, DbBlock, MetadataReader, MetadataWriter},
+ };
use pretty_assertions::assert_eq;
use crate::indexer::{
- ChronikBlock, ChronikIndexer, ChronikIndexerError, ChronikIndexerParams,
+ ChronikBlock, ChronikIndexer, ChronikIndexerError,
+ ChronikIndexerParams, CURRENT_INDEXER_VERSION,
};
#[test]
@@ -307,4 +357,61 @@
Ok(())
}
+
+ #[test]
+ fn test_schema_version() -> Result<()> {
+ let dir = tempdir::TempDir::new("chronik-indexer--schema_version")?;
+ let chronik_path = dir.path().join("indexes").join("chronik");
+ let params = ChronikIndexerParams {
+ datadir_net: dir.path().to_path_buf(),
+ wipe_db: false,
+ };
+
+ // Setting up DB first time sets the schema version
+ ChronikIndexer::setup(params.clone())?;
+ {
+ let db = Db::open(&chronik_path)?;
+ assert_eq!(
+ MetadataReader::new(&db)?.schema_version()?,
+ Some(CURRENT_INDEXER_VERSION)
+ );
+ }
+ // Opening DB again works fine
+ ChronikIndexer::setup(params.clone())?;
+
+ // Override DB schema version to 0
+ {
+ let db = Db::open(&chronik_path)?;
+ let mut batch = WriteBatch::default();
+ MetadataWriter::new(&db)?.update_schema_version(&mut batch, 0)?;
+ db.write_batch(batch)?;
+ }
+ // -> DB too old
+ assert_eq!(
+ ChronikIndexer::setup(params.clone())
+ .unwrap_err()
+ .downcast::<ChronikIndexerError>()?,
+ ChronikIndexerError::DatabaseOutdated(0),
+ );
+
+ // Override DB schema version to CURRENT_INDEXER_VERSION + 1
+ {
+ let db = Db::open(&chronik_path)?;
+ let mut batch = WriteBatch::default();
+ MetadataWriter::new(&db)?.update_schema_version(
+ &mut batch,
+ CURRENT_INDEXER_VERSION + 1,
+ )?;
+ db.write_batch(batch)?;
+ }
+ // -> Chronik too old
+ assert_eq!(
+ ChronikIndexer::setup(params)
+ .unwrap_err()
+ .downcast::<ChronikIndexerError>()?,
+ ChronikIndexerError::ChronikOutdated(CURRENT_INDEXER_VERSION + 1),
+ );
+
+ Ok(())
+ }
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, May 20, 21:37 (15 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865982
Default Alt Text
D13337.id38581.diff (8 KB)
Attached To
D13337: [Chronik] Add metadata and schema version to DB
Event Timeline
Log In to Comment