mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-09-03 02:38:14 +00:00
garage_db: refactor open function
This commit is contained in:
+21
-4
@@ -11,12 +11,30 @@ use fjall::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
open::{Engine, OpenOpt},
|
||||||
Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult,
|
Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult,
|
||||||
TxResult, TxValueIter, Value, ValueIter,
|
TxResult, TxValueIter, Value, ValueIter,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use fjall;
|
pub use fjall;
|
||||||
|
|
||||||
|
// --
|
||||||
|
|
||||||
|
pub(crate) fn open_db(path: &PathBuf, opt: &OpenOpt) -> Result<Db> {
|
||||||
|
info!("Opening Fjall database at: {}", path.display());
|
||||||
|
if opt.fsync {
|
||||||
|
return Err(Error(
|
||||||
|
"metadata_fsync is not supported with the Fjall database engine".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut config = fjall::Config::new(path);
|
||||||
|
if let Some(block_cache_size) = opt.fjall_block_cache_size {
|
||||||
|
config = config.cache_size(block_cache_size as u64);
|
||||||
|
}
|
||||||
|
let keyspace = config.open_transactional()?;
|
||||||
|
Ok(FjallDb::init(keyspace))
|
||||||
|
}
|
||||||
|
|
||||||
// -- err
|
// -- err
|
||||||
|
|
||||||
impl From<fjall::Error> for Error {
|
impl From<fjall::Error> for Error {
|
||||||
@@ -95,10 +113,9 @@ impl IDb for FjallDb {
|
|||||||
.collect::<Result<Vec<_>>>()?)
|
.collect::<Result<Vec<_>>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&self, to: &PathBuf) -> Result<()> {
|
fn snapshot(&self, base_path: &PathBuf) -> Result<()> {
|
||||||
std::fs::create_dir_all(to)?;
|
std::fs::create_dir_all(base_path)?;
|
||||||
let mut path = to.clone();
|
let path = Engine::Fjall.db_path(base_path);
|
||||||
path.push("data.fjall");
|
|
||||||
|
|
||||||
let source_state = self.keyspace.read_tx();
|
let source_state = self.keyspace.read_tx();
|
||||||
let copy_keyspace = fjall::Config::new(path).open()?;
|
let copy_keyspace = fjall::Config::new(path).open()?;
|
||||||
|
|||||||
+46
-4
@@ -11,12 +11,55 @@ use heed::types::ByteSlice;
|
|||||||
use heed::{BytesDecode, Env, RoTxn, RwTxn, UntypedDatabase as Database};
|
use heed::{BytesDecode, Env, RoTxn, RwTxn, UntypedDatabase as Database};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
open::{Engine, OpenOpt},
|
||||||
Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult,
|
Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult,
|
||||||
TxResult, TxValueIter, Value, ValueIter,
|
TxResult, TxValueIter, Value, ValueIter,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use heed;
|
pub use heed;
|
||||||
|
|
||||||
|
// ---- top-level open function
|
||||||
|
|
||||||
|
pub(crate) fn open_db(path: &PathBuf, opt: &OpenOpt) -> Result<Db> {
|
||||||
|
info!("Opening LMDB database at: {}", path.display());
|
||||||
|
if let Err(e) = std::fs::create_dir_all(&path) {
|
||||||
|
return Err(Error(
|
||||||
|
format!("Unable to create LMDB data directory: {}", e).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let map_size = match opt.lmdb_map_size {
|
||||||
|
None => recommended_map_size(),
|
||||||
|
Some(v) => v - (v % 4096),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut env_builder = heed::EnvOpenOptions::new();
|
||||||
|
env_builder.max_dbs(100);
|
||||||
|
env_builder.map_size(map_size);
|
||||||
|
env_builder.max_readers(2048);
|
||||||
|
unsafe {
|
||||||
|
env_builder.flag(heed::flags::Flags::MdbNoRdAhead);
|
||||||
|
env_builder.flag(heed::flags::Flags::MdbNoMetaSync);
|
||||||
|
if !opt.fsync {
|
||||||
|
env_builder.flag(heed::flags::Flags::MdbNoSync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match env_builder.open(&path) {
|
||||||
|
Err(heed::Error::Io(e)) if e.kind() == std::io::ErrorKind::OutOfMemory => {
|
||||||
|
return Err(Error(
|
||||||
|
"OutOfMemory error while trying to open LMDB database. This can happen \
|
||||||
|
if your operating system is not allowing you to use sufficient virtual \
|
||||||
|
memory address space. Please check that no limit is set (ulimit -v). \
|
||||||
|
You may also try to set a smaller `lmdb_map_size` configuration parameter. \
|
||||||
|
On 32-bit machines, you should probably switch to another database engine."
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Err(e) => Err(Error(format!("Cannot open LMDB database: {}", e).into())),
|
||||||
|
Ok(db) => Ok(LmdbDb::init(db)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -- err
|
// -- err
|
||||||
|
|
||||||
impl From<heed::Error> for Error {
|
impl From<heed::Error> for Error {
|
||||||
@@ -104,10 +147,9 @@ impl IDb for LmdbDb {
|
|||||||
Ok(ret2)
|
Ok(ret2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&self, to: &PathBuf) -> Result<()> {
|
fn snapshot(&self, base_path: &PathBuf) -> Result<()> {
|
||||||
std::fs::create_dir_all(to)?;
|
std::fs::create_dir_all(base_path)?;
|
||||||
let mut path = to.clone();
|
let path = Engine::Lmdb.db_path(base_path);
|
||||||
path.push("data.mdb");
|
|
||||||
self.db
|
self.db
|
||||||
.copy_to_path(path, heed::CompactionOption::Enabled)?;
|
.copy_to_path(path, heed::CompactionOption::Enabled)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+21
-60
@@ -1,4 +1,3 @@
|
|||||||
use std::convert::TryInto;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{Db, Error, Result};
|
use crate::{Db, Error, Result};
|
||||||
@@ -24,6 +23,23 @@ impl Engine {
|
|||||||
Self::Fjall => "fjall",
|
Self::Fjall => "fjall",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return engine-specific DB path from base path
|
||||||
|
pub fn db_path(&self, base_path: &PathBuf) -> PathBuf {
|
||||||
|
let mut ret = base_path.clone();
|
||||||
|
match self {
|
||||||
|
Self::Lmdb => {
|
||||||
|
ret.push("db.lmdb");
|
||||||
|
}
|
||||||
|
Self::Sqlite => {
|
||||||
|
ret.push("db.sqlite");
|
||||||
|
}
|
||||||
|
Self::Fjall => {
|
||||||
|
ret.push("db.fjall");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Engine {
|
impl std::fmt::Display for Engine {
|
||||||
@@ -43,7 +59,7 @@ impl std::str::FromStr for Engine {
|
|||||||
"sled" => Err(Error("Sled is no longer supported as a database engine. Converting your old metadata db can be done using an older Garage binary (e.g. v0.9.4).".into())),
|
"sled" => Err(Error("Sled is no longer supported as a database engine. Converting your old metadata db can be done using an older Garage binary (e.g. v0.9.4).".into())),
|
||||||
kind => Err(Error(
|
kind => Err(Error(
|
||||||
format!(
|
format!(
|
||||||
"Invalid DB engine: {} (options are: lmdb, sqlite)",
|
"Invalid DB engine: {} (options are: lmdb, sqlite, fjall)",
|
||||||
kind
|
kind
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@@ -72,70 +88,15 @@ pub fn open_db(path: &PathBuf, engine: Engine, opt: &OpenOpt) -> Result<Db> {
|
|||||||
match engine {
|
match engine {
|
||||||
// ---- Sqlite DB ----
|
// ---- Sqlite DB ----
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
Engine::Sqlite => {
|
Engine::Sqlite => crate::sqlite_adapter::open_db(path, opt),
|
||||||
info!("Opening Sqlite database at: {}", path.display());
|
|
||||||
let manager = r2d2_sqlite::SqliteConnectionManager::file(path);
|
|
||||||
Ok(crate::sqlite_adapter::SqliteDb::new(manager, opt.fsync)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- LMDB DB ----
|
// ---- LMDB DB ----
|
||||||
#[cfg(feature = "lmdb")]
|
#[cfg(feature = "lmdb")]
|
||||||
Engine::Lmdb => {
|
Engine::Lmdb => crate::lmdb_adapter::open_db(path, opt),
|
||||||
info!("Opening LMDB database at: {}", path.display());
|
|
||||||
if let Err(e) = std::fs::create_dir_all(&path) {
|
|
||||||
return Err(Error(
|
|
||||||
format!("Unable to create LMDB data directory: {}", e).into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let map_size = match opt.lmdb_map_size {
|
|
||||||
None => crate::lmdb_adapter::recommended_map_size(),
|
|
||||||
Some(v) => v - (v % 4096),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut env_builder = heed::EnvOpenOptions::new();
|
|
||||||
env_builder.max_dbs(100);
|
|
||||||
env_builder.map_size(map_size);
|
|
||||||
env_builder.max_readers(2048);
|
|
||||||
unsafe {
|
|
||||||
env_builder.flag(crate::lmdb_adapter::heed::flags::Flags::MdbNoRdAhead);
|
|
||||||
env_builder.flag(crate::lmdb_adapter::heed::flags::Flags::MdbNoMetaSync);
|
|
||||||
if !opt.fsync {
|
|
||||||
env_builder.flag(heed::flags::Flags::MdbNoSync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match env_builder.open(&path) {
|
|
||||||
Err(heed::Error::Io(e)) if e.kind() == std::io::ErrorKind::OutOfMemory => {
|
|
||||||
return Err(Error(
|
|
||||||
"OutOfMemory error while trying to open LMDB database. This can happen \
|
|
||||||
if your operating system is not allowing you to use sufficient virtual \
|
|
||||||
memory address space. Please check that no limit is set (ulimit -v). \
|
|
||||||
You may also try to set a smaller `lmdb_map_size` configuration parameter. \
|
|
||||||
On 32-bit machines, you should probably switch to another database engine."
|
|
||||||
.into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Err(e) => Err(Error(format!("Cannot open LMDB database: {}", e).into())),
|
|
||||||
Ok(db) => Ok(crate::lmdb_adapter::LmdbDb::init(db)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Fjall DB ----
|
// ---- Fjall DB ----
|
||||||
#[cfg(feature = "fjall")]
|
#[cfg(feature = "fjall")]
|
||||||
Engine::Fjall => {
|
Engine::Fjall => crate::fjall_adapter::open_db(path, opt),
|
||||||
info!("Opening Fjall database at: {}", path.display());
|
|
||||||
if opt.fsync {
|
|
||||||
return Err(Error(
|
|
||||||
"metadata_fsync is not supported with the Fjall database engine".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let mut config = fjall::Config::new(path);
|
|
||||||
if let Some(block_cache_size) = opt.fjall_block_cache_size {
|
|
||||||
config = config.cache_size(block_cache_size.try_into().unwrap());
|
|
||||||
}
|
|
||||||
let keyspace = config.open_transactional()?;
|
|
||||||
Ok(crate::fjall_adapter::FjallDb::init(keyspace))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pattern is unreachable when all supported DB engines are compiled into binary. The allow
|
// Pattern is unreachable when all supported DB engines are compiled into binary. The allow
|
||||||
// attribute is added so that we won't have to change this match in case stop building
|
// attribute is added so that we won't have to change this match in case stop building
|
||||||
|
|||||||
@@ -11,12 +11,23 @@ use r2d2_sqlite::SqliteConnectionManager;
|
|||||||
use rusqlite::{params, Rows, Statement, Transaction};
|
use rusqlite::{params, Rows, Statement, Transaction};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
open::{Engine, OpenOpt},
|
||||||
Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult,
|
Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult,
|
||||||
TxResult, TxValueIter, Value, ValueIter,
|
TxResult, TxValueIter, Value, ValueIter,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use rusqlite;
|
pub use rusqlite;
|
||||||
|
|
||||||
|
// ---- top-level open function
|
||||||
|
|
||||||
|
pub(crate) fn open_db(path: &PathBuf, opt: &OpenOpt) -> Result<Db> {
|
||||||
|
info!("Opening Sqlite database at: {}", path.display());
|
||||||
|
let manager = r2d2_sqlite::SqliteConnectionManager::file(path);
|
||||||
|
Ok(SqliteDb::new(manager, opt.fsync)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
type Connection = r2d2::PooledConnection<SqliteConnectionManager>;
|
type Connection = r2d2::PooledConnection<SqliteConnectionManager>;
|
||||||
|
|
||||||
// --- err
|
// --- err
|
||||||
@@ -139,17 +150,19 @@ impl IDb for SqliteDb {
|
|||||||
Ok(trees)
|
Ok(trees)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&self, to: &PathBuf) -> Result<()> {
|
fn snapshot(&self, base_path: &PathBuf) -> Result<()> {
|
||||||
fn progress(p: rusqlite::backup::Progress) {
|
fn progress(p: rusqlite::backup::Progress) {
|
||||||
let percent = (p.pagecount - p.remaining) * 100 / p.pagecount;
|
let percent = (p.pagecount - p.remaining) * 100 / p.pagecount;
|
||||||
info!("Sqlite snapshot progress: {}%", percent);
|
info!("Sqlite snapshot progress: {}%", percent);
|
||||||
}
|
}
|
||||||
std::fs::create_dir_all(to)?;
|
|
||||||
let mut path = to.clone();
|
std::fs::create_dir_all(base_path)?;
|
||||||
path.push("db.sqlite");
|
let path = Engine::Sqlite.db_path(&base_path);
|
||||||
|
|
||||||
self.db
|
self.db
|
||||||
.get()?
|
.get()?
|
||||||
.backup(rusqlite::DatabaseName::Main, path, Some(progress))?;
|
.backup(rusqlite::DatabaseName::Main, path, Some(progress))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -155,7 +155,7 @@ fn test_fjall_db() {
|
|||||||
use crate::fjall_adapter::{fjall, FjallDb};
|
use crate::fjall_adapter::{fjall, FjallDb};
|
||||||
|
|
||||||
let path = mktemp::Temp::new_dir().unwrap();
|
let path = mktemp::Temp::new_dir().unwrap();
|
||||||
let config = fjall::Config::new(path);
|
let config = fjall::Config::new(path).temporary(true);
|
||||||
let keyspace = config.open_transactional().unwrap();
|
let keyspace = config.open_transactional().unwrap();
|
||||||
let db = FjallDb::init(keyspace);
|
let db = FjallDb::init(keyspace);
|
||||||
test_suite(db);
|
test_suite(db);
|
||||||
|
|||||||
+1
-12
@@ -116,18 +116,7 @@ impl Garage {
|
|||||||
info!("Opening database...");
|
info!("Opening database...");
|
||||||
let db_engine = db::Engine::from_str(&config.db_engine)
|
let db_engine = db::Engine::from_str(&config.db_engine)
|
||||||
.ok_or_message("Invalid `db_engine` value in configuration file")?;
|
.ok_or_message("Invalid `db_engine` value in configuration file")?;
|
||||||
let mut db_path = config.metadata_dir.clone();
|
let db_path = db_engine.db_path(&config.metadata_dir);
|
||||||
match db_engine {
|
|
||||||
db::Engine::Sqlite => {
|
|
||||||
db_path.push("db.sqlite");
|
|
||||||
}
|
|
||||||
db::Engine::Lmdb => {
|
|
||||||
db_path.push("db.lmdb");
|
|
||||||
}
|
|
||||||
db::Engine::Fjall => {
|
|
||||||
db_path.push("db.fjall");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let db_opt = db::OpenOpt {
|
let db_opt = db::OpenOpt {
|
||||||
fsync: config.metadata_fsync,
|
fsync: config.metadata_fsync,
|
||||||
lmdb_map_size: match config.lmdb_map_size {
|
lmdb_map_size: match config.lmdb_map_size {
|
||||||
|
|||||||
Reference in New Issue
Block a user