radicle: Configure database connections on open
Configuration of database connections is not performed on `open`, which leaves room for error (e.g. to miss specifying configuration). Methods on `Profile` automatically supply the configuration of the profile. In testing code, just using the default configuration suffices.
This commit is contained in:
parent
f3afe7b02a
commit
5aaf978f97
|
|
@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
to create temporary patches.
|
to create temporary patches.
|
||||||
- The `rad id` command will provide a better error message when a non-delegate
|
- The `rad id` command will provide a better error message when a non-delegate
|
||||||
attempts to modify the identity document.
|
attempts to modify the identity document.
|
||||||
|
- The `journal_mode` and `synchronous` pragmas can now be configured using the
|
||||||
|
configuration file. The default values used are `WAL` and `NORMAL`, which
|
||||||
|
generates less I/O operations. On power loss, transactions might be rolled
|
||||||
|
back, but SQLite still guarantees consistency in this mode.
|
||||||
|
|
||||||
## Fixed Bugs
|
## Fixed Bugs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ impl Environment {
|
||||||
db.migrate(radicle::cob::migrate::ignore).unwrap();
|
db.migrate(radicle::cob::migrate::ignore).unwrap();
|
||||||
|
|
||||||
policy::Store::open(policies_db).unwrap();
|
policy::Store::open(policies_db).unwrap();
|
||||||
home.database_mut()
|
home.database_mut(node::db::config::Config::default())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.init(
|
.init(
|
||||||
&keypair.pk.into(),
|
&keypair.pk.into(),
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,7 @@ impl Runtime {
|
||||||
.expect("Runtime::init: unable to solve proof-of-work puzzle");
|
.expect("Runtime::init: unable to solve proof-of-work puzzle");
|
||||||
|
|
||||||
log::info!(target: "node", "Opening node database..");
|
log::info!(target: "node", "Opening node database..");
|
||||||
let db = home
|
let db = home.database_mut(config.database)?.init(
|
||||||
.database_mut()?
|
|
||||||
.journal_mode(config.database.sqlite.pragma.journal_mode)?
|
|
||||||
.synchronous(config.database.sqlite.pragma.synchronous)?
|
|
||||||
.init(
|
|
||||||
&id,
|
&id,
|
||||||
announcement.features,
|
announcement.features,
|
||||||
&announcement.alias,
|
&announcement.alias,
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,11 @@ impl<G: Signer<Signature> + cyphernet::Ecdh> NodeHandle<G> {
|
||||||
pub fn routing(&self) -> impl Iterator<Item = (RepoId, NodeId)> {
|
pub fn routing(&self) -> impl Iterator<Item = (RepoId, NodeId)> {
|
||||||
use node::routing::Store as _;
|
use node::routing::Store as _;
|
||||||
|
|
||||||
self.home.routing_mut().unwrap().entries().unwrap()
|
self.home
|
||||||
|
.routing_mut(node::db::config::Config::default())
|
||||||
|
.unwrap()
|
||||||
|
.entries()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inventory(&self) -> impl Iterator<Item = RepoId> + '_ {
|
pub fn inventory(&self) -> impl Iterator<Item = RepoId> + '_ {
|
||||||
|
|
@ -161,7 +165,11 @@ impl<G: Signer<Signature> + cyphernet::Ecdh> NodeHandle<G> {
|
||||||
|
|
||||||
/// Get sync status of a repo.
|
/// Get sync status of a repo.
|
||||||
pub fn synced_seeds(&self, rid: &RepoId) -> Vec<node::seed::SyncedSeed> {
|
pub fn synced_seeds(&self, rid: &RepoId) -> Vec<node::seed::SyncedSeed> {
|
||||||
let db = Database::reader(self.home.node().join(node::NODE_DB_FILE)).unwrap();
|
let db = Database::reader(
|
||||||
|
self.home.node().join(node::NODE_DB_FILE),
|
||||||
|
node::db::config::Config::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let seeds = db.seeds_for(rid).unwrap();
|
let seeds = db.seeds_for(rid).unwrap();
|
||||||
|
|
||||||
seeds.into_iter().collect::<Result<Vec<_>, _>>().unwrap()
|
seeds.into_iter().collect::<Result<Vec<_>, _>>().unwrap()
|
||||||
|
|
@ -446,7 +454,9 @@ impl Node<MockSigner> {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let policies = home.policies_mut().unwrap();
|
let policies = home.policies_mut().unwrap();
|
||||||
let db = home.database_mut().unwrap();
|
let db = home
|
||||||
|
.database_mut(node::db::config::Config::default())
|
||||||
|
.unwrap();
|
||||||
let db = service::Stores::from(db);
|
let db = service::Stores::from(db);
|
||||||
|
|
||||||
log::debug!(target: "test", "Node::init {}: {}", config.alias, signer.public_key());
|
log::debug!(target: "test", "Node::init {}: {}", config.alias, signer.public_key());
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,10 @@ where
|
||||||
policies.seed(&repo.rid, Scope::Followed).unwrap();
|
policies.seed(&repo.rid, Scope::Followed).unwrap();
|
||||||
}
|
}
|
||||||
// Initialize database.
|
// Initialize database.
|
||||||
let db = Database::open(config.tmp.path().join(node::NODE_DB_FILE))
|
let db = Database::open(
|
||||||
|
config.tmp.path().join(node::NODE_DB_FILE),
|
||||||
|
node::db::config::Config::default(),
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.init(
|
.init(
|
||||||
&id,
|
&id,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
from `FULL` to `NORMAL`, which generates less I/O operations. On power
|
from `FULL` to `NORMAL`, which generates less I/O operations. On power
|
||||||
loss, transactions might be rolled back, but SQLite still guarantees
|
loss, transactions might be rolled back, but SQLite still guarantees
|
||||||
consistency in this mode.
|
consistency in this mode.
|
||||||
|
- Opening database connections requires specification of a configuration.
|
||||||
|
`radicle::Profile` conveniently provides methods that supply the
|
||||||
|
configuration from `radicle::Profile::config`.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -568,7 +568,7 @@ mod test {
|
||||||
fn test_empty() {
|
fn test_empty() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
let path = tmp.path().join("cache");
|
let path = tmp.path().join("cache");
|
||||||
let cache = Database::open(path).unwrap();
|
let cache = Database::open(path, crate::node::db::config::Config::default()).unwrap();
|
||||||
|
|
||||||
assert!(cache.is_empty().unwrap());
|
assert!(cache.is_empty().unwrap());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,19 +82,19 @@ impl Database {
|
||||||
const PRAGMA: &'static str = "PRAGMA foreign_keys = ON";
|
const PRAGMA: &'static str = "PRAGMA foreign_keys = ON";
|
||||||
|
|
||||||
/// Open a database at the given path. Creates a new database if it
|
/// Open a database at the given path. Creates a new database if it
|
||||||
/// doesn't exist.
|
/// does not exist.
|
||||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
pub fn open<P: AsRef<Path>>(path: P, config: config::Config) -> Result<Self, Error> {
|
||||||
let mut db = sql::Connection::open_thread_safe(path)?;
|
let mut db = sql::Connection::open_thread_safe(path)?;
|
||||||
db.set_busy_timeout(DB_WRITE_TIMEOUT.as_millis() as usize)?;
|
db.set_busy_timeout(DB_WRITE_TIMEOUT.as_millis() as usize)?;
|
||||||
db.execute(Self::PRAGMA)?;
|
db.execute(Self::PRAGMA)?;
|
||||||
migrate(&db)?;
|
migrate(&db)?;
|
||||||
|
|
||||||
Ok(Self { db: Arc::new(db) })
|
Self { db: Arc::new(db) }.configure(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [`Self::open`], but in read-only mode. This is useful to have multiple
|
/// Same as [`Self::open`], but in read-only mode. This is useful to have multiple
|
||||||
/// open databases, as no locking is required.
|
/// open databases, as no locking is required.
|
||||||
pub fn reader<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
pub fn reader<P: AsRef<Path>>(path: P, config: config::Config) -> Result<Self, Error> {
|
||||||
let mut db = sql::Connection::open_thread_safe_with_flags(
|
let mut db = sql::Connection::open_thread_safe_with_flags(
|
||||||
path,
|
path,
|
||||||
sql::OpenFlags::new().with_read_only(),
|
sql::OpenFlags::new().with_read_only(),
|
||||||
|
|
@ -102,7 +102,7 @@ impl Database {
|
||||||
db.set_busy_timeout(DB_READ_TIMEOUT.as_millis() as usize)?;
|
db.set_busy_timeout(DB_READ_TIMEOUT.as_millis() as usize)?;
|
||||||
db.execute(Self::PRAGMA)?;
|
db.execute(Self::PRAGMA)?;
|
||||||
|
|
||||||
Ok(Self { db: Arc::new(db) })
|
Self { db: Arc::new(db) }.configure(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set `journal_mode` pragma.
|
/// Set `journal_mode` pragma.
|
||||||
|
|
@ -163,6 +163,11 @@ impl Database {
|
||||||
pub fn bump(&self) -> Result<usize, Error> {
|
pub fn bump(&self) -> Result<usize, Error> {
|
||||||
transaction(&self.db, bump)
|
transaction(&self.db, bump)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configure(self, config: config::Config) -> Result<Self, Error> {
|
||||||
|
self.journal_mode(config.sqlite.pragma.journal_mode)?
|
||||||
|
.synchronous(config.sqlite.pragma.synchronous)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the `user_version` value from the database header.
|
/// Get the `user_version` value from the database header.
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,7 @@ mod test {
|
||||||
use crate::test::arbitrary;
|
use crate::test::arbitrary;
|
||||||
|
|
||||||
fn database(path: &str) -> Database {
|
fn database(path: &str) -> Database {
|
||||||
let db = Database::open(path).unwrap();
|
let db = Database::open(path, crate::node::db::config::Config::default()).unwrap();
|
||||||
|
|
||||||
// We don't want to test foreign key constraints here.
|
// We don't want to test foreign key constraints here.
|
||||||
db.db.execute("PRAGMA foreign_keys = OFF").unwrap();
|
db.db.execute("PRAGMA foreign_keys = OFF").unwrap();
|
||||||
|
|
|
||||||
|
|
@ -252,10 +252,7 @@ impl Profile {
|
||||||
// Create DBs.
|
// Create DBs.
|
||||||
home.policies_mut()?;
|
home.policies_mut()?;
|
||||||
home.notifications_mut()?;
|
home.notifications_mut()?;
|
||||||
home.database_mut()?
|
home.database_mut(config.node.database)?.init(
|
||||||
.journal_mode(config.node.database.sqlite.pragma.journal_mode)?
|
|
||||||
.synchronous(config.node.database.sqlite.pragma.synchronous)?
|
|
||||||
.init(
|
|
||||||
&public_key,
|
&public_key,
|
||||||
config.node.features(),
|
config.node.features(),
|
||||||
&config.node.alias,
|
&config.node.alias,
|
||||||
|
|
@ -364,7 +361,7 @@ impl Profile {
|
||||||
/// Return a multi-source store for aliases.
|
/// Return a multi-source store for aliases.
|
||||||
pub fn aliases(&self) -> Aliases {
|
pub fn aliases(&self) -> Aliases {
|
||||||
let policies = self.home.policies().ok();
|
let policies = self.home.policies().ok();
|
||||||
let db = self.home.database().ok();
|
let db = self.home.database(self.config.node.database).ok();
|
||||||
|
|
||||||
Aliases { policies, db }
|
Aliases { policies, db }
|
||||||
}
|
}
|
||||||
|
|
@ -419,6 +416,24 @@ impl Profile {
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a handle to the database of the node, with SQLite configuration
|
||||||
|
/// from [`Self::config`] applied.
|
||||||
|
pub fn database_mut(&self) -> Result<node::Database, node::db::Error> {
|
||||||
|
self.home.database_mut(self.config.node.database)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a handle to a read-only database of the node, with SQLite
|
||||||
|
/// configuration from [`Self::config`] applied.
|
||||||
|
pub fn database(&self) -> Result<node::Database, node::db::Error> {
|
||||||
|
self.home.database(self.config.node.database)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the routing store, with SQLite
|
||||||
|
/// configuration from [`Self::config`] applied.
|
||||||
|
pub fn routing(&self) -> Result<impl node::routing::Store, node::db::Error> {
|
||||||
|
self.home.routing(self.config.node.database)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for Profile {
|
impl std::ops::Deref for Profile {
|
||||||
|
|
@ -630,34 +645,49 @@ impl Home {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a handle to a read-only database of the node.
|
/// Return a handle to a read-only database of the node.
|
||||||
pub fn database(&self) -> Result<node::Database, node::db::Error> {
|
pub fn database(
|
||||||
|
&self,
|
||||||
|
config: node::db::config::Config,
|
||||||
|
) -> Result<node::Database, node::db::Error> {
|
||||||
let path = self.node().join(node::NODE_DB_FILE);
|
let path = self.node().join(node::NODE_DB_FILE);
|
||||||
let db = node::Database::reader(path)?;
|
let db = node::Database::reader(path, config)?;
|
||||||
|
|
||||||
Ok(db)
|
Ok(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a handle to the database of the node.
|
/// Return a handle to the database of the node.
|
||||||
pub fn database_mut(&self) -> Result<node::Database, node::db::Error> {
|
pub fn database_mut(
|
||||||
|
&self,
|
||||||
|
config: node::db::config::Config,
|
||||||
|
) -> Result<node::Database, node::db::Error> {
|
||||||
let path = self.node().join(node::NODE_DB_FILE);
|
let path = self.node().join(node::NODE_DB_FILE);
|
||||||
let db = node::Database::open(path)?;
|
let db = node::Database::open(path, config)?;
|
||||||
|
|
||||||
Ok(db)
|
Ok(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the address store.
|
/// Returns the address store.
|
||||||
pub fn addresses(&self) -> Result<impl node::address::Store, node::db::Error> {
|
pub fn addresses(
|
||||||
self.database_mut()
|
&self,
|
||||||
|
config: node::db::config::Config,
|
||||||
|
) -> Result<impl node::address::Store, node::db::Error> {
|
||||||
|
self.database_mut(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the routing store.
|
/// Returns the routing store.
|
||||||
pub fn routing(&self) -> Result<impl node::routing::Store, node::db::Error> {
|
pub fn routing(
|
||||||
self.database()
|
&self,
|
||||||
|
config: node::db::config::Config,
|
||||||
|
) -> Result<impl node::routing::Store, node::db::Error> {
|
||||||
|
self.database(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the routing store, mutably.
|
/// Returns the routing store, mutably.
|
||||||
pub fn routing_mut(&self) -> Result<impl node::routing::Store, node::db::Error> {
|
pub fn routing_mut(
|
||||||
self.database_mut()
|
&self,
|
||||||
|
config: node::db::config::Config,
|
||||||
|
) -> Result<impl node::routing::Store, node::db::Error> {
|
||||||
|
self.database_mut(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get read access to the COBs cache.
|
/// Get read access to the COBs cache.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue