Skip to content

Commit bcf1c6b

Browse files
feat: adding the feature upgrade_0_5_x for auto-upgrading from version 0.5.x to the higher version 0.6.x
1 parent 8f0919e commit bcf1c6b

23 files changed

+885
-36
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ package.json
77
*.expanded.rs
88

99
# Related to [Why do binaries have Cargo.lock in version control, but not libraries?](https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-have-cargolock-in-version-control-but-not-libraries)
10-
Cargo.lock
10+
Cargo.lock
11+
tests/data/db_x_x_x

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = ["native_db_macro"]
1616

1717
[dependencies]
1818
redb = "2.1.0"
19+
redb1 = { version = "=1.5.1", package = "redb", optional = true }
1920
native_db_macro = { version = "0.5.3", path = "native_db_macro" }
2021
thiserror = "1.0"
2122
serde = { version = "1.0" }
@@ -42,9 +43,11 @@ uuid = { version = "1", features = ["serde", "v4"] }
4243
chrono = { version = "0.4", features = ["serde"] }
4344
rand = "0.8"
4445
once_cell = "1.19"
46+
dinghy-test = "0.7.1"
4547

4648
[features]
47-
default = []
49+
default = [ "upgrade_0_5_x" ]
50+
upgrade_0_5_x = [ "redb1" ]
4851

4952
[[bench]]
5053
name = "overhead_data_size"

justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ default:
66
build_no_default *args:
77
cargo build --no-default-features {{args}}
88

9+
# E.g. just build_default --test modules breaking_release_migration::from_0_5_x_to_0_6_x
910
build_default *args:
1011
cargo build {{args}}
1112

renovate.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
"semanticCommitScope": "deps",
66
"platformAutomerge": true,
77
"packageRules": [
8+
{
9+
"excludePackageNames": ["native_db_0_5_3", "redb1"],
10+
"enabled": false
11+
},
812
{
913
"description": "Automerge non-major updates",
1014
"matchUpdateTypes": ["minor", "patch"],

src/database.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::database_builder::ModelBuilder;
2+
use crate::database_instance::DatabaseInstance;
23
use crate::db_type::Result;
34
use crate::stats::{Stats, StatsTable};
45
use crate::table_definition::PrimaryTableDefinition;
@@ -32,7 +33,7 @@ use std::u64;
3233
/// Ok(())
3334
/// }
3435
pub struct Database<'a> {
35-
pub(crate) instance: redb::Database,
36+
pub(crate) instance: DatabaseInstance,
3637
pub(crate) primary_table_definitions: HashMap<String, PrimaryTableDefinition<'a>>,
3738
pub(crate) watchers: Arc<RwLock<watch::Watchers>>,
3839
pub(crate) watchers_counter_id: AtomicU64,
@@ -41,7 +42,7 @@ pub struct Database<'a> {
4142
impl Database<'_> {
4243
/// Creates a new read-write transaction.
4344
pub fn rw_transaction(&self) -> Result<RwTransaction> {
44-
let rw = self.instance.begin_write()?;
45+
let rw = self.instance.redb_database()?.begin_write()?;
4546
let write_txn = RwTransaction {
4647
watcher: &self.watchers,
4748
batch: RefCell::new(watch::Batch::new()),
@@ -55,7 +56,7 @@ impl Database<'_> {
5556

5657
/// Creates a new read-only transaction.
5758
pub fn r_transaction(&self) -> Result<RTransaction> {
58-
let txn = self.instance.begin_read()?;
59+
let txn = self.instance.redb_database()?.begin_read()?;
5960
let read_txn = RTransaction {
6061
internal: InternalRTransaction {
6162
redb_transaction: txn,
@@ -95,7 +96,7 @@ impl<'a> Database<'a> {
9596
let mut primary_table_definition: PrimaryTableDefinition =
9697
(model_builder, main_table_definition).into();
9798

98-
let rw = self.instance.begin_write()?;
99+
let rw = self.instance.redb_database()?.begin_write()?;
99100
rw.open_table(primary_table_definition.redb.clone())?;
100101

101102
for secondary_key in model_builder.model.secondary_keys.iter() {
@@ -120,7 +121,7 @@ impl<'a> Database<'a> {
120121
}
121122

122123
pub fn redb_stats(&self) -> Result<Stats> {
123-
let rx = self.instance.begin_read()?;
124+
let rx = self.instance.redb_database()?.begin_read()?;
124125
let mut stats_primary_tables = vec![];
125126
for primary_table in self.primary_table_definitions.values() {
126127
let result_table_open = rx.open_table(primary_table.redb.clone());

src/database_builder.rs

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,48 @@
1-
use crate::db_type::Result;
1+
use crate::database_instance::DatabaseInstance;
2+
use crate::db_type::{Error, Result};
23
use crate::table_definition::NativeModelOptions;
3-
use crate::{watch, Database, DatabaseModel, Input};
4+
use crate::{upgrade, watch, Database, DatabaseModel, Input};
45
use std::collections::HashMap;
56
use std::path::Path;
67
use std::sync::atomic::AtomicU64;
78
use std::sync::{Arc, RwLock};
89

9-
/// Builder that allows you to create a [`Database`](crate::Database) instance via [`create`](Self::create) or [`open`](Self::open) etc. and [define](Self::define) models.
1010
#[derive(Debug)]
11-
pub struct DatabaseBuilder {
12-
cache_size_bytes: Option<usize>,
13-
models_builder: HashMap<String, ModelBuilder>,
11+
pub(crate) struct DatabaseConfiguration {
12+
pub(crate) cache_size_bytes: Option<usize>,
1413
}
1514

16-
impl DatabaseBuilder {
17-
fn new_rdb_builder(&self) -> redb::Builder {
15+
impl DatabaseConfiguration {
16+
pub(crate) fn new_rdb_builder(&self) -> redb::Builder {
1817
let mut redb_builder = redb::Builder::new();
1918
if let Some(cache_size_bytes) = self.cache_size_bytes {
2019
redb_builder.set_cache_size(cache_size_bytes);
2120
}
2221
redb_builder
2322
}
23+
}
2424

25-
fn init<'a>(&'a self, redb_database: redb::Database) -> Result<Database<'a>> {
25+
#[cfg(feature = "redb1")]
26+
impl DatabaseConfiguration {
27+
pub(crate) fn redb1_new_rdb1_builder(&self) -> redb1::Builder {
28+
let mut redb_builder = redb1::Builder::new();
29+
if let Some(cache_size_bytes) = self.cache_size_bytes {
30+
redb_builder.set_cache_size(cache_size_bytes);
31+
}
32+
redb_builder
33+
}
34+
}
35+
/// Builder that allows you to create a [`Database`](crate::Database) instance via [`create`](Self::create) or [`open`](Self::open) etc. and [define](Self::define) models.
36+
#[derive(Debug)]
37+
pub struct DatabaseBuilder {
38+
database_configuration: DatabaseConfiguration,
39+
models_builder: HashMap<String, ModelBuilder>,
40+
}
41+
42+
impl DatabaseBuilder {
43+
fn init<'a>(&'a self, database_instance: DatabaseInstance) -> Result<Database<'a>> {
2644
let mut database = Database {
27-
instance: redb_database,
45+
instance: database_instance,
2846
primary_table_definitions: HashMap::new(),
2947
watchers: Arc::new(RwLock::new(watch::Watchers::new())),
3048
watchers_counter_id: AtomicU64::new(0),
@@ -42,40 +60,47 @@ impl DatabaseBuilder {
4260
/// Similar to [redb::Builder::new()](https://docs.rs/redb/latest/redb/struct.Builder.html#method.new).
4361
pub fn new() -> Self {
4462
Self {
45-
cache_size_bytes: None,
63+
database_configuration: DatabaseConfiguration {
64+
cache_size_bytes: None,
65+
},
4666
models_builder: HashMap::new(),
4767
}
4868
}
4969

5070
/// Similar to [redb::Builder::set_cache_size()](https://docs.rs/redb/latest/redb/struct.Builder.html#method.set_cache_size).
5171
pub fn set_cache_size(&mut self, bytes: usize) -> &mut Self {
52-
self.cache_size_bytes = Some(bytes);
72+
self.database_configuration.cache_size_bytes = Some(bytes);
5373
self
5474
}
5575

5676
/// Creates a new `Db` instance using the given path.
5777
///
5878
/// Similar to [redb::Builder.create(...)](https://docs.rs/redb/latest/redb/struct.Builder.html#method.create)
5979
pub fn create(&self, path: impl AsRef<Path>) -> Result<Database> {
60-
let db = self.new_rdb_builder().create(path)?;
61-
// Ok(Self::from_redb(db))
62-
self.init(db)
80+
let builder = self.database_configuration.new_rdb_builder();
81+
let database_instance = DatabaseInstance::create_on_disk(builder, path)?;
82+
self.init(database_instance)
6383
}
6484

6585
/// Similar to [redb::Builder::open(...)](https://docs.rs/redb/latest/redb/struct.Builder.html#method.open)
86+
/// But it also upgrades the database if needed if
6687
pub fn open(&self, path: impl AsRef<Path>) -> Result<Database> {
67-
let db = self.new_rdb_builder().open(path)?;
68-
// Ok(Self::from_redb(db))
69-
self.init(db)
88+
let builder = self.database_configuration.new_rdb_builder();
89+
let database_instance = match DatabaseInstance::open_on_disk(builder, &path) {
90+
Err(Error::RedbDatabaseError(redb::DatabaseError::UpgradeRequired(_))) => {
91+
upgrade::upgrade(&self.database_configuration, &path, &self.models_builder)
92+
}
93+
Err(error) => return Err(error),
94+
Ok(database_instance) => Ok(database_instance),
95+
}?;
96+
self.init(database_instance)
7097
}
7198

7299
/// Creates a new [`Database`](crate::Database) instance in memory.
73100
pub fn create_in_memory(&self) -> Result<Database> {
74-
let in_memory_backend = redb::backends::InMemoryBackend::new();
75-
let db = self.new_rdb_builder();
76-
let db = db.create_with_backend(in_memory_backend)?;
77-
// Ok(Self::from_redb(db))
78-
self.init(db)
101+
let builder = self.database_configuration.new_rdb_builder();
102+
let database_instance = DatabaseInstance::create_in_memory(builder)?;
103+
self.init(database_instance)
79104
}
80105

81106
/// Defines a table using the given model.

src/database_instance.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::db_type::Result;
2+
use redb::Builder;
3+
use std::path::Path;
4+
use std::path::PathBuf;
5+
6+
7+
pub(crate) struct DatabaseInstance {
8+
kind: DatabaseInstanceKind,
9+
}
10+
11+
impl DatabaseInstance {
12+
pub(crate) fn create_on_disk(builder: Builder, path: impl AsRef<Path>) -> Result<Self> {
13+
let db = builder.create(path.as_ref())?;
14+
Ok(Self {
15+
kind: DatabaseInstanceKind::OnDisk {
16+
redb_database: db,
17+
path: path.as_ref().to_path_buf(),
18+
},
19+
})
20+
}
21+
22+
pub(crate) fn open_on_disk(builder: Builder, path: impl AsRef<Path>) -> Result<Self> {
23+
let db = builder.open(path.as_ref())?;
24+
Ok(Self {
25+
kind: DatabaseInstanceKind::OnDisk {
26+
redb_database: db,
27+
path: path.as_ref().to_path_buf(),
28+
},
29+
})
30+
}
31+
32+
pub(crate) fn create_in_memory(builder: Builder) -> Result<Self> {
33+
let in_memory_backend = redb::backends::InMemoryBackend::new();
34+
let db = builder.create_with_backend(in_memory_backend)?;
35+
Ok(Self {
36+
kind: DatabaseInstanceKind::InMemory {
37+
redb_database: db,
38+
},
39+
})
40+
}
41+
42+
pub(crate) fn redb_database(&self) -> Result<&redb::Database> {
43+
self.kind.redb_database()
44+
}
45+
}
46+
47+
enum DatabaseInstanceKind {
48+
InMemory {
49+
redb_database: redb::Database,
50+
},
51+
OnDisk {
52+
redb_database: redb::Database,
53+
#[allow(dead_code)]
54+
path: PathBuf,
55+
}
56+
}
57+
58+
impl DatabaseInstanceKind {
59+
pub(crate) fn redb_database(&self) -> Result<&redb::Database> {
60+
match self {
61+
DatabaseInstanceKind::InMemory { redb_database } => Ok(redb_database),
62+
DatabaseInstanceKind::OnDisk { redb_database, .. } => Ok(redb_database)
63+
}
64+
}
65+
}

src/db_type/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@ pub enum Error {
99
#[error("Redb database error")]
1010
RedbDatabaseError(#[from] redb::DatabaseError),
1111

12+
#[cfg(feature = "redb1")]
13+
#[error("Legacy redb1 database error")]
14+
LegacyRedb1DatabaseError(#[from] redb1::DatabaseError),
15+
1216
#[error("Redb transaction error")]
1317
RedbTransactionError(#[from] redb::TransactionError),
1418

19+
#[cfg(feature = "redb1")]
20+
#[error("Redb redb1 transaction error")]
21+
Redb1TransactionError(#[from] redb1::TransactionError),
22+
1523
#[error("Redb storage error")]
1624
RedbStorageError(#[from] redb::StorageError),
1725

@@ -21,6 +29,12 @@ pub enum Error {
2129
#[error("Redb commit error")]
2230
RedbCommitError(#[from] redb::CommitError),
2331

32+
#[error("Redb compaction error")]
33+
RedbCompactionError(#[from] redb::CompactionError),
34+
35+
#[error("Database instance need upgrade")]
36+
DatabaseInstanceNeedUpgrade(u8),
37+
2438
#[error("IO error")]
2539
Io(#[from] std::io::Error),
2640

src/db_type/key/inner_key_value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, Ra
66
pub struct DatabaseInnerKeyValue(Vec<u8>);
77

88
impl DatabaseInnerKeyValue {
9-
fn new(data: Vec<u8>) -> Self {
9+
pub(crate) fn new(data: Vec<u8>) -> Self {
1010
Self(data)
1111
}
1212

0 commit comments

Comments
 (0)