Add new fs abstraction

next
Sayan Nandan 7 months ago
parent 1cb35b6b36
commit 0573e80992
No known key found for this signature in database
GPG Key ID: 0EBD769024B24F0A

@ -266,18 +266,17 @@ impl Model {
AlterAction::Ignore => {} AlterAction::Ignore => {}
AlterAction::Add(new_fields) => { AlterAction::Add(new_fields) => {
// TODO(@ohsayan): this impacts lockdown duration; fix it // TODO(@ohsayan): this impacts lockdown duration; fix it
if G::FS_IS_NON_NULL {
// prepare txn // prepare txn
let txn = gns::model::AlterModelAddTxn::new( let txn = gns::model::AlterModelAddTxn::new(
ModelIDRef::new_ref(&space_name, &space, &model_name, model), ModelIDRef::new_ref(&space_name, &space, &model_name, model),
&new_fields, &new_fields,
); );
// commit txn // commit txn
global global
.gns_driver() .gns_driver()
.lock() .lock()
.driver_context(|drv| drv.commit_event(txn), || {})?; .driver_context(|drv| drv.commit_event(txn), || {})?;
}
let mut mutator = model.model_mutator(); let mut mutator = model.model_mutator();
new_fields new_fields
.stseq_ord_kv() .stseq_ord_kv()
@ -287,36 +286,32 @@ impl Model {
}); });
} }
AlterAction::Remove(removed) => { AlterAction::Remove(removed) => {
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn = gns::model::AlterModelRemoveTxn::new(
let txn = gns::model::AlterModelRemoveTxn::new( ModelIDRef::new_ref(&space_name, space, &model_name, model),
ModelIDRef::new_ref(&space_name, space, &model_name, model), &removed,
&removed, );
); // commit txn
// commit txn global
global .gns_driver()
.gns_driver() .lock()
.lock() .driver_context(|drv| drv.commit_event(txn), || {})?;
.driver_context(|drv| drv.commit_event(txn), || {})?;
}
let mut mutator = model.model_mutator(); let mut mutator = model.model_mutator();
removed.iter().for_each(|field_id| { removed.iter().for_each(|field_id| {
mutator.remove_field(field_id.as_str()); mutator.remove_field(field_id.as_str());
}); });
} }
AlterAction::Update(updated) => { AlterAction::Update(updated) => {
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn = gns::model::AlterModelUpdateTxn::new(
let txn = gns::model::AlterModelUpdateTxn::new( ModelIDRef::new_ref(&space_name, space, &model_name, model),
ModelIDRef::new_ref(&space_name, space, &model_name, model), &updated,
&updated, );
); // commit txn
// commit txn global
global .gns_driver()
.gns_driver() .lock()
.lock() .driver_context(|drv| drv.commit_event(txn), || {})?;
.driver_context(|drv| drv.commit_event(txn), || {})?;
}
let mut mutator = model.model_mutator(); let mut mutator = model.model_mutator();
updated.into_iter().for_each(|(field_id, field)| { updated.into_iter().for_each(|(field_id, field)| {
mutator.update_field(field_id.as_ref(), field); mutator.update_field(field_id.as_ref(), field);

@ -278,36 +278,32 @@ impl Model {
} }
} }
// since we've locked this down, no one else can parallely create another model in the same space (or remove) // since we've locked this down, no one else can parallely create another model in the same space (or remove)
if G::FS_IS_NON_NULL { let mut txn_driver = global.gns_driver().lock();
let mut txn_driver = global.gns_driver().lock(); // prepare txn
// prepare txn let txn = gns::model::CreateModelTxn::new(
let txn = gns::model::CreateModelTxn::new( SpaceIDRef::new(&space_name, &space),
SpaceIDRef::new(&space_name, &space), &model_name,
&model_name, &model,
&model, );
); // attempt to initialize driver
// attempt to initialize driver global.initialize_model_driver(
global.initialize_model_driver( &space_name,
&space_name, space.get_uuid(),
space.get_uuid(), &model_name,
&model_name, model.get_uuid(),
model.get_uuid(), )?;
)?; // commit txn
// commit txn txn_driver.driver_context(
txn_driver.driver_context( |drv| drv.commit_event(txn),
|drv| drv.commit_event(txn), || {
|| { global.taskmgr_post_standard_priority(Task::new(GenericTask::delete_model_dir(
global.taskmgr_post_standard_priority(Task::new( &space_name,
GenericTask::delete_model_dir( space.get_uuid(),
&space_name, &model_name,
space.get_uuid(), model.get_uuid(),
&model_name, )))
model.get_uuid(), },
), )?;
))
},
)?;
}
// update global state // update global state
let _ = space.models_mut().insert(model_name.into()); let _ = space.models_mut().insert(model_name.into());
let _ = global let _ = global
@ -347,28 +343,26 @@ impl Model {
return Err(QueryError::QExecDdlNotEmpty); return Err(QueryError::QExecDdlNotEmpty);
} }
// okay this is looking good for us // okay this is looking good for us
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn = gns::model::DropModelTxn::new(ModelIDRef::new(
let txn = gns::model::DropModelTxn::new(ModelIDRef::new( SpaceIDRef::new(&space_name, &space),
SpaceIDRef::new(&space_name, &space), &model_name,
&model_name, model.get_uuid(),
model.get_uuid(), model.delta_state().schema_current_version().value_u64(),
model.delta_state().schema_current_version().value_u64(), ));
)); // commit txn
// commit txn global
global .gns_driver()
.gns_driver() .lock()
.lock() .driver_context(|drv| drv.commit_event(txn), || {})?;
.driver_context(|drv| drv.commit_event(txn), || {})?; // request cleanup
// request cleanup global.purge_model_driver(
global.purge_model_driver( space_name,
space_name, space.get_uuid(),
space.get_uuid(), model_name,
model_name, model.get_uuid(),
model.get_uuid(), false,
false, );
);
}
// update global state // update global state
let _ = models_idx.remove(&EntityIDRef::new(&space_name, &model_name)); let _ = models_idx.remove(&EntityIDRef::new(&space_name, &model_name));
let _ = space.models_mut().remove(model_name); let _ = space.models_mut().remove(model_name);

@ -24,7 +24,7 @@
* *
*/ */
use crate::engine::storage::safe_interfaces::paths_v1; use crate::engine::storage::safe_interfaces::{paths_v1, FileSystem};
use super::EntityIDRef; use super::EntityIDRef;
@ -35,7 +35,6 @@ use {
fractal::{GenericTask, GlobalInstanceLike, Task}, fractal::{GenericTask, GlobalInstanceLike, Task},
idx::STIndex, idx::STIndex,
ql::ddl::{alt::AlterSpace, crt::CreateSpace, drop::DropSpace}, ql::ddl::{alt::AlterSpace, crt::CreateSpace, drop::DropSpace},
storage::safe_interfaces::FSInterface,
txn::{self, SpaceIDRef}, txn::{self, SpaceIDRef},
}, },
std::collections::HashSet, std::collections::HashSet,
@ -167,24 +166,20 @@ impl Space {
} }
} }
// commit txn // commit txn
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn = txn::gns::space::CreateSpaceTxn::new(space.props(), &space_name, &space);
let txn = txn::gns::space::CreateSpaceTxn::new(space.props(), &space_name, &space); // try to create space for...the space
// try to create space for...the space FileSystem::create_dir_all(&paths_v1::space_dir(&space_name, space.get_uuid()))?;
G::FileSystem::fs_create_dir_all(&paths_v1::space_dir( // commit txn
&space_name, global.gns_driver().lock().driver_context(
space.get_uuid(), |drv| drv.commit_event(txn),
))?; || {
// commit txn global.taskmgr_post_standard_priority(Task::new(GenericTask::delete_space_dir(
global.gns_driver().lock().driver_context( &space_name,
|drv| drv.commit_event(txn), space.get_uuid(),
|| { )))
global.taskmgr_post_standard_priority(Task::new( },
GenericTask::delete_space_dir(&space_name, space.get_uuid()), )?;
))
},
)?;
}
// update global state // update global state
let _ = spaces.st_insert(space_name, space); let _ = spaces.st_insert(space_name, space);
if if_not_exists { if if_not_exists {
@ -214,19 +209,15 @@ impl Space {
Some(patch) => patch, Some(patch) => patch,
None => return Err(QueryError::QExecDdlInvalidProperties), None => return Err(QueryError::QExecDdlInvalidProperties),
}; };
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn =
let txn = txn::gns::space::AlterSpaceTxn::new( txn::gns::space::AlterSpaceTxn::new(SpaceIDRef::new(&space_name, space), &patch);
SpaceIDRef::new(&space_name, space), // commit
&patch, // commit txn
); global
// commit .gns_driver()
// commit txn .lock()
global .driver_context(|drv| drv.commit_event(txn), || {})?;
.gns_driver()
.lock()
.driver_context(|drv| drv.commit_event(txn), || {})?;
}
// merge // merge
dict::rmerge_data_with_patch(space.props_mut(), patch); dict::rmerge_data_with_patch(space.props_mut(), patch);
// the `env` key may have been popped, so put it back (setting `env: null` removes the env key and we don't want to waste time enforcing this in the // the `env` key may have been popped, so put it back (setting `env: null` removes the env key and we don't want to waste time enforcing this in the
@ -255,20 +246,18 @@ impl Space {
} }
}; };
// commit drop // commit drop
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn = txn::gns::space::DropSpaceTxn::new(SpaceIDRef::new(&space_name, &space));
let txn = // commit txn
txn::gns::space::DropSpaceTxn::new(SpaceIDRef::new(&space_name, &space)); global
// commit txn .gns_driver()
global .lock()
.gns_driver() .driver_context(|drv| drv.commit_event(txn), || {})?;
.lock() // request cleanup
.driver_context(|drv| drv.commit_event(txn), || {})?; global.taskmgr_post_standard_priority(Task::new(GenericTask::delete_space_dir(
// request cleanup &space_name,
global.taskmgr_post_standard_priority(Task::new( space.get_uuid(),
GenericTask::delete_space_dir(&space_name, space.get_uuid()), )));
));
}
let space_uuid = space.get_uuid(); let space_uuid = space.get_uuid();
for model in space.models.into_iter() { for model in space.models.into_iter() {
let e: EntityIDRef<'static> = unsafe { let e: EntityIDRef<'static> = unsafe {
@ -305,20 +294,18 @@ impl Space {
return Err(QueryError::QExecDdlNotEmpty); return Err(QueryError::QExecDdlNotEmpty);
} }
// okay, it's empty; good riddance // okay, it's empty; good riddance
if G::FS_IS_NON_NULL { // prepare txn
// prepare txn let txn = txn::gns::space::DropSpaceTxn::new(SpaceIDRef::new(&space_name, &space));
let txn = // commit txn
txn::gns::space::DropSpaceTxn::new(SpaceIDRef::new(&space_name, &space)); global
// commit txn .gns_driver()
global .lock()
.gns_driver() .driver_context(|drv| drv.commit_event(txn), || {})?;
.lock() // request cleanup
.driver_context(|drv| drv.commit_event(txn), || {})?; global.taskmgr_post_standard_priority(Task::new(GenericTask::delete_space_dir(
// request cleanup &space_name,
global.taskmgr_post_standard_priority(Task::new( space.get_uuid(),
GenericTask::delete_space_dir(&space_name, space.get_uuid()), )));
));
}
let _ = spaces.st_delete(space_name.as_str()); let _ = spaces.st_delete(space_name.as_str());
if if_exists { if if_exists {
Ok(Some(true)) Ok(Some(true))

@ -358,7 +358,7 @@ mod exec {
}; };
#[test] #[test]
fn simple_add() { fn simple_add() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("simple_add");
super::exec_plan( super::exec_plan(
&global, &global,
true, true,
@ -388,7 +388,7 @@ mod exec {
} }
#[test] #[test]
fn simple_remove() { fn simple_remove() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("simple_remove");
super::exec_plan( super::exec_plan(
&global, &global,
true, true,
@ -413,7 +413,7 @@ mod exec {
} }
#[test] #[test]
fn simple_update() { fn simple_update() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("simple_update");
super::exec_plan( super::exec_plan(
&global, &global,
true, true,
@ -431,7 +431,7 @@ mod exec {
} }
#[test] #[test]
fn failing_alter_nullable_switch_need_lock() { fn failing_alter_nullable_switch_need_lock() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("failing_alter_nullable_switch_need_lock");
assert_eq!( assert_eq!(
super::exec_plan( super::exec_plan(
&global, &global,

@ -146,7 +146,7 @@ mod exec {
#[test] #[test]
fn simple() { fn simple() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("exec_simple_create");
exec_create_new_space( exec_create_new_space(
&global, &global,
"create model myspace.mymodel(username: string, password: binary)", "create model myspace.mymodel(username: string, password: binary)",

@ -33,7 +33,7 @@ use crate::engine::{
#[test] #[test]
fn alter_add_prop_env_var() { fn alter_add_prop_env_var() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("alter_add_prop_env_var");
super::exec_create_alter( super::exec_create_alter(
&global, &global,
"create space myspace", "create space myspace",
@ -53,7 +53,7 @@ fn alter_add_prop_env_var() {
#[test] #[test]
fn alter_update_prop_env_var() { fn alter_update_prop_env_var() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("alter_update_prop_env_var");
let uuid = super::exec_create( let uuid = super::exec_create(
&global, &global,
"create space myspace with { env: { MY_NEW_PROP: 100 } }", "create space myspace with { env: { MY_NEW_PROP: 100 } }",
@ -83,7 +83,7 @@ fn alter_update_prop_env_var() {
#[test] #[test]
fn alter_remove_prop_env_var() { fn alter_remove_prop_env_var() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("alter_remove_prop_env_var");
let uuid = super::exec_create( let uuid = super::exec_create(
&global, &global,
"create space myspace with { env: { MY_NEW_PROP: 100 } }", "create space myspace with { env: { MY_NEW_PROP: 100 } }",
@ -113,7 +113,7 @@ fn alter_remove_prop_env_var() {
#[test] #[test]
fn alter_nx() { fn alter_nx() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("alter_nx");
assert_eq!( assert_eq!(
super::exec_alter( super::exec_alter(
&global, &global,
@ -127,7 +127,7 @@ fn alter_nx() {
#[test] #[test]
fn alter_remove_all_env() { fn alter_remove_all_env() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("alter_remove_all_env");
let uuid = super::exec_create( let uuid = super::exec_create(
&global, &global,
"create space myspace with { env: { MY_NEW_PROP: 100 } }", "create space myspace with { env: { MY_NEW_PROP: 100 } }",

@ -33,7 +33,7 @@ use crate::engine::{
#[test] #[test]
fn exec_create_space_simple() { fn exec_create_space_simple() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("exec_create_space_simple");
super::exec_create(&global, "create space myspace", |spc| { super::exec_create(&global, "create space myspace", |spc| {
assert!(spc.models().is_empty()) assert!(spc.models().is_empty())
}) })
@ -42,7 +42,7 @@ fn exec_create_space_simple() {
#[test] #[test]
fn exec_create_space_with_env() { fn exec_create_space_with_env() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("exec_create_space_with_env");
super::exec_create( super::exec_create(
&global, &global,
r#" r#"
@ -69,7 +69,7 @@ fn exec_create_space_with_env() {
#[test] #[test]
fn exec_create_space_with_bad_env_type() { fn exec_create_space_with_bad_env_type() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("exec_create_space_with_bad_env_type");
assert_eq!( assert_eq!(
super::exec_create(&global, "create space myspace with { env: 100 }", |_| {}).unwrap_err(), super::exec_create(&global, "create space myspace with { env: 100 }", |_| {}).unwrap_err(),
QueryError::QExecDdlInvalidProperties QueryError::QExecDdlInvalidProperties
@ -78,7 +78,7 @@ fn exec_create_space_with_bad_env_type() {
#[test] #[test]
fn exec_create_space_with_random_property() { fn exec_create_space_with_random_property() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id("exec_create_space_with_random_property");
assert_eq!( assert_eq!(
super::exec_create( super::exec_create(
&global, &global,

@ -28,7 +28,7 @@ use crate::engine::{error::QueryError, fractal::test_utils::TestGlobal};
#[test] #[test]
fn simple_delete() { fn simple_delete() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_delete_simple_delete");
super::exec_delete( super::exec_delete(
&global, &global,
"create model myspace.mymodel(username: string, password: string)", "create model myspace.mymodel(username: string, password: string)",
@ -41,7 +41,7 @@ fn simple_delete() {
#[test] #[test]
fn delete_nonexisting() { fn delete_nonexisting() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_delete_delete_nonexisting");
assert_eq!( assert_eq!(
super::exec_delete( super::exec_delete(
&global, &global,

@ -31,7 +31,7 @@ struct Tuple(Vec<(Box<str>, Datacell)>);
#[test] #[test]
fn insert_simple() { fn insert_simple() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_insert_simple");
super::exec_insert( super::exec_insert(
&global, &global,
"create model myspace.mymodel(username: string, password: string)", "create model myspace.mymodel(username: string, password: string)",
@ -46,7 +46,7 @@ fn insert_simple() {
#[test] #[test]
fn insert_with_null() { fn insert_with_null() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_insert_with_null");
super::exec_insert( super::exec_insert(
&global, &global,
"create model myspace.mymodel(username: string, null useless_password: string, null useless_email: string, null useless_random_column: uint64)", "create model myspace.mymodel(username: string, null useless_password: string, null useless_email: string, null useless_random_column: uint64)",
@ -69,7 +69,7 @@ fn insert_with_null() {
#[test] #[test]
fn insert_duplicate() { fn insert_duplicate() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_insert_duplicate");
super::exec_insert( super::exec_insert(
&global, &global,
"create model myspace.mymodel(username: string, password: string)", "create model myspace.mymodel(username: string, password: string)",

@ -31,7 +31,7 @@ use {
#[test] #[test]
fn simple_select_wildcard() { fn simple_select_wildcard() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_select_simple_select_wildcard");
assert_eq!( assert_eq!(
super::exec_select( super::exec_select(
&global, &global,
@ -46,7 +46,7 @@ fn simple_select_wildcard() {
#[test] #[test]
fn simple_select_specified_same_order() { fn simple_select_specified_same_order() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_select_simple_select_specified_same_order");
assert_eq!( assert_eq!(
super::exec_select( super::exec_select(
&global, &global,
@ -61,7 +61,8 @@ fn simple_select_specified_same_order() {
#[test] #[test]
fn simple_select_specified_reversed_order() { fn simple_select_specified_reversed_order() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global =
TestGlobal::new_with_driver_id_instant_update("dml_select_simple_select_specified_reversed_order");
assert_eq!( assert_eq!(
super::exec_select( super::exec_select(
&global, &global,
@ -76,7 +77,7 @@ fn simple_select_specified_reversed_order() {
#[test] #[test]
fn select_null() { fn select_null() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_select_select_null");
assert_eq!( assert_eq!(
super::exec_select( super::exec_select(
&global, &global,
@ -91,7 +92,7 @@ fn select_null() {
#[test] #[test]
fn select_nonexisting() { fn select_nonexisting() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_select_select_nonexisting");
assert_eq!( assert_eq!(
super::exec_select( super::exec_select(
&global, &global,
@ -110,7 +111,7 @@ fn select_nonexisting() {
#[test] #[test]
fn select_all_wildcard() { fn select_all_wildcard() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_select_select_all_wildcard");
let ret = super::exec_select_all( let ret = super::exec_select_all(
&global, &global,
"create model myspace.mymodel(username: string, password: string)", "create model myspace.mymodel(username: string, password: string)",
@ -137,7 +138,7 @@ fn select_all_wildcard() {
#[test] #[test]
fn select_all_onefield() { fn select_all_onefield() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_select_select_all_onefield");
let ret = super::exec_select_all( let ret = super::exec_select_all(
&global, &global,
"create model myspace.mymodel(username: string, password: string)", "create model myspace.mymodel(username: string, password: string)",

@ -30,7 +30,7 @@ use crate::engine::{
#[test] #[test]
fn simple() { fn simple() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_update_simple");
assert_eq!( assert_eq!(
super::exec_update( super::exec_update(
&global, &global,
@ -49,7 +49,7 @@ fn simple() {
#[test] #[test]
fn with_null() { fn with_null() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_update_with_null");
assert_eq!( assert_eq!(
super::exec_update( super::exec_update(
&global, &global,
@ -66,7 +66,7 @@ fn with_null() {
#[test] #[test]
fn with_list() { fn with_list() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_update_with_list");
assert_eq!( assert_eq!(
super::exec_update( super::exec_update(
&global, &global,
@ -86,7 +86,7 @@ fn with_list() {
#[test] #[test]
fn fail_operation_on_null() { fn fail_operation_on_null() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_update_fail_operation_on_null");
assert_eq!( assert_eq!(
super::exec_update( super::exec_update(
&global, &global,
@ -106,7 +106,7 @@ fn fail_operation_on_null() {
#[test] #[test]
fn fail_unknown_fields() { fn fail_unknown_fields() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_update_fail_unknown_fields");
assert_eq!( assert_eq!(
super::exec_update( super::exec_update(
&global, &global,
@ -132,7 +132,7 @@ fn fail_unknown_fields() {
#[test] #[test]
fn fail_typedef_violation() { fn fail_typedef_violation() {
let global = TestGlobal::new_with_tmp_nullfs_driver(); let global = TestGlobal::new_with_driver_id_instant_update("dml_update_fail_typedef_violation");
assert_eq!( assert_eq!(
super::exec_update( super::exec_update(
&global, &global,

@ -97,6 +97,12 @@ pub enum QueryError {
QExecNeedLock = 112, QExecNeedLock = 112,
} }
direct_from! {
QueryError[_] => {
std::io::Error as SysServerError,
}
}
impl From<super::fractal::error::Error> for QueryError { impl From<super::fractal::error::Error> for QueryError {
fn from(e: super::fractal::error::Error) -> Self { fn from(e: super::fractal::error::Error) -> Self {
match e.kind() { match e.kind() {

@ -29,7 +29,7 @@ use {
crate::{ crate::{
engine::{ engine::{
error::{QueryError, QueryResult, RuntimeResult}, error::{QueryError, QueryResult, RuntimeResult},
storage::{safe_interfaces::FSInterface, GNSDriver, ModelDriver}, storage::{GNSDriver, ModelDriver},
}, },
util::compiler, util::compiler,
}, },
@ -38,13 +38,13 @@ use {
}; };
/// GNS driver /// GNS driver
pub struct FractalGNSDriver<Fs: FSInterface> { pub struct FractalGNSDriver {
status: util::Status, status: util::Status,
pub(super) txn_driver: GNSDriver<Fs>, pub(super) txn_driver: GNSDriver,
} }
impl<Fs: FSInterface> FractalGNSDriver<Fs> { impl FractalGNSDriver {
pub(super) fn new(txn_driver: GNSDriver<Fs>) -> Self { pub(super) fn new(txn_driver: GNSDriver) -> Self {
Self { Self {
status: util::Status::new_okay(), status: util::Status::new_okay(),
txn_driver: txn_driver, txn_driver: txn_driver,
@ -52,7 +52,7 @@ impl<Fs: FSInterface> FractalGNSDriver<Fs> {
} }
pub fn driver_context<T>( pub fn driver_context<T>(
&mut self, &mut self,
f: impl Fn(&mut GNSDriver<Fs>) -> RuntimeResult<T>, f: impl Fn(&mut GNSDriver) -> RuntimeResult<T>,
on_failure: impl Fn(), on_failure: impl Fn(),
) -> QueryResult<T> { ) -> QueryResult<T> {
if self.status.is_iffy() { if self.status.is_iffy() {
@ -70,23 +70,23 @@ impl<Fs: FSInterface> FractalGNSDriver<Fs> {
} }
} }
pub struct ModelDrivers<Fs: FSInterface> { pub struct ModelDrivers {
drivers: RwLock<HashMap<ModelUniqueID, FractalModelDriver<Fs>>>, drivers: RwLock<HashMap<ModelUniqueID, FractalModelDriver>>,
} }
impl<Fs: FSInterface> ModelDrivers<Fs> { impl ModelDrivers {
pub fn empty() -> Self { pub fn empty() -> Self {
Self { Self {
drivers: RwLock::new(HashMap::new()), drivers: RwLock::new(HashMap::new()),
} }
} }
pub fn drivers(&self) -> &RwLock<HashMap<ModelUniqueID, FractalModelDriver<Fs>>> { pub fn drivers(&self) -> &RwLock<HashMap<ModelUniqueID, FractalModelDriver>> {
&self.drivers &self.drivers
} }
pub fn count(&self) -> usize { pub fn count(&self) -> usize {
self.drivers.read().len() self.drivers.read().len()
} }
pub fn add_driver(&self, id: ModelUniqueID, batch_driver: ModelDriver<Fs>) { pub fn add_driver(&self, id: ModelUniqueID, batch_driver: ModelDriver) {
assert!(self assert!(self
.drivers .drivers
.write() .write()
@ -96,19 +96,19 @@ impl<Fs: FSInterface> ModelDrivers<Fs> {
pub fn remove_driver(&self, id: ModelUniqueID) { pub fn remove_driver(&self, id: ModelUniqueID) {
assert!(self.drivers.write().remove(&id).is_some()) assert!(self.drivers.write().remove(&id).is_some())
} }
pub fn into_inner(self) -> HashMap<ModelUniqueID, FractalModelDriver<Fs>> { pub fn into_inner(self) -> HashMap<ModelUniqueID, FractalModelDriver> {
self.drivers.into_inner() self.drivers.into_inner()
} }
} }
/// Model driver /// Model driver
pub struct FractalModelDriver<Fs: FSInterface> { pub struct FractalModelDriver {
status: Arc<util::Status>, status: Arc<util::Status>,
batch_driver: Mutex<ModelDriver<Fs>>, batch_driver: Mutex<ModelDriver>,
} }
impl<Fs: FSInterface> FractalModelDriver<Fs> { impl FractalModelDriver {
pub(in crate::engine::fractal) fn init(batch_driver: ModelDriver<Fs>) -> Self { pub(in crate::engine::fractal) fn init(batch_driver: ModelDriver) -> Self {
Self { Self {
status: Arc::new(util::Status::new_okay()), status: Arc::new(util::Status::new_okay()),
batch_driver: Mutex::new(batch_driver), batch_driver: Mutex::new(batch_driver),
@ -118,7 +118,7 @@ impl<Fs: FSInterface> FractalModelDriver<Fs> {
&self.status &self.status
} }
/// Returns a reference to the batch persist driver /// Returns a reference to the batch persist driver
pub fn batch_driver(&self) -> &Mutex<ModelDriver<Fs>> { pub fn batch_driver(&self) -> &Mutex<ModelDriver> {
&self.batch_driver &self.batch_driver
} }
pub fn close(self) -> RuntimeResult<()> { pub fn close(self) -> RuntimeResult<()> {

@ -35,7 +35,7 @@ use {
data::uuid::Uuid, data::uuid::Uuid,
error::ErrorKind, error::ErrorKind,
storage::{ storage::{
safe_interfaces::{paths_v1, LocalFS, StdModelBatch}, safe_interfaces::{paths_v1, StdModelBatch},
BatchStats, BatchStats,
}, },
}, },
@ -442,7 +442,7 @@ impl FractalMgr {
fn try_write_model_data_batch( fn try_write_model_data_batch(
model: &Model, model: &Model,
observed_size: usize, observed_size: usize,
mdl_driver: &super::drivers::FractalModelDriver<LocalFS>, mdl_driver: &super::drivers::FractalModelDriver,
) -> Result<(), (super::error::Error, BatchStats)> { ) -> Result<(), (super::error::Error, BatchStats)> {
if mdl_driver.status().is_iffy() { if mdl_driver.status().is_iffy() {
// don't mess this up any further // don't mess this up any further

@ -29,7 +29,7 @@ use {
core::{dml::QueryExecMeta, model::Model, GlobalNS}, core::{dml::QueryExecMeta, model::Model, GlobalNS},
data::uuid::Uuid, data::uuid::Uuid,
storage::{ storage::{
safe_interfaces::{paths_v1, FSInterface, LocalFS}, safe_interfaces::{paths_v1, FileSystem},
GNSDriver, ModelDriver, GNSDriver, ModelDriver,
}, },
}, },
@ -70,8 +70,8 @@ pub struct GlobalStateStart {
/// Must be called iff this is the only thread calling it /// Must be called iff this is the only thread calling it
pub unsafe fn load_and_enable_all( pub unsafe fn load_and_enable_all(
gns: GlobalNS, gns: GlobalNS,
gns_driver: GNSDriver<LocalFS>, gns_driver: GNSDriver,
model_drivers: ModelDrivers<LocalFS>, model_drivers: ModelDrivers,
) -> GlobalStateStart { ) -> GlobalStateStart {
let model_cnt_on_boot = model_drivers.count(); let model_cnt_on_boot = model_drivers.count();
let gns_driver = drivers::FractalGNSDriver::new(gns_driver); let gns_driver = drivers::FractalGNSDriver::new(gns_driver);
@ -97,13 +97,11 @@ pub unsafe fn load_and_enable_all(
/// Something that represents the global state /// Something that represents the global state
pub trait GlobalInstanceLike { pub trait GlobalInstanceLike {
type FileSystem: FSInterface;
const FS_IS_NON_NULL: bool = Self::FileSystem::NOT_NULL;
// stat // stat
fn get_max_delta_size(&self) -> usize; fn get_max_delta_size(&self) -> usize;
// global namespace // global namespace
fn state(&self) -> &GlobalNS; fn state(&self) -> &GlobalNS;
fn gns_driver(&self) -> &Mutex<drivers::FractalGNSDriver<Self::FileSystem>>; fn gns_driver(&self) -> &Mutex<drivers::FractalGNSDriver>;
// model drivers // model drivers
fn initialize_model_driver( fn initialize_model_driver(
&self, &self,
@ -148,12 +146,11 @@ pub trait GlobalInstanceLike {
} }
impl GlobalInstanceLike for Global { impl GlobalInstanceLike for Global {
type FileSystem = LocalFS;
// ns // ns
fn state(&self) -> &GlobalNS { fn state(&self) -> &GlobalNS {
self._namespace() self._namespace()
} }
fn gns_driver(&self) -> &Mutex<drivers::FractalGNSDriver<Self::FileSystem>> { fn gns_driver(&self) -> &Mutex<drivers::FractalGNSDriver> {
&self.get_state().gns_driver &self.get_state().gns_driver
} }
// taskmgr // taskmgr
@ -192,7 +189,7 @@ impl GlobalInstanceLike for Global {
model_uuid: Uuid, model_uuid: Uuid,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
// create dir // create dir
LocalFS::fs_create_dir(&paths_v1::model_dir( FileSystem::create_dir(&paths_v1::model_dir(
space_name, space_uuid, model_name, model_uuid, space_name, space_uuid, model_name, model_uuid,
))?; ))?;
// init driver // init driver
@ -271,16 +268,16 @@ impl Global {
/// The global state /// The global state
struct GlobalState { struct GlobalState {
gns: GlobalNS, gns: GlobalNS,
gns_driver: Mutex<drivers::FractalGNSDriver<LocalFS>>, gns_driver: Mutex<drivers::FractalGNSDriver>,
mdl_driver: ModelDrivers<LocalFS>, mdl_driver: ModelDrivers,
task_mgr: mgr::FractalMgr, task_mgr: mgr::FractalMgr,
} }
impl GlobalState { impl GlobalState {
fn new( fn new(
gns: GlobalNS, gns: GlobalNS,
gns_driver: drivers::FractalGNSDriver<LocalFS>, gns_driver: drivers::FractalGNSDriver,
mdl_driver: ModelDrivers<LocalFS>, mdl_driver: ModelDrivers,
task_mgr: mgr::FractalMgr, task_mgr: mgr::FractalMgr,
) -> Self { ) -> Self {
Self { Self {
@ -290,7 +287,7 @@ impl GlobalState {
task_mgr, task_mgr,
} }
} }
pub(self) fn get_mdl_drivers(&self) -> &ModelDrivers<LocalFS> { pub(self) fn get_mdl_drivers(&self) -> &ModelDrivers {
&self.mdl_driver &self.mdl_driver
} }
pub(self) fn fractal_mgr(&self) -> &mgr::FractalMgr { pub(self) fn fractal_mgr(&self) -> &mgr::FractalMgr {

@ -35,7 +35,7 @@ use {
error::ErrorKind, error::ErrorKind,
fractal::drivers::FractalModelDriver, fractal::drivers::FractalModelDriver,
storage::{ storage::{
safe_interfaces::{paths_v1, FSInterface, NullFS, StdModelBatch, VirtualFS}, safe_interfaces::{paths_v1, FileSystem, StdModelBatch},
BatchStats, GNSDriver, ModelDriver, BatchStats, GNSDriver, ModelDriver,
}, },
RuntimeResult, RuntimeResult,
@ -45,18 +45,18 @@ use {
}; };
/// A `test` mode global implementation /// A `test` mode global implementation
pub struct TestGlobal<Fs: FSInterface = VirtualFS> { pub struct TestGlobal {
gns: GlobalNS, gns: GlobalNS,
lp_queue: RwLock<Vec<Task<GenericTask>>>, lp_queue: RwLock<Vec<Task<GenericTask>>>,
#[allow(unused)] #[allow(unused)]
max_delta_size: usize, max_delta_size: usize,
txn_driver: Mutex<FractalGNSDriver<Fs>>, txn_driver: Mutex<FractalGNSDriver>,
model_drivers: RwLock<HashMap<ModelUniqueID, super::drivers::FractalModelDriver<Fs>>>, model_drivers: RwLock<HashMap<ModelUniqueID, super::drivers::FractalModelDriver>>,
max_data_pressure: usize, max_data_pressure: usize,
} }
impl<Fs: FSInterface> TestGlobal<Fs> { impl TestGlobal {
fn new(gns: GlobalNS, max_delta_size: usize, txn_driver: GNSDriver<Fs>) -> Self { fn new(gns: GlobalNS, max_delta_size: usize, txn_driver: GNSDriver) -> Self {
Self { Self {
gns, gns,
lp_queue: RwLock::default(), lp_queue: RwLock::default(),
@ -96,7 +96,12 @@ impl<Fs: FSInterface> TestGlobal<Fs> {
} }
} }
impl<Fs: FSInterface> TestGlobal<Fs> { impl TestGlobal {
pub fn new_with_driver_id_instant_update(log_name: &str) -> Self {
let mut me = Self::new_with_driver_id(log_name);
me.set_max_data_pressure(1);
me
}
pub fn new_with_driver_id(log_name: &str) -> Self { pub fn new_with_driver_id(log_name: &str) -> Self {
let gns = GlobalNS::empty(); let gns = GlobalNS::empty();
let driver = match GNSDriver::create_gns_with_name(log_name) { let driver = match GNSDriver::create_gns_with_name(log_name) {
@ -116,27 +121,11 @@ impl<Fs: FSInterface> TestGlobal<Fs> {
} }
} }
impl TestGlobal<VirtualFS> { impl GlobalInstanceLike for TestGlobal {
pub fn new_with_vfs_driver(log_name: &str) -> Self {
Self::new_with_driver_id(log_name)
}
}
impl TestGlobal<NullFS> {
pub fn new_with_nullfs_driver(log_name: &str) -> Self {
Self::new_with_driver_id(log_name)
}
pub fn new_with_tmp_nullfs_driver() -> Self {
Self::new_with_nullfs_driver("")
}
}
impl<Fs: FSInterface> GlobalInstanceLike for TestGlobal<Fs> {
type FileSystem = Fs;
fn state(&self) -> &GlobalNS { fn state(&self) -> &GlobalNS {
&self.gns &self.gns
} }
fn gns_driver(&self) -> &Mutex<FractalGNSDriver<Self::FileSystem>> { fn gns_driver(&self) -> &Mutex<FractalGNSDriver> {
&self.txn_driver &self.txn_driver
} }
fn taskmgr_post_high_priority(&self, task: Task<CriticalTask>) { fn taskmgr_post_high_priority(&self, task: Task<CriticalTask>) {
@ -190,7 +179,7 @@ impl<Fs: FSInterface> GlobalInstanceLike for TestGlobal<Fs> {
model_uuid: Uuid, model_uuid: Uuid,
) -> crate::engine::error::RuntimeResult<()> { ) -> crate::engine::error::RuntimeResult<()> {
// create model dir // create model dir
Fs::fs_create_dir(&paths_v1::model_dir( FileSystem::create_dir_all(&paths_v1::model_dir(
space_name, space_uuid, model_name, model_uuid, space_name, space_uuid, model_name, model_uuid,
))?; ))?;
let driver = ModelDriver::create_model_driver(&paths_v1::model_path( let driver = ModelDriver::create_model_driver(&paths_v1::model_path(
@ -204,7 +193,7 @@ impl<Fs: FSInterface> GlobalInstanceLike for TestGlobal<Fs> {
} }
} }
impl<Fs: FSInterface> Drop for TestGlobal<Fs> { impl Drop for TestGlobal {
fn drop(&mut self) { fn drop(&mut self) {
let mut txn_driver = self.txn_driver.lock(); let mut txn_driver = self.txn_driver.lock();
GNSDriver::close_driver(&mut txn_driver.txn_driver).unwrap(); GNSDriver::close_driver(&mut txn_driver.txn_driver).unwrap();

@ -410,7 +410,6 @@ macro_rules! local_mut {
}}; }};
} }
#[cfg(test)]
macro_rules! local_ref { macro_rules! local_ref {
($ident:ident, $call:expr) => {{ ($ident:ident, $call:expr) => {{
#[inline(always)] #[inline(always)]
@ -448,3 +447,16 @@ macro_rules! array {
$(#[$attr])*$vis const$name:[$ty;$name::LEN]=[$($expr),*];)* $(#[$attr])*$vis const$name:[$ty;$name::LEN]=[$($expr),*];)*
} }
} }
macro_rules! e {
($e:expr) => {{
#[inline(always)]
fn r<T, E1, E2>(r: Result<T, E1>) -> Result<T, E2>
where
E2: From<E1>,
{
r.map_err(::core::convert::From::from)
}
r($e)
}};
}

@ -0,0 +1,568 @@
/*
* Created on Thu Feb 29 2024
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2024, Sayan Nandan <nandansayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#![allow(dead_code)]
/*
file system
*/
use std::io::BufWriter;
#[cfg(test)]
use super::vfs::{VFileDescriptor, VirtualFS};
use {
crate::IoResult,
std::{
fs as std_fs,
io::{BufReader, Error, ErrorKind, Read, Seek, SeekFrom, Write},
},
};
pub struct FileSystem {}
impl Default for FileSystem {
fn default() -> Self {
Self::instance()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FSContext {
Local,
Virtual,
}
impl FileSystem {
pub fn instance() -> Self {
Self {}
}
fn context() -> FSContext {
local! { static CTX: FSContext = FSContext::Virtual; }
local_ref!(CTX, |ctx| *ctx)
}
}
impl FileSystem {
#[inline(always)]
pub fn read(path: &str) -> IoResult<Vec<u8>> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().read().get_data(path),
}
}
std_fs::read(path)
}
#[inline(always)]
pub fn create_dir(path: &str) -> IoResult<()> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().write().fs_create_dir(path),
}
}
std_fs::create_dir(path)
}
#[inline(always)]
pub fn create_dir_all(path: &str) -> IoResult<()> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().write().fs_create_dir_all(path),
}
}
std_fs::create_dir_all(path)
}
#[inline(always)]
pub fn remove_dir(path: &str) -> IoResult<()> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().write().fs_delete_dir(path),
}
}
std_fs::remove_dir(path)
}
#[inline(always)]
pub fn remove_dir_all(path: &str) -> IoResult<()> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().write().fs_delete_dir_all(path),
}
}
std_fs::remove_dir_all(path)
}
#[inline(always)]
pub fn remove_file(path: &str) -> IoResult<()> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().write().fs_remove_file(path),
}
}
std_fs::remove_file(path)
}
#[inline(always)]
pub fn rename(from: &str, to: &str) -> IoResult<()> {
#[cfg(test)]
{
match Self::context() {
FSContext::Local => {}
FSContext::Virtual => return VirtualFS::instance().write().fs_rename(from, to),
}
}
std_fs::rename(from, to)
}
}
/*
file traits
*/
pub trait FileRead {
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()>;
fn fread_exact_block<const N: usize>(&mut self) -> IoResult<[u8; N]> {
let mut blk = [0; N];
self.fread_exact(&mut blk).map(|_| blk)
}
}
pub trait FileWrite {
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64>;
fn fwrite_all(&mut self, buf: &[u8]) -> IoResult<()> {
self.fwrite_all_count(buf).1
}
fn fwrite_all_count(&mut self, buf: &[u8]) -> (u64, IoResult<()>) {
let len = buf.len() as u64;
let mut written = 0;
while written != len {
match self.fwrite(buf) {
Ok(0) => {
return (
written,
Err(Error::new(
ErrorKind::WriteZero,
format!("could only write {} of {} bytes", written, buf.len()),
)
.into()),
)
}
Ok(n) => written += n,
Err(e) => return (written, Err(e)),
}
}
(written, Ok(()))
}
}
pub trait FileWriteExt {
fn fsync_all(&mut self) -> IoResult<()>;
fn fsync_data(&mut self) -> IoResult<()>;
fn f_truncate(&mut self, new_size: u64) -> IoResult<()>;
}
pub trait FileExt {
fn f_len(&self) -> IoResult<u64>;
fn f_cursor(&mut self) -> IoResult<u64>;
fn f_seek_start(&mut self, offset: u64) -> IoResult<()>;
}
/*
file impls
*/
impl FileWrite for File {
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64> {
self.f.fwrite(buf)
}
}
impl FileRead for File {
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.f.fread_exact(buf)
}
}
impl FileWriteExt for File {
fn fsync_all(&mut self) -> IoResult<()> {
self.f.fsync_all()
}
fn fsync_data(&mut self) -> IoResult<()> {
self.f.fsync_data()
}
fn f_truncate(&mut self, new_size: u64) -> IoResult<()> {
self.f.f_truncate(new_size)
}
}
impl FileExt for File {
fn f_len(&self) -> IoResult<u64> {
self.f.f_len()
}
fn f_cursor(&mut self) -> IoResult<u64> {
self.f.f_cursor()
}
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
self.f.f_seek_start(offset)
}
}
/*
impls for local file
*/
trait LocalFile {
fn _mut(&mut self) -> &mut std_fs::File;
fn _ref(&self) -> &std_fs::File;
}
impl LocalFile for BufReader<std_fs::File> {
fn _mut(&mut self) -> &mut std_fs::File {
self.get_mut()
}
fn _ref(&self) -> &std_fs::File {
self.get_ref()
}
}
impl LocalFile for std_fs::File {
fn _mut(&mut self) -> &mut std_fs::File {
self
}
fn _ref(&self) -> &std_fs::File {
self
}
}
impl<W: Write> FileWrite for W {
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64> {
self.write(buf).map(|x| x as u64)
}
}
impl<R: Read> FileRead for R {
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.read_exact(buf)
}
}
impl<Lf: LocalFile> FileWriteExt for Lf {
fn fsync_all(&mut self) -> IoResult<()> {
self._mut().sync_all()
}
fn fsync_data(&mut self) -> IoResult<()> {
self._mut().sync_data()
}
fn f_truncate(&mut self, new_size: u64) -> IoResult<()> {
self._mut().set_len(new_size)
}
}
impl<Lf: LocalFile> FileExt for Lf {
fn f_len(&self) -> IoResult<u64> {
self._ref().metadata().map(|md| md.len())
}
fn f_cursor(&mut self) -> IoResult<u64> {
self._mut().stream_position()
}
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
self._mut().seek(SeekFrom::Start(offset)).map(|_| ())
}
}
/*
impls for vfile
*/
#[cfg(test)]
impl<Lf: FileWrite> FileWrite for AnyFile<Lf> {
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64> {
match self {
Self::Local(lf) => lf.fwrite(buf),
Self::Virtual(vf) => vf.get_mut(&mut VirtualFS::instance().write()).fwrite(buf),
}
}
}
#[cfg(test)]
impl<Lf: FileRead> FileRead for AnyFile<Lf> {
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
match self {
Self::Local(lf) => lf.fread_exact(buf),
Self::Virtual(vf) => vf
.get_mut(&mut VirtualFS::instance().write())
.fread_exact(buf),
}
}
}
#[cfg(test)]
impl<Lf: FileWriteExt> FileWriteExt for AnyFile<Lf> {
fn fsync_all(&mut self) -> IoResult<()> {
match self {
Self::Local(lf) => lf.fsync_all(),
Self::Virtual(_) => Ok(()),
}
}
fn fsync_data(&mut self) -> IoResult<()> {
match self {
Self::Local(lf) => lf.fsync_data(),
Self::Virtual(_) => Ok(()),
}
}
fn f_truncate(&mut self, new_size: u64) -> IoResult<()> {
match self {
Self::Local(lf) => lf.f_truncate(new_size),
Self::Virtual(vf) => vf
.get_mut(&mut VirtualFS::instance().write())
.truncate(new_size),
}
}
}
#[cfg(test)]
impl<Lf: FileExt> FileExt for AnyFile<Lf> {
fn f_len(&self) -> IoResult<u64> {
match self {
Self::Local(lf) => lf.f_len(),
Self::Virtual(vf) => vf.get_ref(&VirtualFS::instance().read()).length(),
}
}
fn f_cursor(&mut self) -> IoResult<u64> {
match self {
Self::Local(lf) => lf.f_cursor(),
Self::Virtual(vf) => vf.get_ref(&VirtualFS::instance().read()).cursor(),
}
}
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
match self {
Self::Local(lf) => lf.f_seek_start(offset),
Self::Virtual(vf) => vf
.get_mut(&mut VirtualFS::instance().write())
.seek_from_start(offset),
}
}
}
/*
file abstraction
*/
#[cfg(test)]
enum AnyFile<Lf = std_fs::File> {
Local(Lf),
Virtual(VFileDescriptor),
}
pub struct File {
#[cfg(test)]
f: AnyFile,
#[cfg(not(test))]
f: std_fs::File,
}
impl File {
pub fn open(path: &str) -> IoResult<Self> {
#[cfg(test)]
{
match FileSystem::context() {
FSContext::Local => {}
FSContext::Virtual => {
return VirtualFS::instance()
.write()
.fs_fopen_rw(path)
.map(|f| Self {
f: AnyFile::Virtual(f),
})
}
}
}
let file = std_fs::File::options().read(true).write(true).open(path)?;
Ok(Self {
#[cfg(test)]
f: AnyFile::Local(file),
#[cfg(not(test))]
f: file,
})
}
pub fn create(path: &str) -> IoResult<Self> {
#[cfg(test)]
{
match FileSystem::context() {
FSContext::Local => {}
FSContext::Virtual => {
return VirtualFS::instance()
.write()
.fs_fcreate_rw(path)
.map(|f| Self {
f: AnyFile::Virtual(f),
})
}
}
}
let file = std_fs::File::options()
.create_new(true)
.read(true)
.write(true)
.open(path)?;
Ok(Self {
#[cfg(test)]
f: AnyFile::Local(file),
#[cfg(not(test))]
f: file,
})
}
pub fn into_buffered_reader(self) -> BufferedReader {
BufferedReader::new(self.f)
}
pub fn into_buffered_writer(self) -> BufferedWriter {
BufferedWriter::new(self.f)
}
}
/*
buffered readers and writers
*/
pub struct BufferedReader {
#[cfg(test)]
f: AnyFile<BufReader<std_fs::File>>,
#[cfg(not(test))]
f: BufReader<std_fs::File>,
}
impl BufferedReader {
fn new(#[cfg(test)] f: AnyFile<std_fs::File>, #[cfg(not(test))] f: std_fs::File) -> Self {
Self {
#[cfg(test)]
f: match f {
AnyFile::Local(lf) => AnyFile::Local(BufReader::new(lf)),
AnyFile::Virtual(vf) => AnyFile::Virtual(vf),
},
#[cfg(not(test))]
f: BufReader::new(f),
}
}
pub fn into_inner(self) -> File {
File {
#[cfg(test)]
f: match self.f {
AnyFile::Local(lf) => AnyFile::Local(lf.into_inner()),
AnyFile::Virtual(vf) => AnyFile::Virtual(vf),
},
#[cfg(not(test))]
f: self.f.into_inner(),
}
}
}
impl FileRead for BufferedReader {
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.f.fread_exact(buf)
}
}
impl FileExt for BufferedReader {
fn f_len(&self) -> IoResult<u64> {
self.f.f_len()
}
fn f_cursor(&mut self) -> IoResult<u64> {
self.f.f_cursor()
}
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
self.f.f_seek_start(offset)
}
}
pub struct BufferedWriter {
#[cfg(test)]
f: AnyFile<BufWriter<std_fs::File>>,
#[cfg(not(test))]
f: BufWriter<std_fs::File>,
}
impl BufferedWriter {
pub fn into_inner(self) -> IoResult<File> {
let mut local;
#[cfg(test)]
{
match self.f {
AnyFile::Local(lf) => local = lf,
AnyFile::Virtual(vf) => {
return Ok(File {
f: AnyFile::Virtual(vf),
})
}
}
}
#[cfg(not(test))]
{
local = self.f;
}
local.flush()?;
let local = local.into_inner().unwrap();
Ok(File {
#[cfg(test)]
f: AnyFile::Local(local),
#[cfg(not(test))]
f: local,
})
}
pub fn flush(&mut self) -> IoResult<()> {
let local;
#[cfg(test)]
{
match self.f {
AnyFile::Local(ref mut l) => local = l,
AnyFile::Virtual(_) => return Ok(()),
}
}
#[cfg(not(test))]
{
local = &mut self.f;
}
local.flush()
}
fn new(#[cfg(test)] f: AnyFile<std_fs::File>, #[cfg(not(test))] f: std_fs::File) -> Self {
Self {
#[cfg(test)]
f: match f {
AnyFile::Local(lf) => AnyFile::Local(BufWriter::new(lf)),
AnyFile::Virtual(vf) => AnyFile::Virtual(vf),
},
#[cfg(not(test))]
f: BufWriter::new(f),
}
}
}

@ -1,205 +0,0 @@
/*
* Created on Sun Jan 07 2024
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2024, Sayan Nandan <nandansayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use {
super::fs_traits::{
FSInterface, FileInterface, FileInterfaceBufWrite, FileInterfaceExt, FileInterfaceRead,
FileInterfaceWrite, FileInterfaceWriteExt, FileOpen,
},
crate::engine::RuntimeResult,
std::{
fs::{self, File},
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
},
};
/*
local fs impls
*/
/// A type representing the host's local filesystem (or atleast where our data directory is)
pub struct LocalFS;
fn cvt<T, E1, E2: From<E1>>(r: Result<T, E1>) -> Result<T, E2> {
r.map_err(Into::into)
}
impl FSInterface for LocalFS {
type File = File;
fn fs_remove_file(fpath: &str) -> RuntimeResult<()> {
cvt(fs::remove_file(fpath))
}
fn fs_rename(from: &str, to: &str) -> RuntimeResult<()> {
cvt(fs::rename(from, to))
}
fn fs_create_dir(fpath: &str) -> RuntimeResult<()> {
cvt(fs::create_dir(fpath))
}
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()> {
cvt(fs::create_dir_all(fpath))
}
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()> {
cvt(fs::remove_dir(fpath))
}
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()> {
cvt(fs::remove_dir_all(fpath))
}
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<super::fs_traits::FileOpen<Self::File>> {
let r = || -> Result<_, std::io::Error> {
let f = File::options()
.create(true)
.read(true)
.write(true)
.open(fpath)?;
let md = f.metadata()?;
if md.len() == 0 {
Ok(FileOpen::Created(f))
} else {
Ok(FileOpen::Existing(f))
}
};
cvt(r())
}
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File> {
let f = File::options().read(true).write(true).open(fpath)?;
Ok(f)
}
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File> {
let f = File::options()
.create_new(true)
.read(true)
.write(true)
.open(fpath)?;
Ok(f)
}
}
/*
common impls for files
*/
impl<R: Read> FileInterfaceRead for R {
fn fread_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
cvt(self.read_exact(buf))
}
}
impl<W: Write> FileInterfaceWrite for W {
fn fwrite(&mut self, buf: &[u8]) -> RuntimeResult<u64> {
cvt(self.write(buf).map(|v| v as _))
}
}
/*
local file impls
*/
impl FileInterface for File {
type BufReader = BufReader<Self>;
type BufWriter = BufWriter<Self>;
fn upgrade_to_buffered_reader(self) -> RuntimeResult<Self::BufReader> {
Ok(BufReader::new(self))
}
fn upgrade_to_buffered_writer(self) -> RuntimeResult<Self::BufWriter> {
Ok(BufWriter::new(self))
}
fn downgrade_reader(r: Self::BufReader) -> RuntimeResult<Self> {
Ok(r.into_inner())
}
fn downgrade_writer(mut r: Self::BufWriter) -> RuntimeResult<Self> {
// TODO(@ohsayan): maybe we'll want to explicitly handle not syncing this?
r.flush()?;
let (me, err) = r.into_parts();
match err {
Ok(x) if x.is_empty() => Ok(me),
Ok(_) | Err(_) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to flush data from buffer into sink",
)
.into())
}
}
}
}
/// A trait for handling wrappers of [`std::fs::File`]
trait AsLocalFile {
fn file(&self) -> &File;
fn file_mut(&mut self) -> &mut File;
}
impl AsLocalFile for File {
fn file(&self) -> &File {
self
}
fn file_mut(&mut self) -> &mut File {
self
}
}
impl AsLocalFile for BufReader<File> {
fn file(&self) -> &File {
self.get_ref()
}
fn file_mut(&mut self) -> &mut File {
self.get_mut()
}
}
impl AsLocalFile for BufWriter<File> {
fn file(&self) -> &File {
self.get_ref()
}
fn file_mut(&mut self) -> &mut File {
self.get_mut()
}
}
impl FileInterfaceBufWrite for BufWriter<File> {
fn sync_write_cache(&mut self) -> RuntimeResult<()> {
// TODO(@ohsayan): maybe we'll want to explicitly handle not syncing this?
cvt(self.flush())
}
}
impl<F: AsLocalFile> FileInterfaceExt for F {
fn fext_length(&self) -> RuntimeResult<u64> {
Ok(self.file().metadata()?.len())
}
fn fext_cursor(&mut self) -> RuntimeResult<u64> {
cvt(self.file_mut().stream_position())
}
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()> {
cvt(self.file_mut().seek(SeekFrom::Start(by)).map(|_| ()))
}
}
impl FileInterfaceWriteExt for File {
fn fwext_truncate_to(&mut self, to: u64) -> RuntimeResult<()> {
cvt(self.set_len(to))
}
}

@ -1,176 +0,0 @@
/*
* Created on Sun Jan 07 2024
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2024, Sayan Nandan <nandansayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use {
crate::engine::RuntimeResult,
std::io::{Error as IoError, ErrorKind as IoErrorKind},
};
#[derive(Debug, PartialEq)]
/// Result of opening a file
/// - Created: newly created file
/// - Existing: existing file that was reopened
pub enum FileOpen<CF, EF = CF> {
/// new file
Created(CF),
/// existing file
Existing(EF),
}
pub trait FSInterface {
// settings
/// set to false if the file system is a special device like `/dev/null`
const NOT_NULL: bool = true;
// types
/// the file type that is returned by this file system
type File: FileInterface;
// functions
/// Remove a file
fn fs_remove_file(fpath: &str) -> RuntimeResult<()>;
/// Rename a file
fn fs_rename(from: &str, to: &str) -> RuntimeResult<()>;
/// Create a directory
fn fs_create_dir(fpath: &str) -> RuntimeResult<()>;
/// Create a directory and all corresponding path components
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()>;
/// Delete a directory
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()>;
/// Delete a directory and recursively remove all (if any) children
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()>;
/// Open or create a file in R/W mode
///
/// This will:
/// - Create a file if it doesn't exist
/// - Open a file it it does exist
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<FileOpen<Self::File>>;
/// Open an existing file
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File>;
/// Create a new file
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File>;
}
/// File interface definition
pub trait FileInterface:
FileInterfaceRead + FileInterfaceWrite + FileInterfaceWriteExt + FileInterfaceExt + Sized
{
/// A buffered reader implementation
type BufReader: FileInterfaceRead + FileInterfaceExt;
/// A buffered writer implementation
type BufWriter: FileInterfaceBufWrite;
/// Get a buffered reader for this file
fn upgrade_to_buffered_reader(self) -> RuntimeResult<Self::BufReader>;
/// Get a buffered writer for this file
fn upgrade_to_buffered_writer(self) -> RuntimeResult<Self::BufWriter>;
/// Get the file back from the buffered reader
fn downgrade_reader(r: Self::BufReader) -> RuntimeResult<Self>;
/// Get the file back from the buffered writer
fn downgrade_writer(r: Self::BufWriter) -> RuntimeResult<Self>;
}
pub trait FileInterfaceBufWrite: FileInterfaceWrite + FileInterfaceExt {
fn sync_write_cache(&mut self) -> RuntimeResult<()>;
}
/// Readable object
pub trait FileInterfaceRead {
/// Read in a block of the exact given length
fn fread_exact_block<const N: usize>(&mut self) -> RuntimeResult<[u8; N]> {
let mut ret = [0u8; N];
self.fread_exact(&mut ret)?;
Ok(ret)
}
/// Read in `n` bytes to fill the given buffer
fn fread_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()>;
}
/// Writable object
pub trait FileInterfaceWrite {
/// Attempt to write the buffer into this object, returning the number of bytes that were
/// written. It is **NOT GUARANTEED THAT ALL DATA WILL BE WRITTEN**
fn fwrite(&mut self, buf: &[u8]) -> RuntimeResult<u64>;
/// Attempt to write the entire buffer into this object, returning the number of bytes written
///
/// It is guaranteed that if the [`Result`] returned is [`Ok(())`], then the entire buffer was
/// written to disk.
fn fwrite_all_count(&mut self, buf: &[u8]) -> (u64, RuntimeResult<()>) {
let len = buf.len() as u64;
let mut written = 0;
while written != len {
match self.fwrite(buf) {
Ok(0) => {
return (
written,
Err(IoError::new(
IoErrorKind::WriteZero,
format!("could only write {} of {} bytes", written, buf.len()),
)
.into()),
)
}
Ok(n) => written += n,
Err(e) => return (written, Err(e)),
}
}
(written, Ok(()))
}
/// Attempt to write the entire buffer into this object
///
/// If this return [`Ok(())`] then it is guaranteed that all bytes have been written
fn fw_write_all(&mut self, buf: &[u8]) -> RuntimeResult<()> {
self.fwrite_all_count(buf).1
}
}
/// Advanced write traits
pub trait FileInterfaceWriteExt {
/// Sync data and metadata for this file
fn fwext_sync_all(&mut self) -> RuntimeResult<()> {
Ok(())
}
/// Sync data for this file
fn fwext_sync_data(&mut self) -> RuntimeResult<()> {
Ok(())
}
/// Sync meta for this file
fn fwext_sync_meta(&mut self) -> RuntimeResult<()> {
Ok(())
}
/// Truncate the size of the file to the given size
///
/// - If `to` > actual file length: the file is zero padded to fill `to - len`
/// - If `to` < actual file length: the file is trimmed to the size `to`
fn fwext_truncate_to(&mut self, to: u64) -> RuntimeResult<()>;
}
/// Advanced file access
pub trait FileInterfaceExt {
/// Returns the length of the file
fn fext_length(&self) -> RuntimeResult<u64>;
/// Returns the current cursor position of the file
fn fext_cursor(&mut self) -> RuntimeResult<u64>;
/// Seek by `from` bytes from the start of the file
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()>;
}

@ -30,7 +30,6 @@
//! traits that provide an unified API for all file systems irrespective of their base impl //! traits that provide an unified API for all file systems irrespective of their base impl
//! //!
pub mod fs_imp; pub mod fs;
#[cfg(test)] #[cfg(test)]
pub mod fs_test; mod vfs;
pub mod fs_traits;

@ -1,5 +1,5 @@
/* /*
* Created on Sun Jan 07 2024 * Created on Thu Feb 29 2024
* *
* This file is a part of Skytable * This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source * Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
@ -24,19 +24,10 @@
* *
*/ */
//! File system emulation #![allow(dead_code)]
//!
//! This directory contains some implementations of virtual file systems (either emulating `/tmp` or
//! `/dev/null`) that are directly implemented at the application level with some necessary changes
//! required for testing
//!
use { use {
super::fs_traits::{ crate::{engine::sync::cell::Lazy, IoResult},
FSInterface, FileInterface, FileInterfaceBufWrite, FileInterfaceExt, FileInterfaceRead,
FileInterfaceWrite, FileInterfaceWriteExt, FileOpen,
},
crate::engine::{sync::cell::Lazy, RuntimeResult},
parking_lot::RwLock, parking_lot::RwLock,
std::{ std::{
collections::{ collections::{
@ -48,58 +39,130 @@ use {
}; };
/* /*
vfs definitions virtual fs impl
---
*/ */
/// # VirtualFS /*
/// definitions
/// A virtual file system stored entirely in the process's memory (inclusive of swap if enabled; no explicit discrimination is made) ---
/// fs, node, dir, file
/// The virtual file system is generally intended for being utilized as an in-memory filesystem, primarily for testing */
/// purposes and has a lot of limitations.
///
/// It has support for:
/// - nested directories
/// - read/write permissions
/// - file position tracking and seeking
/// - directory operations
pub struct VirtualFS;
/// A virtual directory /// A virtual directory
type VDir = HashMap<Box<str>, VNode>; type VDir = HashMap<Box<str>, VNode>;
/// An iterator over the components of a file path (alias) /// An iterator over the components of a file path (alias)
type ComponentIter<'a> = std::iter::Take<std::vec::IntoIter<&'a str>>; type ComponentIter<'a> = std::iter::Take<std::vec::IntoIter<&'a str>>;
/** pub struct VirtualFS {
vnode root: HashMap<Box<str>, VNode>,
--- }
either a vfile or a vdir
*/
#[derive(Debug)] #[derive(Debug)]
pub(super) enum VNode { enum VNode {
Dir(HashMap<Box<str>, Self>), Dir(HashMap<Box<str>, Self>),
File(VFile), File(VFile),
} }
impl VNode { #[derive(Debug)]
fn as_dir_mut(&mut self) -> Option<&mut VDir> { pub(super) struct VFile {
match self { read: bool,
Self::Dir(d) => Some(d), write: bool,
Self::File(_) => None, data: Vec<u8>,
} pos: usize,
}
#[derive(Debug, PartialEq)]
/// Result of opening a file
/// - Created: newly created file
/// - Existing: existing file that was reopened
pub enum FileOpen<CF, EF = CF> {
/// new file
Created(CF),
/// existing file
Existing(EF),
}
pub struct VFileDescriptor(pub(super) Box<str>);
impl VFileDescriptor {
pub(super) fn get_ref<'a>(&self, vfs: &'a VirtualFS) -> &'a VFile {
vfs.with_file(&self.0, |f| Ok(f)).unwrap()
}
pub(super) fn get_mut<'a>(&self, vfs: &'a mut VirtualFS) -> &'a mut VFile {
vfs.with_file_mut(&self.0, |f| Ok(f)).unwrap()
}
}
impl Drop for VFileDescriptor {
fn drop(&mut self) {
VirtualFS::instance()
.write()
.with_file_mut(&self.0, |f| {
f.pos = 0;
f.write = false;
f.read = false;
Ok(())
})
.unwrap()
} }
} }
/* /*
vfile impl
*/ */
#[derive(Debug)] impl VFile {
pub struct VFile { pub fn truncate(&mut self, to: u64) -> IoResult<()> {
read: bool, if !self.write {
write: bool, return Err(Error::new(ErrorKind::PermissionDenied, "Write permission denied").into());
data: Vec<u8>, }
pos: usize, if to as usize > self.data.len() {
self.data.resize(to as usize, 0);
} else {
self.data.truncate(to as usize);
}
if self.pos > self.data.len() {
self.pos = self.data.len();
}
Ok(())
}
pub fn length(&self) -> IoResult<u64> {
Ok(self.data.len() as u64)
}
pub fn cursor(&self) -> IoResult<u64> {
Ok(self.pos as u64)
}
pub fn seek_from_start(&mut self, by: u64) -> IoResult<()> {
if by > self.data.len() as u64 {
return Err(Error::new(ErrorKind::InvalidInput, "Can't seek beyond file's end").into());
}
self.pos = by as usize;
Ok(())
}
pub fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
if !self.read {
return Err(Error::new(ErrorKind::PermissionDenied, "Read permission denied").into());
}
let available_bytes = self.current().len();
if available_bytes < buf.len() {
return Err(Error::from(ErrorKind::UnexpectedEof).into());
}
buf.copy_from_slice(&self.data[self.pos..self.pos + buf.len()]);
self.pos += buf.len();
Ok(())
}
pub fn fwrite(&mut self, bytes: &[u8]) -> IoResult<u64> {
if !self.write {
return Err(Error::new(ErrorKind::PermissionDenied, "Write permission denied").into());
}
if self.pos + bytes.len() > self.data.len() {
self.data.resize(self.pos + bytes.len(), 0);
}
self.data[self.pos..self.pos + bytes.len()].copy_from_slice(bytes);
self.pos += bytes.len();
Ok(bytes.len() as _)
}
} }
impl VFile { impl VFile {
@ -114,171 +177,86 @@ impl VFile {
fn current(&self) -> &[u8] { fn current(&self) -> &[u8] {
&self.data[self.pos..] &self.data[self.pos..]
} }
} fn fw_write_all(&mut self, bytes: &[u8]) -> IoResult<()> {
self.fwrite(bytes).map(|_| ())
mod err {
//! Errors
//!
//! These are custom errors returned by the virtual file system
use {
crate::engine::RuntimeResult,
std::io::{Error, ErrorKind},
};
pub(super) fn item_is_not_file<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found directory, not a file").into())
}
pub(super) fn file_in_dir_path<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found file in directory path").into())
}
pub(super) fn dir_missing_in_path<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "could not find directory in path").into())
}
pub(super) fn could_not_find_item<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::NotFound, "could not find item").into())
} }
} }
mod util { impl VNode {
use { fn as_dir_mut(&mut self) -> Option<&mut VDir> {
super::{err, ComponentIter, VDir, VNode}, match self {
crate::engine::RuntimeResult, Self::Dir(d) => Some(d),
}; Self::File(_) => None,
pub(super) fn split_parts(fpath: &str) -> Vec<&str> {
fpath.split("/").collect()
}
pub(super) fn split_target_and_components(fpath: &str) -> (&str, ComponentIter) {
let parts = split_parts(fpath);
let target = parts.last().unwrap();
let component_len = parts.len() - 1;
(target, parts.into_iter().take(component_len))
}
pub(super) fn find_target_dir_mut<'a>(
components: ComponentIter,
mut current: &'a mut VDir,
) -> RuntimeResult<&'a mut VDir> {
for component in components {
match current.get_mut(component) {
Some(VNode::Dir(d)) => current = d,
Some(VNode::File(_)) => return err::file_in_dir_path(),
None => return err::dir_missing_in_path(),
}
}
Ok(current)
}
pub(super) fn find_target_dir<'a>(
components: ComponentIter,
mut current: &'a VDir,
) -> RuntimeResult<&'a VDir> {
for component in components {
match current.get(component) {
Some(VNode::Dir(d)) => current = d,
Some(VNode::File(_)) => return err::file_in_dir_path(),
None => return err::dir_missing_in_path(),
}
} }
Ok(current)
} }
} }
/*
vfs impl:
- nested directory structure
- make parents
- make child
*/
impl VirtualFS { impl VirtualFS {
pub fn fetch_raw_data(file_name: &str) -> RuntimeResult<Vec<u8>> { pub fn instance() -> &'static RwLock<Self> {
Self::with_file(file_name, |f| Ok(f.data.clone())) static GLOBAL_VFS: Lazy<RwLock<VirtualFS>, fn() -> RwLock<VirtualFS>> =
Lazy::new(|| RwLock::new(VirtualFS::new()));
&GLOBAL_VFS
} }
/// Get a handle to the virtual filesystem pub fn get_data(&self, path: &str) -> IoResult<Vec<u8>> {
fn handle() -> &'static RwLock<VDir> { self.with_file(path, |f| Ok(f.data.clone()))
static VFS: Lazy<RwLock<VDir>, fn() -> RwLock<VDir>> = Lazy::new(|| Default::default());
&VFS
} }
fn with_file_mut<T>( pub fn fs_fcreate_rw(&mut self, fpath: &str) -> IoResult<VFileDescriptor> {
fpath: &str,
mut f: impl FnMut(&mut VFile) -> RuntimeResult<T>,
) -> RuntimeResult<T> {
let mut vfs = Self::handle().write();
let (target_file, components) = util::split_target_and_components(fpath); let (target_file, components) = util::split_target_and_components(fpath);
let target_dir = util::find_target_dir_mut(components, &mut vfs)?; let target_dir = util::find_target_dir_mut(components, &mut self.root)?;
match target_dir.get_mut(target_file) { match target_dir.entry(target_file.into()) {
Some(VNode::File(file)) => f(file), Entry::Occupied(k) => {
Some(VNode::Dir(_)) => return err::item_is_not_file(), match k.get() {
None => return Err(Error::from(ErrorKind::NotFound).into()), VNode::Dir(_) => {
} return Err(Error::new(
} ErrorKind::AlreadyExists,
fn with_file<T>( "found directory with same name where file was to be created",
fpath: &str, )
mut f: impl FnMut(&VFile) -> RuntimeResult<T>, .into());
) -> RuntimeResult<T> { }
let vfs = Self::handle().read(); VNode::File(_) => {
let (target_file, components) = util::split_target_and_components(fpath); // the file already exists
let target_dir = util::find_target_dir(components, &vfs)?; return Err(Error::new(
match target_dir.get(target_file) { ErrorKind::AlreadyExists,
Some(VNode::File(file)) => f(file), "the file already exists",
Some(VNode::Dir(_)) => return err::item_is_not_file(), )
None => return Err(Error::from(ErrorKind::NotFound).into()), .into());
} }
}
fn with_item_mut<T>(
fpath: &str,
f: impl Fn(OccupiedEntry<Box<str>, VNode>) -> RuntimeResult<T>,
) -> RuntimeResult<T> {
let mut vfs = Self::handle().write();
let mut current = &mut *vfs;
// process components
let (target, components) = util::split_target_and_components(fpath);
for component in components {
match current.get_mut(component) {
Some(VNode::Dir(dir)) => {
current = dir;
} }
Some(VNode::File(_)) => return err::file_in_dir_path(),
None => return err::dir_missing_in_path(),
} }
} Entry::Vacant(v) => {
match current.entry(target.into()) { // no file exists, we can create this
Entry::Occupied(item) => return f(item), v.insert(VNode::File(VFile::new(true, true, vec![], 0)));
Entry::Vacant(_) => return err::could_not_find_item(), Ok(VFileDescriptor(fpath.into()))
}
} }
} }
fn delete_dir(fpath: &str, allow_if_non_empty: bool) -> RuntimeResult<()> { pub fn fs_fopen_rw(&mut self, fpath: &str) -> IoResult<VFileDescriptor> {
Self::with_item_mut(fpath, |node| match node.get() { self.with_file_mut(fpath, |f| {
VNode::Dir(d) => { f.read = true;
if allow_if_non_empty || d.is_empty() { f.write = true;
node.remove(); Ok(VFileDescriptor(fpath.into()))
return Ok(());
}
return Err(Error::new(ErrorKind::InvalidInput, "directory is not empty").into());
}
VNode::File(_) => return err::file_in_dir_path(),
}) })
} }
} pub fn fs_rename(&mut self, from: &str, to: &str) -> IoResult<()> {
impl FSInterface for VirtualFS {
type File = VFileDescriptor;
fn fs_rename(from: &str, to: &str) -> RuntimeResult<()> {
// get file data // get file data
let data = VirtualFS::with_file(from, |f| Ok(f.data.clone()))?; let data = self.with_file(from, |f| Ok(f.data.clone()))?;
// create new file // create new file
let file = VirtualFS::fs_fopen_or_create_rw(to)?; let file = self.fs_fopen_or_create_rw(to)?;
match file { match file {
FileOpen::Created(mut c) => { FileOpen::Created(c) => {
c.fw_write_all(&data)?; c.get_mut(self).fw_write_all(&data)?;
} }
FileOpen::Existing(mut e) => { FileOpen::Existing(c) => {
e.fwext_truncate_to(0)?; let file = c.get_mut(self);
e.fw_write_all(&data)?; file.truncate(0)?;
file.fw_write_all(&data)?;
} }
} }
// delete old file // delete old file
Self::fs_remove_file(from) self.fs_remove_file(from)
} }
fn fs_remove_file(fpath: &str) -> RuntimeResult<()> { pub fn fs_remove_file(&mut self, fpath: &str) -> IoResult<()> {
VirtualFS::with_item_mut(fpath, |e| match e.get() { self.with_item_mut(fpath, |e| match e.get() {
VNode::File(_) => { VNode::File(_) => {
e.remove(); e.remove();
Ok(()) Ok(())
@ -286,11 +264,9 @@ impl FSInterface for VirtualFS {
_ => return err::item_is_not_file(), _ => return err::item_is_not_file(),
}) })
} }
fn fs_create_dir(fpath: &str) -> RuntimeResult<()> { pub fn fs_create_dir(&mut self, fpath: &str) -> IoResult<()> {
// get vfs
let mut vfs = VirtualFS::handle().write();
// get root dir // get root dir
let mut current = &mut *vfs; let mut current = &mut self.root;
// process components // process components
let (target, mut components) = util::split_target_and_components(fpath); let (target, mut components) = util::split_target_and_components(fpath);
while let Some(component) = components.next() { while let Some(component) = components.next() {
@ -310,9 +286,8 @@ impl FSInterface for VirtualFS {
} }
} }
} }
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()> { pub fn fs_create_dir_all(&mut self, fpath: &str) -> IoResult<()> {
let mut vfs = VirtualFS::handle().write(); fn create_ahead(mut ahead: &[&str], current: &mut VDir) -> IoResult<()> {
fn create_ahead(mut ahead: &[&str], current: &mut VDir) -> RuntimeResult<()> {
if ahead.is_empty() { if ahead.is_empty() {
return Ok(()); return Ok(());
} }
@ -335,19 +310,26 @@ impl FSInterface for VirtualFS {
} }
} }
let pieces = util::split_parts(fpath); let pieces = util::split_parts(fpath);
create_ahead(&pieces, &mut *vfs) create_ahead(&pieces, &mut self.root)
} }
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()> { pub fn fs_delete_dir(&mut self, fpath: &str) -> IoResult<()> {
VirtualFS::delete_dir(fpath, false) self.dir_delete(fpath, false)
} }
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()> { pub fn fs_delete_dir_all(&mut self, fpath: &str) -> IoResult<()> {
VirtualFS::delete_dir(fpath, true) self.dir_delete(fpath, true)
}
}
impl VirtualFS {
fn new() -> Self {
Self {
root: HashMap::new(),
}
} }
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<FileOpen<Self::File>> { fn fs_fopen_or_create_rw(&mut self, fpath: &str) -> IoResult<FileOpen<VFileDescriptor>> {
let mut vfs = VirtualFS::handle().write();
// components // components
let (target_file, components) = util::split_target_and_components(fpath); let (target_file, components) = util::split_target_and_components(fpath);
let target_dir = util::find_target_dir_mut(components, &mut vfs)?; let target_dir = util::find_target_dir_mut(components, &mut self.root)?;
match target_dir.entry(target_file.into()) { match target_dir.entry(target_file.into()) {
Entry::Occupied(mut oe) => match oe.get_mut() { Entry::Occupied(mut oe) => match oe.get_mut() {
VNode::File(f) => { VNode::File(f) => {
@ -363,248 +345,128 @@ impl FSInterface for VirtualFS {
} }
} }
} }
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File> { fn with_file_mut<'a, T>(
let mut vfs = VirtualFS::handle().write(); &'a mut self,
fpath: &str,
mut f: impl FnMut(&'a mut VFile) -> IoResult<T>,
) -> IoResult<T> {
let (target_file, components) = util::split_target_and_components(fpath); let (target_file, components) = util::split_target_and_components(fpath);
let target_dir = util::find_target_dir_mut(components, &mut vfs)?; let target_dir = util::find_target_dir_mut(components, &mut self.root)?;
match target_dir.entry(target_file.into()) { match target_dir.get_mut(target_file) {
Entry::Occupied(k) => { Some(VNode::File(file)) => f(file),
match k.get() { Some(VNode::Dir(_)) => return err::item_is_not_file(),
VNode::Dir(_) => { None => return Err(Error::from(ErrorKind::NotFound).into()),
return Err(Error::new(
ErrorKind::AlreadyExists,
"found directory with same name where file was to be created",
)
.into());
}
VNode::File(_) => {
// the file already exists
return Err(Error::new(
ErrorKind::AlreadyExists,
"the file already exists",
)
.into());
}
}
}
Entry::Vacant(v) => {
// no file exists, we can create this
v.insert(VNode::File(VFile::new(true, true, vec![], 0)));
Ok(VFileDescriptor(fpath.into()))
}
} }
} }
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File> { fn with_file<'a, T>(
VirtualFS::with_file_mut(fpath, |f| { &'a self,
f.read = true; fpath: &str,
f.write = true; mut f: impl FnMut(&'a VFile) -> IoResult<T>,
Ok(VFileDescriptor(fpath.into())) ) -> IoResult<T> {
}) let (target_file, components) = util::split_target_and_components(fpath);
} let target_dir = util::find_target_dir(components, &self.root)?;
} match target_dir.get(target_file) {
Some(VNode::File(file)) => f(file),
/* Some(VNode::Dir(_)) => return err::item_is_not_file(),
vfile & descriptor impls None => return Err(Error::from(ErrorKind::NotFound).into()),
(this is our `File` but a temporary, completely in-memory file) }
*/
pub struct VFileDescriptor(Box<str>);
impl Drop for VFileDescriptor {
fn drop(&mut self) {
let _ = VirtualFS::with_file_mut(&self.0, |f| {
f.read = false;
f.write = false;
f.pos = 0;
Ok(())
});
}
}
impl FileInterface for VFileDescriptor {
type BufReader = Self;
type BufWriter = Self;
fn upgrade_to_buffered_reader(self) -> RuntimeResult<Self::BufReader> {
Ok(self)
}
fn upgrade_to_buffered_writer(self) -> RuntimeResult<Self::BufWriter> {
Ok(self)
}
fn downgrade_reader(r: Self::BufReader) -> RuntimeResult<Self> {
Ok(r)
}
fn downgrade_writer(r: Self::BufWriter) -> RuntimeResult<Self> {
Ok(r)
}
}
impl FileInterfaceRead for VFileDescriptor {
fn fread_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
VirtualFS::with_file_mut(&self.0, |file| {
if !file.read {
return Err(
Error::new(ErrorKind::PermissionDenied, "Read permission denied").into(),
);
}
let available_bytes = file.current().len();
if available_bytes < buf.len() {
return Err(Error::from(ErrorKind::UnexpectedEof).into());
}
buf.copy_from_slice(&file.data[file.pos..file.pos + buf.len()]);
file.pos += buf.len();
Ok(())
})
}
}
impl FileInterfaceWrite for VFileDescriptor {
fn fwrite(&mut self, bytes: &[u8]) -> RuntimeResult<u64> {
VirtualFS::with_file_mut(&self.0, |file| {
if !file.write {
return Err(
Error::new(ErrorKind::PermissionDenied, "Write permission denied").into(),
);
}
if file.pos + bytes.len() > file.data.len() {
file.data.resize(file.pos + bytes.len(), 0);
}
file.data[file.pos..file.pos + bytes.len()].copy_from_slice(bytes);
file.pos += bytes.len();
Ok(bytes.len() as _)
})
} }
} fn with_item_mut<T>(
&mut self,
impl FileInterfaceWriteExt for VFileDescriptor { fpath: &str,
fn fwext_truncate_to(&mut self, to: u64) -> RuntimeResult<()> { f: impl Fn(OccupiedEntry<Box<str>, VNode>) -> IoResult<T>,
VirtualFS::with_file_mut(&self.0, |file| { ) -> IoResult<T> {
if !file.write { // process components
return Err( let (target, components) = util::split_target_and_components(fpath);
Error::new(ErrorKind::PermissionDenied, "Write permission denied").into(), let mut current = &mut self.root;
); for component in components {
} match current.get_mut(component) {
if to as usize > file.data.len() { Some(VNode::Dir(dir)) => {
file.data.resize(to as usize, 0); current = dir;
} else { }
file.data.truncate(to as usize); Some(VNode::File(_)) => return err::file_in_dir_path(),
} None => return err::dir_missing_in_path(),
if file.pos > file.data.len() {
file.pos = file.data.len();
} }
Ok(()) }
}) match current.entry(target.into()) {
} Entry::Occupied(item) => return f(item),
} Entry::Vacant(_) => return err::could_not_find_item(),
}
impl FileInterfaceBufWrite for VFileDescriptor {
fn sync_write_cache(&mut self) -> RuntimeResult<()> {
Ok(())
} }
} fn dir_delete(&mut self, fpath: &str, allow_if_non_empty: bool) -> IoResult<()> {
self.with_item_mut(fpath, |node| match node.get() {
impl FileInterfaceExt for VFileDescriptor { VNode::Dir(d) => {
fn fext_length(&self) -> RuntimeResult<u64> { if allow_if_non_empty || d.is_empty() {
VirtualFS::with_file(&self.0, |f| Ok(f.data.len() as u64)) node.remove();
} return Ok(());
fn fext_cursor(&mut self) -> RuntimeResult<u64> { }
VirtualFS::with_file(&self.0, |f| Ok(f.pos as u64)) return Err(Error::new(ErrorKind::InvalidInput, "directory is not empty").into());
}
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()> {
VirtualFS::with_file_mut(&self.0, |file| {
if by > file.data.len() as u64 {
return Err(
Error::new(ErrorKind::InvalidInput, "Can't seek beyond file's end").into(),
);
} }
file.pos = by as usize; VNode::File(_) => return err::file_in_dir_path(),
Ok(())
}) })
} }
} }
/// An application level implementation of `/dev/null` with some changes mod err {
pub struct NullFS; //! Errors
/// A handle to a file in `/dev/null` (emulated) //!
pub struct NullFile; //! These are custom errors returned by the virtual file system
use {
impl FSInterface for NullFS { crate::IoResult,
type File = NullFile; std::io::{Error, ErrorKind},
fn fs_remove_file(_: &str) -> RuntimeResult<()> { };
Ok(()) pub(super) fn item_is_not_file<T>() -> IoResult<T> {
} Err(Error::new(ErrorKind::InvalidInput, "found directory, not a file").into())
fn fs_rename(_: &str, _: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_create_dir(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_create_dir_all(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_delete_dir(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_delete_dir_all(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_fopen_or_create_rw(_: &str) -> RuntimeResult<FileOpen<Self::File>> {
Ok(FileOpen::Created(NullFile))
}
fn fs_fopen_rw(_: &str) -> RuntimeResult<Self::File> {
Ok(NullFile)
}
fn fs_fcreate_rw(_: &str) -> RuntimeResult<Self::File> {
Ok(NullFile)
}
}
impl FileInterface for NullFile {
type BufReader = Self;
type BufWriter = Self;
fn upgrade_to_buffered_reader(self) -> RuntimeResult<Self::BufReader> {
Ok(self)
}
fn upgrade_to_buffered_writer(self) -> RuntimeResult<Self::BufWriter> {
Ok(self)
}
fn downgrade_reader(r: Self::BufReader) -> RuntimeResult<Self> {
Ok(r)
}
fn downgrade_writer(r: Self::BufWriter) -> RuntimeResult<Self> {
Ok(r)
} }
} pub(super) fn file_in_dir_path<T>() -> IoResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found file in directory path").into())
impl FileInterfaceWrite for NullFile {
fn fwrite(&mut self, buf: &[u8]) -> RuntimeResult<u64> {
Ok(buf.len() as _)
} }
} pub(super) fn dir_missing_in_path<T>() -> IoResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "could not find directory in path").into())
impl FileInterfaceWriteExt for NullFile {
fn fwext_truncate_to(&mut self, _: u64) -> RuntimeResult<()> {
Ok(())
} }
} pub(super) fn could_not_find_item<T>() -> IoResult<T> {
Err(Error::new(ErrorKind::NotFound, "could not find item").into())
impl FileInterfaceRead for NullFile {
fn fread_exact(&mut self, _: &mut [u8]) -> RuntimeResult<()> {
Ok(())
} }
} }
impl FileInterfaceExt for NullFile { mod util {
fn fext_length(&self) -> RuntimeResult<u64> { use {
Ok(0) super::{err, ComponentIter, VDir, VNode},
crate::IoResult,
};
pub(super) fn split_parts(fpath: &str) -> Vec<&str> {
fpath.split("/").collect()
} }
fn fext_cursor(&mut self) -> RuntimeResult<u64> { pub(super) fn split_target_and_components(fpath: &str) -> (&str, ComponentIter) {
Ok(0) let parts = split_parts(fpath);
let target = parts.last().unwrap();
let component_len = parts.len() - 1;
(target, parts.into_iter().take(component_len))
} }
fn fext_seek_ahead_from_start_by(&mut self, _: u64) -> RuntimeResult<()> { pub(super) fn find_target_dir_mut<'a>(
Ok(()) components: ComponentIter,
mut current: &'a mut VDir,
) -> IoResult<&'a mut VDir> {
for component in components {
match current.get_mut(component) {
Some(VNode::Dir(d)) => current = d,
Some(VNode::File(_)) => return err::file_in_dir_path(),
None => return err::dir_missing_in_path(),
}
}
Ok(current)
} }
} pub(super) fn find_target_dir<'a>(
impl FileInterfaceBufWrite for NullFile { components: ComponentIter,
fn sync_write_cache(&mut self) -> RuntimeResult<()> { mut current: &'a VDir,
Ok(()) ) -> IoResult<&'a VDir> {
for component in components {
match current.get(component) {
Some(VNode::Dir(d)) => current = d,
Some(VNode::File(_)) => return err::file_in_dir_path(),
None => return err::dir_missing_in_path(),
}
}
Ok(current)
} }
} }

@ -34,13 +34,18 @@ pub mod rw;
use { use {
super::super::super::{ super::super::super::{
interface::fs_traits::{FileInterfaceRead, FileInterfaceWrite},
static_meta::{HostArch, HostEndian, HostOS, HostPointerWidth, SDSS_MAGIC_8B}, static_meta::{HostArch, HostEndian, HostOS, HostPointerWidth, SDSS_MAGIC_8B},
versions::{self, DriverVersion, FileSpecifierVersion, HeaderVersion, ServerVersion}, versions::{self, DriverVersion, FileSpecifierVersion, HeaderVersion, ServerVersion},
}, },
crate::{ crate::{
engine::{error::StorageError, mem::unsafe_apis::memcpy, RuntimeResult}, engine::{
error::StorageError,
mem::unsafe_apis::memcpy,
storage::common::interface::fs::{FileRead, FileWrite},
RuntimeResult,
},
util::os, util::os,
IoResult,
}, },
std::{ std::{
mem::{transmute, ManuallyDrop}, mem::{transmute, ManuallyDrop},
@ -412,17 +417,14 @@ pub trait FileSpecV1 {
) -> RuntimeResult<Self::Metadata>; ) -> RuntimeResult<Self::Metadata>;
/// read and validate metadata (only override if you need to) /// read and validate metadata (only override if you need to)
fn read_metadata( fn read_metadata(
f: &mut impl FileInterfaceRead, f: &mut impl FileRead,
v_args: Self::DecodeArgs, v_args: Self::DecodeArgs,
) -> RuntimeResult<Self::Metadata> { ) -> RuntimeResult<Self::Metadata> {
let md = HeaderV1::decode(f.fread_exact_block()?)?; let md = HeaderV1::decode(f.fread_exact_block()?)?;
Self::validate_metadata(md, v_args) Self::validate_metadata(md, v_args)
} }
/// write metadata /// write metadata
fn write_metadata( fn write_metadata(f: &mut impl FileWrite, args: Self::EncodeArgs) -> IoResult<Self::Metadata>;
f: &mut impl FileInterfaceWrite,
args: Self::EncodeArgs,
) -> RuntimeResult<Self::Metadata>;
fn metadata_to_block(args: Self::EncodeArgs) -> RuntimeResult<Vec<u8>> { fn metadata_to_block(args: Self::EncodeArgs) -> RuntimeResult<Vec<u8>> {
let mut v = Vec::new(); let mut v = Vec::new();
Self::write_metadata(&mut v, args)?; Self::write_metadata(&mut v, args)?;
@ -479,15 +481,12 @@ impl<Sfs: SimpleFileSpecV1> FileSpecV1 for Sfs {
Err(StorageError::HeaderDecodeVersionMismatch.into()) Err(StorageError::HeaderDecodeVersionMismatch.into())
} }
} }
fn write_metadata( fn write_metadata(f: &mut impl FileWrite, _: Self::EncodeArgs) -> IoResult<Self::Metadata> {
f: &mut impl FileInterfaceWrite,
_: Self::EncodeArgs,
) -> RuntimeResult<Self::Metadata> {
let (md, block) = HeaderV1::<Self::HeaderSpec>::encode_return( let (md, block) = HeaderV1::<Self::HeaderSpec>::encode_return(
Self::FILE_CLASS, Self::FILE_CLASS,
Self::FILE_SPECIFIER, Self::FILE_SPECIFIER,
Self::FILE_SPECFIER_VERSION, Self::FILE_SPECFIER_VERSION,
); );
f.fw_write_all(&block).map(|_| md) f.fwrite_all(&block).map(|_| md)
} }
} }

@ -32,15 +32,13 @@ use {
mem::fixed_vec::FixedVec, mem::fixed_vec::FixedVec,
storage::common::{ storage::common::{
checksum::SCrc64, checksum::SCrc64,
interface::fs_traits::{ interface::fs::{BufferedReader, File, FileExt, FileRead, FileWrite, FileWriteExt},
FSInterface, FileInterface, FileInterfaceBufWrite, FileInterfaceExt,
FileInterfaceRead, FileInterfaceWrite, FileInterfaceWriteExt, FileOpen,
},
sdss::sdss_r1::FileSpecV1, sdss::sdss_r1::FileSpecV1,
}, },
RuntimeResult, RuntimeResult,
}, },
util::os::SysIOError, util::os::SysIOError,
IoResult,
}, },
std::mem, std::mem,
}; };
@ -51,109 +49,56 @@ use {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
/// A file with it's layout defined by a SDSS file specification /// A file with it's layout defined by a SDSS file specification
pub struct SdssFile<F, S: FileSpecV1> { pub struct SdssFile<S: FileSpecV1, F = File> {
file: F, file: F,
meta: S::Metadata, meta: S::Metadata,
} }
impl<F, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1, F> SdssFile<S, F> {
fn new(file: F, meta: S::Metadata) -> Self { fn new(file: F, meta: S::Metadata) -> Self {
Self { file, meta } Self { file, meta }
} }
/// Returns the SDSS metadata associated with this file
pub fn sdss_metadata(&self) -> &S::Metadata {
&self.meta
}
} }
impl<F: FileInterface, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1> SdssFile<S> {
/// Open an existing SDSS based file (with no validation arguments) /// Open an existing SDSS based file (with no validation arguments)
pub fn open<Fs: FSInterface<File = F>>(path: &str) -> RuntimeResult<Self> pub fn open(path: &str) -> RuntimeResult<Self>
where where
S: FileSpecV1<DecodeArgs = ()>, S: FileSpecV1<DecodeArgs = ()>,
{ {
let mut f = Fs::fs_fopen_rw(path)?; let mut f = File::open(path)?;
let md = S::read_metadata(&mut f, ())?; let md = S::read_metadata(&mut f, ())?;
Ok(Self::new(f, md)) Ok(Self::new(f, md))
} }
/// Create a new SDSS based file (with no initialization arguments) /// Create a new SDSS based file (with no initialization arguments)
pub fn create<Fs: FSInterface<File = F>>(path: &str) -> RuntimeResult<Self> pub fn create(path: &str) -> RuntimeResult<Self>
where where
S: FileSpecV1<EncodeArgs = ()>, S: FileSpecV1<EncodeArgs = ()>,
{ {
let mut f = Fs::fs_fcreate_rw(path)?; let mut f = File::create(path)?;
let md = S::write_metadata(&mut f, ())?; let md = S::write_metadata(&mut f, ())?;
Ok(Self::new(f, md)) Ok(Self::new(f, md))
} }
/// Create or open an SDSS based file (with no initialization or validation arguments) pub fn into_buffered_reader(self) -> IoResult<SdssFile<S, BufferedReader>> {
pub fn open_or_create_perm_rw<Fs: FSInterface<File = F>>(
path: &str,
) -> RuntimeResult<FileOpen<Self>>
where
S: FileSpecV1<DecodeArgs = (), EncodeArgs = ()>,
{
match Fs::fs_fopen_or_create_rw(path)? {
FileOpen::Created(mut new) => {
let md = S::write_metadata(&mut new, ())?;
Ok(FileOpen::Created(Self::new(new, md)))
}
FileOpen::Existing(mut existing) => {
let md = S::read_metadata(&mut existing, ())?;
Ok(FileOpen::Existing(Self::new(existing, md)))
}
}
}
}
impl<F: FileInterface, S: FileSpecV1> SdssFile<F, S> {
/// Get a buffered reader. Use [`SdssFile::downgrade_reader`] to get back the original file
pub fn into_buffered_reader(self) -> RuntimeResult<SdssFile<F::BufReader, S>> {
let Self { file, meta } = self; let Self { file, meta } = self;
let bufreader = F::upgrade_to_buffered_reader(file)?; let r = file.into_buffered_reader();
Ok(SdssFile::new(bufreader, meta)) Ok(SdssFile::new(r, meta))
}
/// Get back the original file from the buffered reader
pub fn downgrade_reader(
SdssFile { file, meta }: SdssFile<F::BufReader, S>,
) -> RuntimeResult<Self> {
let me = F::downgrade_reader(file)?;
Ok(Self::new(me, meta))
}
/// Get a buffered writer. Use [`SdssFile::downgrade_writer`] to get back the original file
pub fn into_buffered_writer(self) -> RuntimeResult<SdssFile<F::BufWriter, S>> {
let Self { file, meta } = self;
let bufwriter = F::upgrade_to_buffered_writer(file)?;
Ok(SdssFile::new(bufwriter, meta))
} }
/// Get back the original file from the buffered writer pub fn downgrade_reader(SdssFile { file, meta }: SdssFile<S, BufferedReader>) -> Self {
/// Self::new(file.into_inner(), meta)
/// NB: THis will usually not explicitly sync any pending data, unless the downgrade implementation
/// of the interface does so
pub fn downgrade_writer(
SdssFile { file, meta }: SdssFile<F::BufWriter, S>,
) -> RuntimeResult<Self> {
let me = F::downgrade_writer(file)?;
Ok(Self::new(me, meta))
}
}
impl<F: FileInterfaceBufWrite, S: FileSpecV1> SdssFile<F, S> {
/// Sync writes
pub fn sync_writes(&mut self) -> RuntimeResult<()> {
self.file.sync_write_cache()
} }
} }
impl<F: FileInterfaceRead, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1, F: FileRead> SdssFile<S, F> {
/// Attempt to fill the entire buffer from the file /// Attempt to fill the entire buffer from the file
pub fn read_buffer(&mut self, buffer: &mut [u8]) -> RuntimeResult<()> { pub fn read_buffer(&mut self, buffer: &mut [u8]) -> IoResult<()> {
self.file.fread_exact(buffer) self.file.fread_exact(buffer)
} }
} }
impl<F: FileInterfaceRead + FileInterfaceExt, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1, F: FileRead + FileExt> SdssFile<S, F> {
/// Read the entire part of the remaining file into memory /// Read the entire part of the remaining file into memory
pub fn read_full(&mut self) -> RuntimeResult<Vec<u8>> { pub fn read_full(&mut self) -> IoResult<Vec<u8>> {
let len = self.file_length()? - self.file_cursor()?; let len = self.file_length()? - self.file_cursor()?;
let mut buf = vec![0; len as usize]; let mut buf = vec![0; len as usize];
self.read_buffer(&mut buf)?; self.read_buffer(&mut buf)?;
@ -161,38 +106,38 @@ impl<F: FileInterfaceRead + FileInterfaceExt, S: FileSpecV1> SdssFile<F, S> {
} }
} }
impl<F: FileInterfaceExt, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1, F: FileExt> SdssFile<S, F> {
/// Get the current position of the file /// Get the current position of the file
pub fn file_cursor(&mut self) -> RuntimeResult<u64> { pub fn file_cursor(&mut self) -> IoResult<u64> {
self.file.fext_cursor() self.file.f_cursor()
} }
/// Get the length of the file /// Get the length of the file
pub fn file_length(&self) -> RuntimeResult<u64> { pub fn file_length(&self) -> IoResult<u64> {
self.file.fext_length() self.file.f_len()
} }
/// Move the cursor `n` bytes from the start /// Move the cursor `n` bytes from the start
pub fn seek_from_start(&mut self, n: u64) -> RuntimeResult<()> { pub fn seek_from_start(&mut self, n: u64) -> IoResult<()> {
self.file.fext_seek_ahead_from_start_by(n) self.file.f_seek_start(n)
} }
} }
impl<F: FileInterfaceWrite, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1, F: FileWrite> SdssFile<S, F> {
/// Attempt to write the entire buffer into the file /// Attempt to write the entire buffer into the file
pub fn write_buffer(&mut self, data: &[u8]) -> RuntimeResult<()> { pub fn write_buffer(&mut self, data: &[u8]) -> IoResult<()> {
self.file.fw_write_all(data) self.file.fwrite_all(data)
} }
} }
impl<F: FileInterfaceWrite + FileInterfaceWriteExt, S: FileSpecV1> SdssFile<F, S> { impl<S: FileSpecV1, F: FileWrite + FileWriteExt> SdssFile<S, F> {
/// Sync all data and metadata permanently /// Sync all data and metadata permanently
pub fn fsync_all(&mut self) -> RuntimeResult<()> { pub fn fsync_all(&mut self) -> IoResult<()> {
self.file.fwext_sync_all()?; self.file.fsync_all()?;
Ok(()) Ok(())
} }
/// Write a block followed by an explicit fsync call /// Write a block followed by an explicit fsync call
pub fn fsynced_write(&mut self, data: &[u8]) -> RuntimeResult<()> { pub fn fsynced_write(&mut self, data: &[u8]) -> IoResult<()> {
self.file.fw_write_all(data)?; self.file.fwrite_all(data)?;
self.file.fwext_sync_all() self.file.fsync_all()
} }
} }
@ -202,32 +147,32 @@ impl<F: FileInterfaceWrite + FileInterfaceWriteExt, S: FileSpecV1> SdssFile<F, S
/// A [`TrackedReader`] will track various parameters of the file during read operations. By default /// A [`TrackedReader`] will track various parameters of the file during read operations. By default
/// all reads are buffered /// all reads are buffered
pub struct TrackedReader<F, S: FileSpecV1> { pub struct TrackedReader<S: FileSpecV1> {
f: SdssFile<F, S>, f: SdssFile<S, BufferedReader>,
len: u64, len: u64,
cursor: u64, cursor: u64,
cs: SCrc64, cs: SCrc64,
} }
pub struct TrackedReaderContext<'a, F, S: FileSpecV1> { pub struct TrackedReaderContext<'a, S: FileSpecV1> {
tr: &'a mut TrackedReader<F, S>, tr: &'a mut TrackedReader<S>,
p_checksum: SCrc64, p_checksum: SCrc64,
} }
impl<'a, F: FileInterfaceRead, S: FileSpecV1> TrackedReaderContext<'a, F, S> { impl<'a, S: FileSpecV1> TrackedReaderContext<'a, S> {
pub fn read(&mut self, buf: &mut [u8]) -> RuntimeResult<()> { pub fn read(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.tr self.tr
.tracked_read(buf) .tracked_read(buf)
.map(|_| self.p_checksum.update(buf)) .map(|_| self.p_checksum.update(buf))
} }
pub fn read_block<const N: usize>(&mut self) -> RuntimeResult<[u8; N]> { pub fn read_block<const N: usize>(&mut self) -> IoResult<[u8; N]> {
let mut block = [0; N]; let mut block = [0; N];
self.tr.tracked_read(&mut block).map(|_| { self.tr.tracked_read(&mut block).map(|_| {
self.p_checksum.update(&block); self.p_checksum.update(&block);
block block
}) })
} }
pub fn finish(self) -> (u64, &'a mut TrackedReader<F, S>) { pub fn finish(self) -> (u64, &'a mut TrackedReader<S>) {
let Self { tr, p_checksum } = self; let Self { tr, p_checksum } = self;
(p_checksum.finish(), tr) (p_checksum.finish(), tr)
} }
@ -236,15 +181,12 @@ impl<'a, F: FileInterfaceRead, S: FileSpecV1> TrackedReaderContext<'a, F, S> {
} }
} }
impl<F: FileInterface, S: FileSpecV1> TrackedReader<F, S> { impl<S: FileSpecV1> TrackedReader<S> {
/// Create a new [`TrackedReader`]. This needs to retrieve file position and length /// Create a new [`TrackedReader`]. This needs to retrieve file position and length
pub fn new(mut f: SdssFile<F, S>) -> RuntimeResult<TrackedReader<F::BufReader, S>> { pub fn new(mut f: SdssFile<S, File>) -> IoResult<TrackedReader<S>> {
f.file_cursor().and_then(|c| Self::with_cursor(f, c)) f.file_cursor().and_then(|c| Self::with_cursor(f, c))
} }
pub fn with_cursor( pub fn with_cursor(f: SdssFile<S, File>, cursor: u64) -> IoResult<Self> {
f: SdssFile<F, S>,
cursor: u64,
) -> RuntimeResult<TrackedReader<F::BufReader, S>> {
let len = f.file_length()?; let len = f.file_length()?;
let f = f.into_buffered_reader()?; let f = f.into_buffered_reader()?;
Ok(TrackedReader { Ok(TrackedReader {
@ -256,19 +198,19 @@ impl<F: FileInterface, S: FileSpecV1> TrackedReader<F, S> {
} }
} }
impl<F: FileInterfaceRead, S: FileSpecV1> TrackedReader<F, S> { impl<S: FileSpecV1> TrackedReader<S> {
pub fn context(&mut self) -> TrackedReaderContext<F, S> { pub fn context(&mut self) -> TrackedReaderContext<S> {
TrackedReaderContext { TrackedReaderContext {
tr: self, tr: self,
p_checksum: SCrc64::new(), p_checksum: SCrc64::new(),
} }
} }
/// Attempt to fill the buffer. This read is tracked. /// Attempt to fill the buffer. This read is tracked.
pub fn tracked_read(&mut self, buf: &mut [u8]) -> RuntimeResult<()> { pub fn tracked_read(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.untracked_read(buf).map(|_| self.cs.update(buf)) self.untracked_read(buf).map(|_| self.cs.update(buf))
} }
/// Attempt to read a byte. This read is also tracked. /// Attempt to read a byte. This read is also tracked.
pub fn read_byte(&mut self) -> RuntimeResult<u8> { pub fn read_byte(&mut self) -> IoResult<u8> {
let mut buf = [0u8; 1]; let mut buf = [0u8; 1];
self.tracked_read(&mut buf).map(|_| buf[0]) self.tracked_read(&mut buf).map(|_| buf[0])
} }
@ -281,7 +223,7 @@ impl<F: FileInterfaceRead, S: FileSpecV1> TrackedReader<F, S> {
/// Do an untracked read of the file. /// Do an untracked read of the file.
/// ///
/// NB: The change in cursor however will still be tracked. /// NB: The change in cursor however will still be tracked.
pub fn untracked_read(&mut self, buf: &mut [u8]) -> RuntimeResult<()> { pub fn untracked_read(&mut self, buf: &mut [u8]) -> IoResult<()> {
if self.remaining() >= buf.len() as u64 { if self.remaining() >= buf.len() as u64 {
match self.f.read_buffer(buf) { match self.f.read_buffer(buf) {
Ok(()) => { Ok(()) => {
@ -291,20 +233,20 @@ impl<F: FileInterfaceRead, S: FileSpecV1> TrackedReader<F, S> {
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} else { } else {
Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into()) Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into_inner())
} }
} }
/// Tracked read of a given block size. Shorthand for [`Self::tracked_read`] /// Tracked read of a given block size. Shorthand for [`Self::tracked_read`]
pub fn read_block<const N: usize>(&mut self) -> RuntimeResult<[u8; N]> { pub fn read_block<const N: usize>(&mut self) -> IoResult<[u8; N]> {
if !self.has_left(N as _) { if !self.has_left(N as _) {
return Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into()); return Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into_inner());
} }
let mut buf = [0; N]; let mut buf = [0; N];
self.tracked_read(&mut buf)?; self.tracked_read(&mut buf)?;
Ok(buf) Ok(buf)
} }
/// Tracked read of a [`u64`] value /// Tracked read of a [`u64`] value
pub fn read_u64_le(&mut self) -> RuntimeResult<u64> { pub fn read_u64_le(&mut self) -> IoResult<u64> {
Ok(u64::from_le_bytes(self.read_block()?)) Ok(u64::from_le_bytes(self.read_block()?))
} }
pub fn current_checksum(&self) -> u64 { pub fn current_checksum(&self) -> u64 {
@ -318,9 +260,9 @@ impl<F: FileInterfaceRead, S: FileSpecV1> TrackedReader<F, S> {
} }
} }
impl<F, S: FileSpecV1> TrackedReader<F, S> { impl<S: FileSpecV1> TrackedReader<S> {
/// Returns the base [`SdssFile`] /// Returns the base [`SdssFile`]
pub fn into_inner<F_: FileInterface<BufReader = F>>(self) -> RuntimeResult<SdssFile<F_, S>> { pub fn into_inner(self) -> SdssFile<S> {
SdssFile::downgrade_reader(self.f) SdssFile::downgrade_reader(self.f)
} }
/// Returns the number of remaining bytes /// Returns the number of remaining bytes
@ -407,7 +349,7 @@ impl<
} }
impl< impl<
F: FileInterfaceExt, F: FileExt,
S: FileSpecV1, S: FileSpecV1,
const SIZE: usize, const SIZE: usize,
const PANIC_IF_UNFLUSHED: bool, const PANIC_IF_UNFLUSHED: bool,
@ -418,18 +360,18 @@ impl<
/// ///
/// NB: The cursor is fetched. If the cursor is already available, use [`Self::with_cursor`] /// NB: The cursor is fetched. If the cursor is already available, use [`Self::with_cursor`]
pub fn new( pub fn new(
mut f: SdssFile<F, S>, mut f: SdssFile<S, F>,
) -> RuntimeResult<TrackedWriter<F, S, SIZE, PANIC_IF_UNFLUSHED, CHECKSUM_WRITTEN_IF_BLOCK_ERROR>> ) -> IoResult<TrackedWriter<F, S, SIZE, PANIC_IF_UNFLUSHED, CHECKSUM_WRITTEN_IF_BLOCK_ERROR>>
{ {
f.file_cursor().map(|v| TrackedWriter::with_cursor(f, v)) f.file_cursor().map(|v| TrackedWriter::with_cursor(f, v))
} }
/// Create a new tracked writer with the provided cursor /// Create a new tracked writer with the provided cursor
pub fn with_cursor(f: SdssFile<F, S>, c: u64) -> Self { pub fn with_cursor(f: SdssFile<S, F>, c: u64) -> Self {
Self::with_cursor_and_checksum(f, c, SCrc64::new()) Self::with_cursor_and_checksum(f, c, SCrc64::new())
} }
/// Create a new tracked writer with the provided checksum and cursor /// Create a new tracked writer with the provided checksum and cursor
pub fn with_cursor_and_checksum( pub fn with_cursor_and_checksum(
SdssFile { file, meta }: SdssFile<F, S>, SdssFile { file, meta }: SdssFile<S, F>,
c: u64, c: u64,
ck: SCrc64, ck: SCrc64,
) -> Self { ) -> Self {
@ -441,7 +383,7 @@ impl<
} }
impl< impl<
F: FileInterfaceWrite, F: FileWrite,
S: FileSpecV1, S: FileSpecV1,
const SIZE: usize, const SIZE: usize,
const PANIC_IF_UNFLUSHED: bool, const PANIC_IF_UNFLUSHED: bool,
@ -449,7 +391,7 @@ impl<
> TrackedWriter<F, S, SIZE, PANIC_IF_UNFLUSHED, CHECKSUM_WRITTEN_IF_BLOCK_ERROR> > TrackedWriter<F, S, SIZE, PANIC_IF_UNFLUSHED, CHECKSUM_WRITTEN_IF_BLOCK_ERROR>
{ {
/// Same as [`Self::tracked_write_through_buffer`], but the partial state is updated /// Same as [`Self::tracked_write_through_buffer`], but the partial state is updated
pub fn dtrack_write_through_buffer(&mut self, buf: &[u8]) -> RuntimeResult<()> { pub fn dtrack_write_through_buffer(&mut self, buf: &[u8]) -> IoResult<()> {
self.tracked_write_through_buffer(buf) self.tracked_write_through_buffer(buf)
.map(|_| self.t_partial_checksum.update(buf)) .map(|_| self.t_partial_checksum.update(buf))
} }
@ -458,7 +400,7 @@ impl<
/// NB: /// NB:
/// - If errored, the number of bytes written are still tracked /// - If errored, the number of bytes written are still tracked
/// - If errored, the checksum is updated to reflect the number of bytes written (unless otherwise configured) /// - If errored, the checksum is updated to reflect the number of bytes written (unless otherwise configured)
pub fn tracked_write_through_buffer(&mut self, buf: &[u8]) -> RuntimeResult<()> { pub fn tracked_write_through_buffer(&mut self, buf: &[u8]) -> IoResult<()> {
debug_assert!(self.buf.is_empty()); debug_assert!(self.buf.is_empty());
match self.f_d.fwrite_all_count(buf) { match self.f_d.fwrite_all_count(buf) {
(cnt, r) => { (cnt, r) => {
@ -475,7 +417,7 @@ impl<
} }
} }
/// Same as [`Self::tracked_write`], but the partial state is updated /// Same as [`Self::tracked_write`], but the partial state is updated
pub fn dtrack_write(&mut self, buf: &[u8]) -> RuntimeResult<()> { pub fn dtrack_write(&mut self, buf: &[u8]) -> IoResult<()> {
self.tracked_write(buf) self.tracked_write(buf)
.map(|_| self.t_partial_checksum.update(buf)) .map(|_| self.t_partial_checksum.update(buf))
} }
@ -487,7 +429,7 @@ impl<
/// ///
/// On error, if block error checksumming is set then whatever part of the block was written /// On error, if block error checksumming is set then whatever part of the block was written
/// will be updated in the checksum. If disabled, then the checksum is unchanged. /// will be updated in the checksum. If disabled, then the checksum is unchanged.
pub fn tracked_write(&mut self, buf: &[u8]) -> RuntimeResult<()> { pub fn tracked_write(&mut self, buf: &[u8]) -> IoResult<()> {
let cursor_start = self.cursor_usize(); let cursor_start = self.cursor_usize();
match self.untracked_write(buf) { match self.untracked_write(buf) {
Ok(()) => { Ok(()) => {
@ -504,7 +446,7 @@ impl<
} }
} }
/// Do an untracked write /// Do an untracked write
pub fn untracked_write(&mut self, buf: &[u8]) -> RuntimeResult<()> { pub fn untracked_write(&mut self, buf: &[u8]) -> IoResult<()> {
if self.available_capacity() >= buf.len() { if self.available_capacity() >= buf.len() {
unsafe { unsafe {
// UNSAFE(@ohsayan): above branch guarantees that we have sufficient space // UNSAFE(@ohsayan): above branch guarantees that we have sufficient space
@ -529,14 +471,14 @@ impl<
Ok(()) Ok(())
} }
/// Flush the buffer and then sync data and metadata /// Flush the buffer and then sync data and metadata
pub fn flush_sync(&mut self) -> RuntimeResult<()> pub fn flush_sync(&mut self) -> IoResult<()>
where where
F: FileInterfaceWriteExt, F: FileWriteExt,
{ {
self.flush_buf().and_then(|_| self.fsync()) self.flush_buf().and_then(|_| self.fsync())
} }
/// Flush the buffer /// Flush the buffer
pub fn flush_buf(&mut self) -> RuntimeResult<()> { pub fn flush_buf(&mut self) -> IoResult<()> {
match self.f_d.fwrite_all_count(&self.buf) { match self.f_d.fwrite_all_count(&self.buf) {
(written, r) => { (written, r) => {
if written as usize == self.buf.len() { if written as usize == self.buf.len() {
@ -560,11 +502,11 @@ impl<
} }
} }
} }
pub fn fsync(&mut self) -> RuntimeResult<()> pub fn fsync(&mut self) -> IoResult<()>
where where
F: FileInterfaceWriteExt, F: FileWriteExt,
{ {
self.f_d.fwext_sync_all() self.f_d.fsync_all()
} }
} }
@ -586,11 +528,11 @@ impl<
#[test] #[test]
fn check_vfs_buffering() { fn check_vfs_buffering() {
use crate::engine::storage::{ use crate::engine::storage::{
common::interface::fs_test::{VFileDescriptor, VirtualFS}, common::interface::fs::FileSystem,
v2::raw::spec::{Header, SystemDatabaseV1}, v2::raw::spec::{Header, SystemDatabaseV1},
}; };
fn rawfile() -> Vec<u8> { fn rawfile() -> Vec<u8> {
VirtualFS::fetch_raw_data("myfile").unwrap() FileSystem::read("myfile").unwrap()
} }
let compiled_header = SystemDatabaseV1::metadata_to_block(()).unwrap(); let compiled_header = SystemDatabaseV1::metadata_to_block(()).unwrap();
let expected_checksum = { let expected_checksum = {
@ -602,8 +544,8 @@ fn check_vfs_buffering() {
}; };
closure! { closure! {
// init writer // init writer
let mut twriter: TrackedWriter<VFileDescriptor, SystemDatabaseV1> = let mut twriter: TrackedWriter<File, SystemDatabaseV1> =
TrackedWriter::new(SdssFile::create::<VirtualFS>("myfile")?)?; TrackedWriter::new(SdssFile::create("myfile")?)?;
assert_eq!(twriter.cursor_usize(), Header::SIZE); assert_eq!(twriter.cursor_usize(), Header::SIZE);
{ {
// W8192: write exact bufsize block; nothing is written (except SDSS header) // W8192: write exact bufsize block; nothing is written (except SDSS header)

@ -71,11 +71,11 @@ fn create_space() {
let uuid; let uuid;
// start 1 // start 1
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
uuid = init_space(&global, "myspace", "{ SAYAN_MAX: 65536 }"); // good lord that doesn't sound like a good variable uuid = init_space(&global, "myspace", "{ SAYAN_MAX: 65536 }"); // good lord that doesn't sound like a good variable
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
let spaces = global.state().idx().read(); let spaces = global.state().idx().read();
let space = spaces.get("myspace").unwrap(); let space = spaces.get("myspace").unwrap();
assert_eq!( assert_eq!(
@ -96,7 +96,7 @@ fn alter_space() {
with_variable("alter_space_test.global.db-tlog", |log_name| { with_variable("alter_space_test.global.db-tlog", |log_name| {
let uuid; let uuid;
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
uuid = init_space(&global, "myspace", "{}"); uuid = init_space(&global, "myspace", "{}");
let stmt = let stmt =
lex_insecure("alter space myspace with { env: { SAYAN_MAX: 65536 } }".as_bytes()) lex_insecure("alter space myspace with { env: { SAYAN_MAX: 65536 } }".as_bytes())
@ -105,7 +105,7 @@ fn alter_space() {
Space::transactional_exec_alter(&global, stmt).unwrap(); Space::transactional_exec_alter(&global, stmt).unwrap();
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
let spaces = global.state().idx().read(); let spaces = global.state().idx().read();
let space = spaces.get("myspace").unwrap(); let space = spaces.get("myspace").unwrap();
assert_eq!( assert_eq!(
@ -125,14 +125,14 @@ fn alter_space() {
fn drop_space() { fn drop_space() {
with_variable("drop_space_test.global.db-tlog", |log_name| { with_variable("drop_space_test.global.db-tlog", |log_name| {
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
let _ = init_space(&global, "myspace", "{}"); let _ = init_space(&global, "myspace", "{}");
let stmt = lex_insecure("drop space myspace".as_bytes()).unwrap(); let stmt = lex_insecure("drop space myspace".as_bytes()).unwrap();
let stmt = parse_ast_node_full(&stmt[2..]).unwrap(); let stmt = parse_ast_node_full(&stmt[2..]).unwrap();
Space::transactional_exec_drop(&global, stmt).unwrap(); Space::transactional_exec_drop(&global, stmt).unwrap();
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
assert!(global.state().idx().read().get("myspace").is_none()); assert!(global.state().idx().read().get("myspace").is_none());
}) })
}) })
@ -170,12 +170,12 @@ fn create_model() {
let _uuid_space; let _uuid_space;
let uuid_model; let uuid_model;
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
_uuid_space = init_space(&global, "myspace", "{}"); _uuid_space = init_space(&global, "myspace", "{}");
uuid_model = init_default_model(&global); uuid_model = init_default_model(&global);
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
global global
.state() .state()
.with_model(("myspace", "mymodel").into(), |model| { .with_model(("myspace", "mymodel").into(), |model| {
@ -202,7 +202,7 @@ fn create_model() {
fn alter_model_add() { fn alter_model_add() {
with_variable("alter_model_add_test.global.db-tlog", |log_name| { with_variable("alter_model_add_test.global.db-tlog", |log_name| {
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
init_space(&global, "myspace", "{}"); init_space(&global, "myspace", "{}");
init_default_model(&global); init_default_model(&global);
let stmt = lex_insecure( let stmt = lex_insecure(
@ -213,7 +213,7 @@ fn alter_model_add() {
Model::transactional_exec_alter(&global, stmt).unwrap(); Model::transactional_exec_alter(&global, stmt).unwrap();
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
global global
.state() .state()
.with_model(("myspace", "mymodel").into(), |model| { .with_model(("myspace", "mymodel").into(), |model| {
@ -232,7 +232,7 @@ fn alter_model_add() {
fn alter_model_remove() { fn alter_model_remove() {
with_variable("alter_model_remove_test.global.db-tlog", |log_name| { with_variable("alter_model_remove_test.global.db-tlog", |log_name| {
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
init_space(&global, "myspace", "{}"); init_space(&global, "myspace", "{}");
init_model( init_model(
&global, &global,
@ -248,7 +248,7 @@ fn alter_model_remove() {
Model::transactional_exec_alter(&global, stmt).unwrap(); Model::transactional_exec_alter(&global, stmt).unwrap();
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
global global
.state() .state()
.with_model(("myspace", "mymodel").into(), |model| { .with_model(("myspace", "mymodel").into(), |model| {
@ -265,7 +265,7 @@ fn alter_model_remove() {
fn alter_model_update() { fn alter_model_update() {
with_variable("alter_model_update_test.global.db-tlog", |log_name| { with_variable("alter_model_update_test.global.db-tlog", |log_name| {
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
init_space(&global, "myspace", "{}"); init_space(&global, "myspace", "{}");
init_model( init_model(
&global, &global,
@ -280,7 +280,7 @@ fn alter_model_update() {
Model::transactional_exec_alter(&global, stmt).unwrap(); Model::transactional_exec_alter(&global, stmt).unwrap();
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
global global
.state() .state()
.with_model(("myspace", "mymodel").into(), |model| { .with_model(("myspace", "mymodel").into(), |model| {
@ -299,7 +299,7 @@ fn alter_model_update() {
fn drop_model() { fn drop_model() {
with_variable("drop_model_test.global.db-tlog", |log_name| { with_variable("drop_model_test.global.db-tlog", |log_name| {
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
init_space(&global, "myspace", "{}"); init_space(&global, "myspace", "{}");
init_default_model(&global); init_default_model(&global);
let stmt = lex_insecure(b"drop model myspace.mymodel").unwrap(); let stmt = lex_insecure(b"drop model myspace.mymodel").unwrap();
@ -307,7 +307,7 @@ fn drop_model() {
Model::transactional_exec_drop(&global, stmt).unwrap(); Model::transactional_exec_drop(&global, stmt).unwrap();
} }
multirun(|| { multirun(|| {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
assert_eq!( assert_eq!(
global global
.state() .state()

@ -27,7 +27,6 @@
//! Implementations of the Skytable Disk Storage Subsystem (SDSS) //! Implementations of the Skytable Disk Storage Subsystem (SDSS)
use { use {
self::safe_interfaces::LocalFS,
super::{ super::{
config::Configuration, core::GlobalNS, fractal::context, fractal::ModelDrivers, config::Configuration, core::GlobalNS, fractal::context, fractal::ModelDrivers,
RuntimeResult, RuntimeResult,
@ -42,13 +41,8 @@ pub mod v1;
pub mod v2; pub mod v2;
pub mod safe_interfaces { pub mod safe_interfaces {
#[cfg(test)]
pub use super::common::interface::fs_test::{NullFS, VirtualFS};
pub use super::{ pub use super::{
common::{ common::{interface::fs::FileSystem, paths_v1},
interface::{fs_imp::LocalFS, fs_traits::FSInterface},
paths_v1,
},
v2::impls::mdl_journal::StdModelBatch, v2::impls::mdl_journal::StdModelBatch,
}; };
} }
@ -64,8 +58,8 @@ pub use v2::impls::{
pub struct SELoaded { pub struct SELoaded {
pub gns: GlobalNS, pub gns: GlobalNS,
pub gns_driver: v2::impls::gns_log::GNSDriver<LocalFS>, pub gns_driver: v2::impls::gns_log::GNSDriver,
pub model_drivers: ModelDrivers<LocalFS>, pub model_drivers: ModelDrivers,
} }
pub fn load(cfg: &Configuration) -> RuntimeResult<SELoaded> { pub fn load(cfg: &Configuration) -> RuntimeResult<SELoaded> {

@ -24,28 +24,27 @@
* *
*/ */
use std::collections::HashMap; use {
crate::engine::{
use crate::engine::{ core::{EntityIDRef, GlobalNS},
core::{EntityIDRef, GlobalNS}, error::RuntimeResult,
error::RuntimeResult, fractal::{error::ErrorContext, ModelUniqueID},
fractal::{error::ErrorContext, ModelUniqueID}, storage::{
storage::{ common::paths_v1,
common::{interface::fs_imp::LocalFS, paths_v1}, v1::raw::{
v1::raw::{ batch_jrnl,
batch_jrnl, journal::{raw as raw_journal, GNSAdapter},
journal::{raw as raw_journal, GNSAdapter}, spec,
spec, },
}, },
}, },
std::collections::HashMap,
}; };
pub fn load_gns() -> RuntimeResult<GlobalNS> { pub fn load_gns() -> RuntimeResult<GlobalNS> {
let gns = GlobalNS::empty(); let gns = GlobalNS::empty();
let gns_txn_driver = raw_journal::load_journal::<GNSAdapter, LocalFS, spec::GNSTransactionLogV1>( let gns_txn_driver =
super::GNS_PATH, raw_journal::load_journal::<GNSAdapter, spec::GNSTransactionLogV1>(super::GNS_PATH, &gns)?;
&gns,
)?;
let mut model_drivers = HashMap::new(); let mut model_drivers = HashMap::new();
let mut driver_guard = || { let mut driver_guard = || {
let mut models = gns.idx_models().write(); let mut models = gns.idx_models().write();
@ -58,9 +57,9 @@ pub fn load_gns() -> RuntimeResult<GlobalNS> {
.unwrap(); .unwrap();
let path = let path =
paths_v1::model_path(space_name, space_uuid, model_name, model.get_uuid()); paths_v1::model_path(space_name, space_uuid, model_name, model.get_uuid());
let persist_driver = batch_jrnl::reinit::<LocalFS>(&path, model).inherit_set_dmsg( let persist_driver = batch_jrnl::reinit(&path, model).inherit_set_dmsg(format!(
format!("failed to restore model data from journal in `{path}`"), "failed to restore model data from journal in `{path}`"
)?; ))?;
unsafe { unsafe {
// UNSAFE(@ohsayan): all pieces of data are upgraded by now, so vacuum // UNSAFE(@ohsayan): all pieces of data are upgraded by now, so vacuum
model.model_mutator().vacuum_stashed(); model.model_mutator().vacuum_stashed();

@ -33,7 +33,7 @@ pub mod raw;
use { use {
self::raw::sysdb::RestoredSystemDatabase, self::raw::sysdb::RestoredSystemDatabase,
super::common::interface::{fs_imp::LocalFS, fs_traits::FSInterface}, super::common::interface::fs::FileSystem,
crate::{ crate::{
engine::{core::GlobalNS, RuntimeResult}, engine::{core::GlobalNS, RuntimeResult},
util, util,
@ -49,7 +49,7 @@ pub fn load_gns_prepare_migration() -> RuntimeResult<GlobalNS> {
let gns = loader::load_gns()?; let gns = loader::load_gns()?;
// load sysdb // load sysdb
let RestoredSystemDatabase { users, .. } = let RestoredSystemDatabase { users, .. } =
raw::sysdb::RestoredSystemDatabase::restore::<LocalFS>(SYSDB_PATH)?; raw::sysdb::RestoredSystemDatabase::restore(SYSDB_PATH)?;
for (user, phash) in users { for (user, phash) in users {
gns.sys_db().__raw_create_user(user, phash); gns.sys_db().__raw_create_user(user, phash);
} }
@ -59,11 +59,11 @@ pub fn load_gns_prepare_migration() -> RuntimeResult<GlobalNS> {
util::time_now_with_postfix("before_upgrade_to_v2") util::time_now_with_postfix("before_upgrade_to_v2")
); );
// move data folder // move data folder
LocalFS::fs_create_dir_all(&backup_dir_path)?; FileSystem::create_dir_all(&backup_dir_path)?;
util::os::move_files_recursively("data", &format!("{backup_dir_path}/data"))?; util::os::move_files_recursively("data", &format!("{backup_dir_path}/data"))?;
// move GNS // move GNS
LocalFS::fs_rename(GNS_PATH, &format!("{backup_dir_path}/{GNS_PATH}"))?; FileSystem::rename(GNS_PATH, &format!("{backup_dir_path}/{GNS_PATH}"))?;
// move sysdb // move sysdb
LocalFS::fs_rename(SYSDB_PATH, &format!("{backup_dir_path}/{SYSDB_PATH}"))?; FileSystem::rename(SYSDB_PATH, &format!("{backup_dir_path}/{SYSDB_PATH}"))?;
Ok(gns) Ok(gns)
} }

@ -42,20 +42,14 @@ pub use {persist::DataBatchPersistDriver, restore::DataBatchRestoreDriver};
use { use {
super::{rw::SDSSFileIO, spec}, super::{rw::SDSSFileIO, spec},
crate::engine::{ crate::engine::{core::model::Model, error::RuntimeResult},
core::model::Model, error::RuntimeResult,
storage::common::interface::fs_traits::FSInterface,
},
}; };
/// Re-initialize an existing batch journal and read all its data into model /// Re-initialize an existing batch journal and read all its data into model
pub fn reinit<Fs: FSInterface>( pub fn reinit(name: &str, model: &Model) -> RuntimeResult<DataBatchPersistDriver> {
name: &str, let (f, _header) = SDSSFileIO::open::<spec::DataBatchJournalV1>(name)?;
model: &Model,
) -> RuntimeResult<DataBatchPersistDriver<Fs>> {
let (f, _header) = SDSSFileIO::<Fs>::open::<spec::DataBatchJournalV1>(name)?;
// restore // restore
let mut restore_driver = DataBatchRestoreDriver::new(f)?; let mut restore_driver = DataBatchRestoreDriver::new(f)?;
restore_driver.read_data_batch_into_model(model)?; restore_driver.read_data_batch_into_model(model)?;
DataBatchPersistDriver::new(restore_driver.into_file()?, false) DataBatchPersistDriver::new(restore_driver.into_file(), false)
} }

@ -29,18 +29,18 @@ use {
crate::engine::{ crate::engine::{
error::{RuntimeResult, StorageError}, error::{RuntimeResult, StorageError},
storage::{ storage::{
common::interface::fs_traits::FSInterface, common::interface::fs::File,
v1::raw::rw::{SDSSFileIO, TrackedWriter}, v1::raw::rw::{SDSSFileIO, TrackedWriter},
}, },
}, },
}; };
pub struct DataBatchPersistDriver<Fs: FSInterface> { pub struct DataBatchPersistDriver {
f: TrackedWriter<Fs>, f: TrackedWriter,
} }
impl<Fs: FSInterface> DataBatchPersistDriver<Fs> { impl DataBatchPersistDriver {
pub fn new(mut file: SDSSFileIO<Fs>, is_new: bool) -> RuntimeResult<Self> { pub fn new(mut file: SDSSFileIO<File>, is_new: bool) -> RuntimeResult<Self> {
if !is_new { if !is_new {
file.fsynced_write(&[MARKER_BATCH_REOPEN])?; file.fsynced_write(&[MARKER_BATCH_REOPEN])?;
} }

@ -38,7 +38,7 @@ use {
error::{RuntimeResult, StorageError}, error::{RuntimeResult, StorageError},
idx::{MTIndex, STIndex, STIndexSeq}, idx::{MTIndex, STIndex, STIndexSeq},
storage::{ storage::{
common::interface::fs_traits::FSInterface, common::interface::fs::File,
common_encoding::r1::{ common_encoding::r1::{
obj::cell::{self, StorageCellTypeID}, obj::cell::{self, StorageCellTypeID},
DataSource, DataSource,
@ -105,17 +105,17 @@ enum Batch {
BatchClosed, BatchClosed,
} }
pub struct DataBatchRestoreDriver<F: FSInterface> { pub struct DataBatchRestoreDriver {
f: TrackedReader<F>, f: TrackedReader,
} }
impl<F: FSInterface> DataBatchRestoreDriver<F> { impl DataBatchRestoreDriver {
pub fn new(f: SDSSFileIO<F>) -> RuntimeResult<Self> { pub fn new(f: SDSSFileIO<File>) -> RuntimeResult<Self> {
Ok(Self { Ok(Self {
f: TrackedReader::new(f)?, f: TrackedReader::new(f.into_buffered_reader())?,
}) })
} }
pub fn into_file(self) -> RuntimeResult<SDSSFileIO<F>> { pub fn into_file(self) -> SDSSFileIO<File> {
self.f.into_inner_file() self.f.into_inner_file()
} }
pub(in crate::engine::storage::v1) fn read_data_batch_into_model( pub(in crate::engine::storage::v1) fn read_data_batch_into_model(
@ -129,7 +129,7 @@ impl<F: FSInterface> DataBatchRestoreDriver<F> {
} }
} }
impl<F: FSInterface> DataBatchRestoreDriver<F> { impl DataBatchRestoreDriver {
fn read_all_batches_and_for_each( fn read_all_batches_and_for_each(
&mut self, &mut self,
mut f: impl FnMut(NormalBatch) -> RuntimeResult<()>, mut f: impl FnMut(NormalBatch) -> RuntimeResult<()>,
@ -197,7 +197,7 @@ impl<F: FSInterface> DataBatchRestoreDriver<F> {
} }
} }
impl<F: FSInterface> DataBatchRestoreDriver<F> { impl DataBatchRestoreDriver {
fn apply_batch( fn apply_batch(
m: &Model, m: &Model,
NormalBatch { NormalBatch {
@ -292,7 +292,7 @@ impl<F: FSInterface> DataBatchRestoreDriver<F> {
} }
} }
impl<F: FSInterface> DataBatchRestoreDriver<F> { impl DataBatchRestoreDriver {
fn read_batch_summary(&mut self, finished_early: bool) -> RuntimeResult<u64> { fn read_batch_summary(&mut self, finished_early: bool) -> RuntimeResult<u64> {
if !finished_early { if !finished_early {
// we must read the batch termination signature // we must read the batch termination signature
@ -457,7 +457,7 @@ impl BatchStartBlock {
} }
} }
impl<F: FSInterface> DataBatchRestoreDriver<F> { impl DataBatchRestoreDriver {
fn decode_primary_key(&mut self, pk_type: u8) -> RuntimeResult<PrimaryIndexKey> { fn decode_primary_key(&mut self, pk_type: u8) -> RuntimeResult<PrimaryIndexKey> {
let Some(pk_type) = TagUnique::try_from_raw(pk_type) else { let Some(pk_type) = TagUnique::try_from_raw(pk_type) else {
return Err(StorageError::DataBatchRestoreCorruptedEntry.into()); return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
@ -495,7 +495,7 @@ impl<F: FSInterface> DataBatchRestoreDriver<F> {
let Some(dscr) = StorageCellTypeID::try_from_raw(self.f.read_byte()?) else { let Some(dscr) = StorageCellTypeID::try_from_raw(self.f.read_byte()?) else {
return Err(StorageError::DataBatchRestoreCorruptedEntry.into()); return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}; };
unsafe { cell::decode_element::<Datacell, TrackedReader<F>>(&mut self.f, dscr) } unsafe { cell::decode_element::<Datacell, TrackedReader>(&mut self.f, dscr) }
.map_err(|e| e.0) .map_err(|e| e.0)
} }
} }
@ -506,12 +506,18 @@ impl From<crate::engine::fractal::error::Error> for ErrorHack {
Self(value) Self(value)
} }
} }
impl From<std::io::Error> for ErrorHack {
fn from(value: std::io::Error) -> Self {
Self(value.into())
}
}
impl From<()> for ErrorHack { impl From<()> for ErrorHack {
fn from(_: ()) -> Self { fn from(_: ()) -> Self {
Self(StorageError::DataBatchRestoreCorruptedEntry.into()) Self(StorageError::DataBatchRestoreCorruptedEntry.into())
} }
} }
impl<F: FSInterface> DataSource for TrackedReader<F> {
impl DataSource for TrackedReader {
const RELIABLE_SOURCE: bool = false; const RELIABLE_SOURCE: bool = false;
type Error = ErrorHack; type Error = ErrorHack;
fn has_remaining(&self, cnt: usize) -> bool { fn has_remaining(&self, cnt: usize) -> bool {

@ -46,7 +46,10 @@ use {
crate::{ crate::{
engine::{ engine::{
error::{RuntimeResult, StorageError}, error::{RuntimeResult, StorageError},
storage::common::{interface::fs_traits::FSInterface, sdss}, storage::common::{
interface::fs::{BufferedReader, File},
sdss,
},
}, },
util::{compiler, copy_a_into_b, copy_slice_to_array as memcpy}, util::{compiler, copy_a_into_b, copy_slice_to_array as memcpy},
}, },
@ -55,16 +58,12 @@ use {
const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC); const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
pub fn load_journal< pub fn load_journal<TA: JournalAdapter, F: sdss::sdss_r1::FileSpecV1<DecodeArgs = ()>>(
TA: JournalAdapter,
Fs: FSInterface,
F: sdss::sdss_r1::FileSpecV1<DecodeArgs = ()>,
>(
log_file_name: &str, log_file_name: &str,
gs: &TA::GlobalState, gs: &TA::GlobalState,
) -> RuntimeResult<JournalWriter<Fs, TA>> { ) -> RuntimeResult<JournalWriter<TA>> {
let (file, _) = SDSSFileIO::<Fs>::open::<F>(log_file_name)?; let (file, _) = SDSSFileIO::open::<F>(log_file_name)?;
let (file, last_txn_id) = JournalReader::<TA, Fs>::scroll(file, gs)?; let (file, last_txn_id) = JournalReader::<TA>::scroll(file, gs)?;
JournalWriter::new(file, last_txn_id, false) JournalWriter::new(file, last_txn_id, false)
} }
@ -174,19 +173,19 @@ impl JournalEntryMetadata {
} }
} }
pub struct JournalReader<TA, Fs: FSInterface> { pub struct JournalReader<TA> {
log_file: SDSSFileIO<Fs>, log_file: SDSSFileIO<BufferedReader>,
evid: u64, evid: u64,
closed: bool, closed: bool,
remaining_bytes: u64, remaining_bytes: u64,
_m: PhantomData<TA>, _m: PhantomData<TA>,
} }
impl<TA: JournalAdapter, Fs: FSInterface> JournalReader<TA, Fs> { impl<TA: JournalAdapter> JournalReader<TA> {
pub fn new(log_file: SDSSFileIO<Fs>) -> RuntimeResult<Self> { pub fn new(log_file: SDSSFileIO<File>) -> RuntimeResult<Self> {
let log_size = log_file.file_length()? - Header::SIZE as u64; let log_size = log_file.file_length()? - Header::SIZE as u64;
Ok(Self { Ok(Self {
log_file, log_file: log_file.into_buffered_reader(),
evid: 0, evid: 0,
closed: false, closed: false,
remaining_bytes: log_size, remaining_bytes: log_size,
@ -296,22 +295,22 @@ impl<TA: JournalAdapter, Fs: FSInterface> JournalReader<TA, Fs> {
} }
/// Read and apply all events in the given log file to the global state, returning the (open file, last event ID) /// Read and apply all events in the given log file to the global state, returning the (open file, last event ID)
pub fn scroll( pub fn scroll(
file: SDSSFileIO<Fs>, file: SDSSFileIO<File>,
gs: &TA::GlobalState, gs: &TA::GlobalState,
) -> RuntimeResult<(SDSSFileIO<Fs>, u64)> { ) -> RuntimeResult<(SDSSFileIO<File>, u64)> {
let mut slf = Self::new(file)?; let mut slf = Self::new(file)?;
while !slf.end_of_file() { while !slf.end_of_file() {
slf.rapply_next_event(gs)?; slf.rapply_next_event(gs)?;
} }
if slf.closed { if slf.closed {
Ok((slf.log_file, slf.evid)) Ok((slf.log_file.downgrade_reader(), slf.evid))
} else { } else {
Err(StorageError::JournalCorrupted.into()) Err(StorageError::JournalCorrupted.into())
} }
} }
} }
impl<TA, Fs: FSInterface> JournalReader<TA, Fs> { impl<TA> JournalReader<TA> {
fn _incr_evid(&mut self) { fn _incr_evid(&mut self) {
self.evid += 1; self.evid += 1;
} }
@ -326,7 +325,7 @@ impl<TA, Fs: FSInterface> JournalReader<TA, Fs> {
} }
} }
impl<TA, Fs: FSInterface> JournalReader<TA, Fs> { impl<TA> JournalReader<TA> {
fn logfile_read_into_buffer(&mut self, buf: &mut [u8]) -> RuntimeResult<()> { fn logfile_read_into_buffer(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
if !self.has_remaining_bytes(buf.len() as _) { if !self.has_remaining_bytes(buf.len() as _) {
// do this right here to avoid another syscall // do this right here to avoid another syscall
@ -338,17 +337,17 @@ impl<TA, Fs: FSInterface> JournalReader<TA, Fs> {
} }
} }
pub struct JournalWriter<Fs: FSInterface, TA> { pub struct JournalWriter<TA> {
/// the txn log file /// the txn log file
log_file: SDSSFileIO<Fs>, log_file: SDSSFileIO<File>,
/// the id of the **next** journal /// the id of the **next** journal
id: u64, id: u64,
_m: PhantomData<TA>, _m: PhantomData<TA>,
closed: bool, closed: bool,
} }
impl<Fs: FSInterface, TA: JournalAdapter> JournalWriter<Fs, TA> { impl<TA: JournalAdapter> JournalWriter<TA> {
pub fn new(mut log_file: SDSSFileIO<Fs>, last_txn_id: u64, new: bool) -> RuntimeResult<Self> { pub fn new(mut log_file: SDSSFileIO<File>, last_txn_id: u64, new: bool) -> RuntimeResult<Self> {
let log_size = log_file.file_length()?; let log_size = log_file.file_length()?;
log_file.seek_from_start(log_size)?; // avoid jumbling with headers log_file.seek_from_start(log_size)?; // avoid jumbling with headers
let mut slf = Self { let mut slf = Self {
@ -365,12 +364,12 @@ impl<Fs: FSInterface, TA: JournalAdapter> JournalWriter<Fs, TA> {
} }
} }
impl<Fs: FSInterface, TA> JournalWriter<Fs, TA> { impl<TA> JournalWriter<TA> {
pub fn append_journal_reopen(&mut self) -> RuntimeResult<()> { pub fn append_journal_reopen(&mut self) -> RuntimeResult<()> {
let id = self._incr_id() as u128; let id = self._incr_id() as u128;
self.log_file.fsynced_write( e!(self.log_file.fsynced_write(
&JournalEntryMetadata::new(id, EventSourceMarker::DRIVER_REOPENED, 0, 0).encoded(), &JournalEntryMetadata::new(id, EventSourceMarker::DRIVER_REOPENED, 0, 0).encoded(),
) ))
} }
pub fn __close_mut(&mut self) -> RuntimeResult<()> { pub fn __close_mut(&mut self) -> RuntimeResult<()> {
self.closed = true; self.closed = true;
@ -385,7 +384,7 @@ impl<Fs: FSInterface, TA> JournalWriter<Fs, TA> {
} }
} }
impl<Fs: FSInterface, TA> JournalWriter<Fs, TA> { impl<TA> JournalWriter<TA> {
fn _incr_id(&mut self) -> u64 { fn _incr_id(&mut self) -> u64 {
let current = self.id; let current = self.id;
self.id += 1; self.id += 1;
@ -393,7 +392,7 @@ impl<Fs: FSInterface, TA> JournalWriter<Fs, TA> {
} }
} }
impl<Fs: FSInterface, TA> Drop for JournalWriter<Fs, TA> { impl<TA> Drop for JournalWriter<TA> {
fn drop(&mut self) { fn drop(&mut self) {
assert!(self.closed, "log not closed"); assert!(self.closed, "log not closed");
} }

@ -24,56 +24,52 @@
* *
*/ */
use { use crate::{
crate::{ engine::{
engine::{ storage::common::{
error::RuntimeResult, checksum::SCrc64,
storage::common::{ interface::fs::{
checksum::SCrc64, BufferedReader, BufferedWriter, File, FileExt, FileRead, FileWrite, FileWriteExt,
interface::fs_traits::{
FSInterface, FileInterface, FileInterfaceExt, FileInterfaceRead,
FileInterfaceWrite, FileInterfaceWriteExt,
},
sdss,
}, },
sdss,
}, },
util::os::SysIOError, RuntimeResult,
}, },
std::marker::PhantomData, util::os::SysIOError,
IoResult,
}; };
pub struct TrackedWriter<Fs: FSInterface> { pub struct TrackedWriter {
file: SDSSFileIO<Fs, <Fs::File as FileInterface>::BufWriter>, file: SDSSFileIO<BufferedWriter>,
_cs: SCrc64, _cs: SCrc64,
} }
impl<Fs: FSInterface> TrackedWriter<Fs> { impl TrackedWriter {
pub fn new(f: SDSSFileIO<Fs>) -> RuntimeResult<Self> { pub fn new(f: SDSSFileIO<File>) -> IoResult<Self> {
Ok(Self { Ok(Self {
file: f.into_buffered_writer()?, file: f.into_buffered_writer(),
_cs: SCrc64::new(), _cs: SCrc64::new(),
}) })
} }
pub fn sync_into_inner(self) -> RuntimeResult<SDSSFileIO<Fs>> { pub fn sync_into_inner(self) -> IoResult<SDSSFileIO<File>> {
self.file.downgrade_writer() self.file.downgrade_writer()
} }
} }
/// [`SDSSFileLenTracked`] simply maintains application level length and checksum tracking to avoid frequent syscalls because we /// [`SDSSFileLenTracked`] simply maintains application level length and checksum tracking to avoid frequent syscalls because we
/// do not expect (even though it's very possible) users to randomly modify file lengths while we're reading them /// do not expect (even though it's very possible) users to randomly modify file lengths while we're reading them
pub struct TrackedReader<Fs: FSInterface> { pub struct TrackedReader {
f: SDSSFileIO<Fs, <Fs::File as FileInterface>::BufReader>, f: SDSSFileIO<BufferedReader>,
len: u64, len: u64,
cursor: u64, cursor: u64,
cs: SCrc64, cs: SCrc64,
} }
impl<Fs: FSInterface> TrackedReader<Fs> { impl TrackedReader {
/// Important: this will only look at the data post the current cursor! /// Important: this will only look at the data post the current cursor!
pub fn new(mut f: SDSSFileIO<Fs>) -> RuntimeResult<Self> { pub fn new(mut f: SDSSFileIO<BufferedReader>) -> IoResult<Self> {
let len = f.file_length()?; let len = f.file_length()?;
let pos = f.file_cursor()?; let pos = f.file_cursor()?;
let f = f.into_buffered_reader()?;
Ok(Self { Ok(Self {
f, f,
len, len,
@ -90,10 +86,10 @@ impl<Fs: FSInterface> TrackedReader<Fs> {
pub fn has_left(&self, v: u64) -> bool { pub fn has_left(&self, v: u64) -> bool {
self.remaining() >= v self.remaining() >= v
} }
pub fn tracked_read(&mut self, buf: &mut [u8]) -> RuntimeResult<()> { pub fn tracked_read(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.untracked_read(buf).map(|_| self.cs.update(buf)) self.untracked_read(buf).map(|_| self.cs.update(buf))
} }
pub fn read_byte(&mut self) -> RuntimeResult<u8> { pub fn read_byte(&mut self) -> IoResult<u8> {
let mut buf = [0u8; 1]; let mut buf = [0u8; 1];
self.tracked_read(&mut buf).map(|_| buf[0]) self.tracked_read(&mut buf).map(|_| buf[0])
} }
@ -102,7 +98,7 @@ impl<Fs: FSInterface> TrackedReader<Fs> {
core::mem::swap(&mut crc, &mut self.cs); core::mem::swap(&mut crc, &mut self.cs);
crc.finish() crc.finish()
} }
pub fn untracked_read(&mut self, buf: &mut [u8]) -> RuntimeResult<()> { pub fn untracked_read(&mut self, buf: &mut [u8]) -> IoResult<()> {
if self.remaining() >= buf.len() as u64 { if self.remaining() >= buf.len() as u64 {
match self.f.read_buffer(buf) { match self.f.read_buffer(buf) {
Ok(()) => { Ok(()) => {
@ -112,94 +108,89 @@ impl<Fs: FSInterface> TrackedReader<Fs> {
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} else { } else {
Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into()) Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into_inner())
} }
} }
pub fn into_inner_file(self) -> RuntimeResult<SDSSFileIO<Fs>> { pub fn into_inner_file(self) -> SDSSFileIO<File> {
self.f.downgrade_reader() self.f.downgrade_reader()
} }
pub fn read_block<const N: usize>(&mut self) -> RuntimeResult<[u8; N]> { pub fn read_block<const N: usize>(&mut self) -> IoResult<[u8; N]> {
if !self.has_left(N as _) { if !self.has_left(N as _) {
return Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into()); return Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into_inner());
} }
let mut buf = [0; N]; let mut buf = [0; N];
self.tracked_read(&mut buf)?; self.tracked_read(&mut buf)?;
Ok(buf) Ok(buf)
} }
pub fn read_u64_le(&mut self) -> RuntimeResult<u64> { pub fn read_u64_le(&mut self) -> IoResult<u64> {
Ok(u64::from_le_bytes(self.read_block()?)) Ok(u64::from_le_bytes(self.read_block()?))
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SDSSFileIO<Fs: FSInterface, F = <Fs as FSInterface>::File> { pub struct SDSSFileIO<F> {
f: F, f: F,
_fs: PhantomData<Fs>, }
impl<F> SDSSFileIO<F> {
pub fn new(f: F) -> Self {
Self { f }
}
} }
impl<Fs: FSInterface> SDSSFileIO<Fs> { impl SDSSFileIO<File> {
pub fn open<F: sdss::sdss_r1::FileSpecV1<DecodeArgs = ()>>( pub fn open<S: sdss::sdss_r1::FileSpecV1<DecodeArgs = ()>>(
fpath: &str, fpath: &str,
) -> RuntimeResult<(Self, F::Metadata)> { ) -> RuntimeResult<(Self, S::Metadata)> {
let mut f = Self::_new(Fs::fs_fopen_rw(fpath)?); let mut f = Self::_new(File::open(fpath)?);
let v = F::read_metadata(&mut f.f, ())?; let v = S::read_metadata(&mut f.f, ())?;
Ok((f, v)) Ok((f, v))
} }
pub fn into_buffered_reader( pub fn into_buffered_reader(self) -> SDSSFileIO<BufferedReader> {
self, SDSSFileIO::new(self.f.into_buffered_reader())
) -> RuntimeResult<SDSSFileIO<Fs, <Fs::File as FileInterface>::BufReader>> {
self.f.upgrade_to_buffered_reader().map(SDSSFileIO::_new)
} }
pub fn into_buffered_writer( pub fn into_buffered_writer(self) -> SDSSFileIO<BufferedWriter> {
self, SDSSFileIO::new(self.f.into_buffered_writer())
) -> RuntimeResult<SDSSFileIO<Fs, <Fs::File as FileInterface>::BufWriter>> {
self.f.upgrade_to_buffered_writer().map(SDSSFileIO::_new)
} }
} }
impl<Fs: FSInterface> SDSSFileIO<Fs, <Fs::File as FileInterface>::BufReader> { impl SDSSFileIO<BufferedReader> {
pub fn downgrade_reader(self) -> RuntimeResult<SDSSFileIO<Fs, Fs::File>> { pub fn downgrade_reader(self) -> SDSSFileIO<File> {
let me = <Fs::File as FileInterface>::downgrade_reader(self.f)?; SDSSFileIO::_new(self.f.into_inner())
Ok(SDSSFileIO::_new(me))
} }
} }
impl<Fs: FSInterface> SDSSFileIO<Fs, <Fs::File as FileInterface>::BufWriter> { impl SDSSFileIO<BufferedWriter> {
pub fn downgrade_writer(self) -> RuntimeResult<SDSSFileIO<Fs>> { pub fn downgrade_writer(self) -> IoResult<SDSSFileIO<File>> {
let me = <Fs::File as FileInterface>::downgrade_writer(self.f)?; self.f.into_inner().map(SDSSFileIO::_new)
Ok(SDSSFileIO::_new(me))
} }
} }
impl<Fs: FSInterface, F> SDSSFileIO<Fs, F> { impl<F> SDSSFileIO<F> {
fn _new(f: F) -> Self { fn _new(f: F) -> Self {
Self { Self { f }
f,
_fs: PhantomData,
}
} }
} }
impl<Fs: FSInterface, F: FileInterfaceRead> SDSSFileIO<Fs, F> { impl<F: FileRead> SDSSFileIO<F> {
pub fn read_buffer(&mut self, buffer: &mut [u8]) -> RuntimeResult<()> { pub fn read_buffer(&mut self, buffer: &mut [u8]) -> IoResult<()> {
self.f.fread_exact(buffer) self.f.fread_exact(buffer)
} }
} }
impl<Fs: FSInterface, F: FileInterfaceExt> SDSSFileIO<Fs, F> { impl<F: FileExt> SDSSFileIO<F> {
pub fn file_cursor(&mut self) -> RuntimeResult<u64> { pub fn file_cursor(&mut self) -> IoResult<u64> {
self.f.fext_cursor() self.f.f_cursor()
} }
pub fn file_length(&self) -> RuntimeResult<u64> { pub fn file_length(&self) -> IoResult<u64> {
self.f.fext_length() self.f.f_len()
} }
pub fn seek_from_start(&mut self, by: u64) -> RuntimeResult<()> { pub fn seek_from_start(&mut self, by: u64) -> IoResult<()> {
self.f.fext_seek_ahead_from_start_by(by) self.f.f_seek_start(by)
} }
} }
impl<Fs: FSInterface, F: FileInterfaceRead + FileInterfaceExt> SDSSFileIO<Fs, F> { impl<F: FileRead + FileExt> SDSSFileIO<F> {
pub fn read_full(&mut self) -> RuntimeResult<Vec<u8>> { pub fn read_full(&mut self) -> IoResult<Vec<u8>> {
let len = self.file_length()? - self.file_cursor()?; let len = self.file_length()? - self.file_cursor()?;
let mut buf = vec![0; len as usize]; let mut buf = vec![0; len as usize];
self.read_buffer(&mut buf)?; self.read_buffer(&mut buf)?;
@ -207,9 +198,9 @@ impl<Fs: FSInterface, F: FileInterfaceRead + FileInterfaceExt> SDSSFileIO<Fs, F>
} }
} }
impl<Fs: FSInterface, F: FileInterfaceWrite + FileInterfaceWriteExt> SDSSFileIO<Fs, F> { impl<F: FileWrite + FileWriteExt> SDSSFileIO<F> {
pub fn fsynced_write(&mut self, data: &[u8]) -> RuntimeResult<()> { pub fn fsynced_write(&mut self, data: &[u8]) -> IoResult<()> {
self.f.fw_write_all(data)?; self.f.fwrite_all(data)?;
self.f.fwext_sync_all() self.f.fsync_all()
} }
} }

@ -30,9 +30,7 @@ use {
core::system_db::SystemDatabase, core::system_db::SystemDatabase,
data::{cell::Datacell, DictEntryGeneric, DictGeneric}, data::{cell::Datacell, DictEntryGeneric, DictGeneric},
error::{RuntimeResult, StorageError}, error::{RuntimeResult, StorageError},
storage::{ storage::{common_encoding::r1, v1::raw::rw::SDSSFileIO},
common::interface::fs_traits::FSInterface, common_encoding::r1, v1::raw::rw::SDSSFileIO,
},
}, },
std::collections::HashMap, std::collections::HashMap,
}; };
@ -71,8 +69,8 @@ impl RestoredSystemDatabase {
settings_version, settings_version,
} }
} }
pub fn restore<Fs: FSInterface>(name: &str) -> RuntimeResult<Self> { pub fn restore(name: &str) -> RuntimeResult<Self> {
let (mut f, _) = SDSSFileIO::<Fs>::open::<SysDBV1>(name)?; let (mut f, _) = SDSSFileIO::open::<SysDBV1>(name)?;
let mut sysdb_data = r1::dec::dict_full::<r1::map::GenericDictSpec>(&f.read_full()?)?; let mut sysdb_data = r1::dec::dict_full::<r1::map::GenericDictSpec>(&f.read_full()?)?;
// get our auth and sys stores // get our auth and sys stores
let mut auth_store = rkey( let mut auth_store = rkey(

@ -33,7 +33,6 @@ use {
engine::{ engine::{
core::GlobalNS, core::GlobalNS,
storage::{ storage::{
common::interface::fs_traits::FSInterface,
common_encoding::r1::impls::gns::GNSEvent, common_encoding::r1::impls::gns::GNSEvent,
v2::raw::journal::{self, EventLogDriver, JournalAdapterEvent}, v2::raw::journal::{self, EventLogDriver, JournalAdapterEvent},
}, },
@ -56,19 +55,19 @@ use {
GNS event log impl GNS event log impl
*/ */
pub type GNSDriver<Fs> = EventLogDriver<GNSEventLog, Fs>; pub type GNSDriver = EventLogDriver<GNSEventLog>;
pub struct GNSEventLog; pub struct GNSEventLog;
impl<Fs: FSInterface> GNSDriver<Fs> { impl GNSDriver {
const FILE_PATH: &'static str = "gns.db-tlog"; const FILE_PATH: &'static str = "gns.db-tlog";
pub fn open_gns_with_name(name: &str, gs: &GlobalNS) -> RuntimeResult<Self> { pub fn open_gns_with_name(name: &str, gs: &GlobalNS) -> RuntimeResult<Self> {
journal::open_journal::<_, Fs>(name, gs) journal::open_journal(name, gs)
} }
pub fn open_gns(gs: &GlobalNS) -> RuntimeResult<Self> { pub fn open_gns(gs: &GlobalNS) -> RuntimeResult<Self> {
Self::open_gns_with_name(Self::FILE_PATH, gs) Self::open_gns_with_name(Self::FILE_PATH, gs)
} }
pub fn create_gns_with_name(name: &str) -> RuntimeResult<Self> { pub fn create_gns_with_name(name: &str) -> RuntimeResult<Self> {
journal::create_journal::<_, Fs>(name) journal::create_journal(name)
} }
/// Create a new event log /// Create a new event log
pub fn create_gns() -> RuntimeResult<Self> { pub fn create_gns() -> RuntimeResult<Self> {

@ -42,7 +42,7 @@ use {
idx::{MTIndex, STIndex, STIndexSeq}, idx::{MTIndex, STIndex, STIndexSeq},
storage::{ storage::{
common::{ common::{
interface::fs_traits::{FSInterface, FileInterface}, interface::fs::File,
sdss::sdss_r1::rw::{TrackedReaderContext, TrackedWriter}, sdss::sdss_r1::rw::{TrackedReaderContext, TrackedWriter},
}, },
common_encoding::r1, common_encoding::r1,
@ -67,14 +67,14 @@ use {
}, },
}; };
pub type ModelDriver<Fs> = BatchDriver<ModelDataAdapter, Fs>; pub type ModelDriver = BatchDriver<ModelDataAdapter>;
impl<Fs: FSInterface> ModelDriver<Fs> { impl ModelDriver {
pub fn open_model_driver(mdl: &Model, model_data_file_path: &str) -> RuntimeResult<Self> { pub fn open_model_driver(mdl: &Model, model_data_file_path: &str) -> RuntimeResult<Self> {
journal::open_journal::<_, Fs>(model_data_file_path, mdl) journal::open_journal(model_data_file_path, mdl)
} }
/// Create a new event log /// Create a new event log
pub fn create_model_driver(model_data_file_path: &str) -> RuntimeResult<Self> { pub fn create_model_driver(model_data_file_path: &str) -> RuntimeResult<Self> {
journal::create_journal::<_, Fs>(model_data_file_path) journal::create_journal(model_data_file_path)
} }
} }
@ -109,11 +109,11 @@ pub enum EventType {
a little messy. a little messy.
*/ */
struct RowWriter<'b, Fs: FSInterface> { struct RowWriter<'b> {
f: &'b mut TrackedWriter<Fs::File, <BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec>, f: &'b mut TrackedWriter<File, <BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec>,
} }
impl<'b, Fs: FSInterface> RowWriter<'b, Fs> { impl<'b> RowWriter<'b> {
/// write global row information: /// write global row information:
/// - pk tag /// - pk tag
/// - schema version /// - schema version
@ -128,8 +128,9 @@ impl<'b, Fs: FSInterface> RowWriter<'b, Fs> {
.value_u64() .value_u64()
.u64_bytes_le(), .u64_bytes_le(),
)?; )?;
self.f e!(self
.dtrack_write(&(model.fields().st_len() - 1).u64_bytes_le()) .f
.dtrack_write(&(model.fields().st_len() - 1).u64_bytes_le()))
} }
/// write row metadata: /// write row metadata:
/// - change type /// - change type
@ -204,22 +205,19 @@ impl<'b, Fs: FSInterface> RowWriter<'b, Fs> {
} }
} }
struct BatchWriter<'a, 'b, Fs: FSInterface> { struct BatchWriter<'a, 'b> {
model: &'a Model, model: &'a Model,
row_writer: RowWriter<'b, Fs>, row_writer: RowWriter<'b>,
g: &'a Guard, g: &'a Guard,
sync_count: usize, sync_count: usize,
} }
impl<'a, 'b, Fs: FSInterface> BatchWriter<'a, 'b, Fs> { impl<'a, 'b> BatchWriter<'a, 'b> {
fn write_batch( fn write_batch(
model: &'a Model, model: &'a Model,
g: &'a Guard, g: &'a Guard,
expected: usize, expected: usize,
f: &'b mut TrackedWriter< f: &'b mut TrackedWriter<File, <BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec>,
Fs::File,
<BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec,
>,
batch_stat: &mut BatchStats, batch_stat: &mut BatchStats,
) -> RuntimeResult<usize> { ) -> RuntimeResult<usize> {
/* /*
@ -252,10 +250,7 @@ impl<'a, 'b, Fs: FSInterface> BatchWriter<'a, 'b, Fs> {
fn new( fn new(
model: &'a Model, model: &'a Model,
g: &'a Guard, g: &'a Guard,
f: &'b mut TrackedWriter< f: &'b mut TrackedWriter<File, <BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec>,
Fs::File,
<BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec,
>,
) -> RuntimeResult<Self> { ) -> RuntimeResult<Self> {
let mut row_writer = RowWriter { f }; let mut row_writer = RowWriter { f };
row_writer.write_row_global_metadata(model)?; row_writer.write_row_global_metadata(model)?;
@ -310,10 +305,10 @@ impl<'a> JournalAdapterEvent<BatchAdapter<ModelDataAdapter>> for StdModelBatch<'
fn md(&self) -> u64 { fn md(&self) -> u64 {
BatchType::Standard.dscr_u64() BatchType::Standard.dscr_u64()
} }
fn write_direct<Fs: FSInterface>( fn write_direct(
self, self,
writer: &mut TrackedWriter< writer: &mut TrackedWriter<
Fs::File, File,
<BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec, <BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec,
>, >,
ctx: Rc<RefCell<BatchStats>>, ctx: Rc<RefCell<BatchStats>>,
@ -322,12 +317,12 @@ impl<'a> JournalAdapterEvent<BatchAdapter<ModelDataAdapter>> for StdModelBatch<'
writer.dtrack_write(&self.1.u64_bytes_le())?; writer.dtrack_write(&self.1.u64_bytes_le())?;
let g = pin(); let g = pin();
let actual_commit = let actual_commit =
BatchWriter::<Fs>::write_batch(self.0, &g, self.1, writer, &mut ctx.borrow_mut())?; BatchWriter::write_batch(self.0, &g, self.1, writer, &mut ctx.borrow_mut())?;
if actual_commit != self.1 { if actual_commit != self.1 {
// early exit // early exit
writer.dtrack_write(&[EventType::EarlyExit.dscr()])?; writer.dtrack_write(&[EventType::EarlyExit.dscr()])?;
} }
writer.dtrack_write(&actual_commit.u64_bytes_le()) e!(writer.dtrack_write(&actual_commit.u64_bytes_le()))
} }
} }
@ -343,16 +338,13 @@ impl<'a> JournalAdapterEvent<BatchAdapter<ModelDataAdapter>> for FullModel<'a> {
fn md(&self) -> u64 { fn md(&self) -> u64 {
BatchType::Standard.dscr_u64() BatchType::Standard.dscr_u64()
} }
fn write_direct<Fs: FSInterface>( fn write_direct(
self, self,
f: &mut TrackedWriter< f: &mut TrackedWriter<File, <BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec>,
Fs::File,
<BatchAdapter<ModelDataAdapter> as RawJournalAdapter>::Spec,
>,
_: Rc<RefCell<BatchStats>>, _: Rc<RefCell<BatchStats>>,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
let g = pin(); let g = pin();
let mut row_writer: RowWriter<'_, Fs> = RowWriter { f }; let mut row_writer: RowWriter<'_> = RowWriter { f };
let index = self.0.primary_index().__raw_index(); let index = self.0.primary_index().__raw_index();
let current_row_count = index.mt_len(); let current_row_count = index.mt_len();
// expect commit == current row count // expect commit == current row count
@ -452,21 +444,17 @@ impl BatchAdapterSpec for ModelDataAdapter {
fn initialize_batch_state(_: &Self::GlobalState) -> Self::BatchState { fn initialize_batch_state(_: &Self::GlobalState) -> Self::BatchState {
BatchRestoreState { events: Vec::new() } BatchRestoreState { events: Vec::new() }
} }
fn decode_batch_metadata<Fs: FSInterface>( fn decode_batch_metadata(
_: &Self::GlobalState, _: &Self::GlobalState,
f: &mut TrackedReaderContext< f: &mut TrackedReaderContext<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
batch_type: Self::BatchType, batch_type: Self::BatchType,
) -> RuntimeResult<Self::BatchMetadata> { ) -> RuntimeResult<Self::BatchMetadata> {
// [pk tag][schema version][column cnt] // [pk tag][schema version][column cnt]
match batch_type { match batch_type {
BatchType::Standard => {} BatchType::Standard => {}
} }
let pk_tag = f.read_block().and_then(|[b]| { let pk_tag = TagUnique::try_from_raw(f.read_block().map(|[b]| b)?)
TagUnique::try_from_raw(b).ok_or(StorageError::RawJournalCorrupted.into()) .ok_or(StorageError::RawJournalCorrupted)?;
})?;
let schema_version = u64::from_le_bytes(f.read_block()?); let schema_version = u64::from_le_bytes(f.read_block()?);
let column_count = u64::from_le_bytes(f.read_block()?); let column_count = u64::from_le_bytes(f.read_block()?);
Ok(BatchMetadata { Ok(BatchMetadata {
@ -475,20 +463,17 @@ impl BatchAdapterSpec for ModelDataAdapter {
column_count, column_count,
}) })
} }
fn update_state_for_new_event<Fs: FSInterface>( fn update_state_for_new_event(
_: &Self::GlobalState, _: &Self::GlobalState,
bs: &mut Self::BatchState, bs: &mut Self::BatchState,
f: &mut TrackedReaderContext< f: &mut TrackedReaderContext<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
batch_info: &Self::BatchMetadata, batch_info: &Self::BatchMetadata,
event_type: Self::EventType, event_type: Self::EventType,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
// get txn id // get txn id
let txn_id = u64::from_le_bytes(f.read_block()?); let txn_id = u64::from_le_bytes(f.read_block()?);
// get pk // get pk
let pk = restore_impls::decode_primary_key::<Fs, Self::Spec>(f, batch_info.pk_tag)?; let pk = restore_impls::decode_primary_key::<Self::Spec>(f, batch_info.pk_tag)?;
match event_type { match event_type {
EventType::Delete => { EventType::Delete => {
bs.events.push(DecodedBatchEvent::new( bs.events.push(DecodedBatchEvent::new(
@ -500,7 +485,7 @@ impl BatchAdapterSpec for ModelDataAdapter {
EventType::Insert | EventType::Update => { EventType::Insert | EventType::Update => {
// insert or update // insert or update
// prepare row // prepare row
let row = restore_impls::decode_row_data::<Fs>(batch_info, f)?; let row = restore_impls::decode_row_data(batch_info, f)?;
if event_type == EventType::Insert { if event_type == EventType::Insert {
bs.events.push(DecodedBatchEvent::new( bs.events.push(DecodedBatchEvent::new(
txn_id, txn_id,
@ -641,10 +626,7 @@ mod restore_impls {
data::{cell::Datacell, tag::TagUnique}, data::{cell::Datacell, tag::TagUnique},
error::StorageError, error::StorageError,
storage::{ storage::{
common::{ common::sdss::sdss_r1::{rw::TrackedReaderContext, FileSpecV1},
interface::fs_traits::{FSInterface, FileInterface, FileInterfaceRead},
sdss::sdss_r1::{rw::TrackedReaderContext, FileSpecV1},
},
common_encoding::r1::{ common_encoding::r1::{
obj::cell::{self, StorageCellTypeID}, obj::cell::{self, StorageCellTypeID},
DataSource, DataSource,
@ -658,8 +640,8 @@ mod restore_impls {
/// Primary key decode impl /// Primary key decode impl
/// ///
/// NB: We really need to make this generic, but for now we can settle for this /// NB: We really need to make this generic, but for now we can settle for this
pub fn decode_primary_key<Fs: FSInterface, S: FileSpecV1>( pub fn decode_primary_key<S: FileSpecV1>(
f: &mut TrackedReaderContext<<<Fs as FSInterface>::File as FileInterface>::BufReader, S>, f: &mut TrackedReaderContext<S>,
pk_type: TagUnique, pk_type: TagUnique,
) -> RuntimeResult<PrimaryIndexKey> { ) -> RuntimeResult<PrimaryIndexKey> {
Ok(match pk_type { Ok(match pk_type {
@ -691,12 +673,9 @@ mod restore_impls {
}, },
}) })
} }
pub fn decode_row_data<Fs: FSInterface>( pub fn decode_row_data(
batch_info: &BatchMetadata, batch_info: &BatchMetadata,
f: &mut TrackedReaderContext< f: &mut TrackedReaderContext<ModelDataBatchAofV1>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
ModelDataBatchAofV1,
>,
) -> Result<Vec<Datacell>, crate::engine::fractal::error::Error> { ) -> Result<Vec<Datacell>, crate::engine::fractal::error::Error> {
let mut row = vec![]; let mut row = vec![];
let mut this_col_cnt = batch_info.column_count; let mut this_col_cnt = batch_info.column_count;
@ -721,12 +700,17 @@ mod restore_impls {
Self(value) Self(value)
} }
} }
impl From<std::io::Error> for ErrorHack {
fn from(value: std::io::Error) -> Self {
Self(value.into())
}
}
impl From<()> for ErrorHack { impl From<()> for ErrorHack {
fn from(_: ()) -> Self { fn from(_: ()) -> Self {
Self(StorageError::DataBatchRestoreCorruptedEntry.into()) Self(StorageError::DataBatchRestoreCorruptedEntry.into())
} }
} }
impl<'a, F: FileInterfaceRead> DataSource for TrackedReaderContext<'a, F, ModelDataBatchAofV1> { impl<'a> DataSource for TrackedReaderContext<'a, ModelDataBatchAofV1> {
const RELIABLE_SOURCE: bool = false; const RELIABLE_SOURCE: bool = false;
type Error = ErrorHack; type Error = ErrorHack;
fn has_remaining(&self, cnt: usize) -> bool { fn has_remaining(&self, cnt: usize) -> bool {

@ -37,7 +37,6 @@ use {
dml::{ins::InsertStatement, upd::UpdateStatement}, dml::{ins::InsertStatement, upd::UpdateStatement},
tests::lex_insecure, tests::lex_insecure,
}, },
storage::common::interface::fs_test::VirtualFS,
}, },
util::test_utils, util::test_utils,
}, },
@ -64,10 +63,7 @@ fn create_test_kv_int(change_count: usize) -> Vec<(u64, String)> {
.collect() .collect()
} }
fn create_model_and_space( fn create_model_and_space(global: &TestGlobal, create_model: &str) -> QueryResult<EntityID> {
global: &TestGlobal<VirtualFS>,
create_model: &str,
) -> QueryResult<EntityID> {
let tokens = lex_insecure(create_model.as_bytes()).unwrap(); let tokens = lex_insecure(create_model.as_bytes()).unwrap();
let create_model: CreateModel = ast::parse_ast_node_full(&tokens[2..]).unwrap(); let create_model: CreateModel = ast::parse_ast_node_full(&tokens[2..]).unwrap();
let mdl_name = EntityID::new( let mdl_name = EntityID::new(
@ -82,13 +78,13 @@ fn create_model_and_space(
Model::transactional_exec_create(global, create_model).map(|_| mdl_name) Model::transactional_exec_create(global, create_model).map(|_| mdl_name)
} }
fn run_insert(global: &TestGlobal<VirtualFS>, insert: &str) -> QueryResult<()> { fn run_insert(global: &TestGlobal, insert: &str) -> QueryResult<()> {
let tokens = lex_insecure(insert.as_bytes()).unwrap(); let tokens = lex_insecure(insert.as_bytes()).unwrap();
let insert: InsertStatement = ast::parse_ast_node_full(&tokens[1..]).unwrap(); let insert: InsertStatement = ast::parse_ast_node_full(&tokens[1..]).unwrap();
dml::insert(global, insert) dml::insert(global, insert)
} }
fn run_update(global: &TestGlobal<VirtualFS>, update: &str) -> QueryResult<()> { fn run_update(global: &TestGlobal, update: &str) -> QueryResult<()> {
let tokens = lex_insecure(update.as_bytes()).unwrap(); let tokens = lex_insecure(update.as_bytes()).unwrap();
let insert: UpdateStatement = ast::parse_ast_node_full(&tokens[1..]).unwrap(); let insert: UpdateStatement = ast::parse_ast_node_full(&tokens[1..]).unwrap();
dml::update(global, insert) dml::update(global, insert)
@ -110,12 +106,12 @@ fn create_and_close(log_name: &str, decl: &str) {
test_utils::with_variable(log_name, |log_name| { test_utils::with_variable(log_name, |log_name| {
// create and close // create and close
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
let _ = create_model_and_space(&global, decl).unwrap(); let _ = create_model_and_space(&global, decl).unwrap();
} }
// open // open
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
drop(global); drop(global);
} }
}) })
@ -135,7 +131,7 @@ fn run_sample_inserts<K, V>(
// create, insert and close // create, insert and close
let mdl_name; let mdl_name;
{ {
let mut global = TestGlobal::new_with_vfs_driver(log_name); let mut global = TestGlobal::new_with_driver_id(log_name);
global.set_max_data_pressure(key_values.len()); global.set_max_data_pressure(key_values.len());
mdl_name = create_model_and_space(&global, decl).unwrap(); mdl_name = create_model_and_space(&global, decl).unwrap();
for (username, password) in key_values.iter() { for (username, password) in key_values.iter() {
@ -144,7 +140,7 @@ fn run_sample_inserts<K, V>(
} }
// reopen and verify 100 times // reopen and verify 100 times
test_utils::multi_run(100, || { test_utils::multi_run(100, || {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
global.load_model_drivers().unwrap(); global.load_model_drivers().unwrap();
global global
.state() .state()
@ -189,7 +185,7 @@ fn run_sample_updates<K, V>(
let mdl_name; let mdl_name;
{ {
// insert n values // insert n values
let mut global = TestGlobal::new_with_vfs_driver(log_name); let mut global = TestGlobal::new_with_driver_id(log_name);
global.set_max_data_pressure(n); global.set_max_data_pressure(n);
mdl_name = create_model_and_space(&global, decl).unwrap(); mdl_name = create_model_and_space(&global, decl).unwrap();
for (username, password) in key_values.iter() { for (username, password) in key_values.iter() {
@ -204,7 +200,7 @@ fn run_sample_updates<K, V>(
// now update values // now update values
let mut actual_position = 0; let mut actual_position = 0;
for _ in 0..reopen_count { for _ in 0..reopen_count {
let mut global = TestGlobal::new_with_vfs_driver(log_name); let mut global = TestGlobal::new_with_driver_id(log_name);
global.set_max_data_pressure(changes_per_cycle); global.set_max_data_pressure(changes_per_cycle);
global.load_model_drivers().unwrap(); global.load_model_drivers().unwrap();
let mut j = 0; let mut j = 0;
@ -220,7 +216,7 @@ fn run_sample_updates<K, V>(
assert_eq!(actual_position, n); assert_eq!(actual_position, n);
} }
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_driver_id(log_name);
global.load_model_drivers().unwrap(); global.load_model_drivers().unwrap();
for (txn_id, (username, password)) in key_values for (txn_id, (username, password)) in key_values
.iter() .iter()

@ -26,10 +26,7 @@
use { use {
self::impls::mdl_journal::{BatchStats, FullModel}, self::impls::mdl_journal::{BatchStats, FullModel},
super::{ super::{common::interface::fs::FileSystem, v1, SELoaded},
common::interface::{fs_imp::LocalFS, fs_traits::FSInterface},
v1, SELoaded,
},
crate::engine::{ crate::engine::{
config::Configuration, config::Configuration,
core::{ core::{
@ -64,14 +61,14 @@ pub fn recreate(gns: GlobalNS) -> RuntimeResult<SELoaded> {
// create all spaces // create all spaces
context::set_dmsg("creating all spaces"); context::set_dmsg("creating all spaces");
for (space_name, space) in gns.idx().read().iter() { for (space_name, space) in gns.idx().read().iter() {
LocalFS::fs_create_dir_all(&paths_v1::space_dir(space_name, space.get_uuid()))?; FileSystem::create_dir_all(&paths_v1::space_dir(space_name, space.get_uuid()))?;
gns_driver.commit_event(CreateSpaceTxn::new(space.props(), &space_name, space))?; gns_driver.commit_event(CreateSpaceTxn::new(space.props(), &space_name, space))?;
} }
// create all models // create all models
context::set_dmsg("creating all models"); context::set_dmsg("creating all models");
for (model_id, model) in gns.idx_models().read().iter() { for (model_id, model) in gns.idx_models().read().iter() {
let space_uuid = gns.idx().read().get(model_id.space()).unwrap().get_uuid(); let space_uuid = gns.idx().read().get(model_id.space()).unwrap().get_uuid();
LocalFS::fs_create_dir_all(&paths_v1::model_dir( FileSystem::create_dir_all(&paths_v1::model_dir(
model_id.space(), model_id.space(),
space_uuid, space_uuid,
model_id.entity(), model_id.entity(),
@ -102,7 +99,7 @@ pub fn recreate(gns: GlobalNS) -> RuntimeResult<SELoaded> {
} }
pub fn initialize_new(config: &Configuration) -> RuntimeResult<SELoaded> { pub fn initialize_new(config: &Configuration) -> RuntimeResult<SELoaded> {
LocalFS::fs_create_dir_all(DATA_DIR)?; FileSystem::create_dir_all(DATA_DIR)?;
let mut gns_driver = impls::gns_log::GNSDriver::create_gns()?; let mut gns_driver = impls::gns_log::GNSDriver::create_gns()?;
let gns = GlobalNS::empty(); let gns = GlobalNS::empty();
let password_hash = rcrypt::hash(&config.auth.root_key, rcrypt::DEFAULT_COST).unwrap(); let password_hash = rcrypt::hash(&config.auth.root_key, rcrypt::DEFAULT_COST).unwrap();

@ -24,6 +24,8 @@
* *
*/ */
use crate::engine::storage::common::interface::fs::File;
use { use {
self::raw::{CommitPreference, RawJournalAdapterEvent, RawJournalWriter}, self::raw::{CommitPreference, RawJournalAdapterEvent, RawJournalWriter},
crate::{ crate::{
@ -31,7 +33,6 @@ use {
error::StorageError, error::StorageError,
storage::common::{ storage::common::{
checksum::SCrc64, checksum::SCrc64,
interface::fs_traits::{FSInterface, FileInterface},
sdss::sdss_r1::{ sdss::sdss_r1::{
rw::{TrackedReader, TrackedReaderContext, TrackedWriter}, rw::{TrackedReader, TrackedReaderContext, TrackedWriter},
FileSpecV1, FileSpecV1,
@ -62,7 +63,7 @@ pub use raw::{
*/ */
/// An event log driver /// An event log driver
pub type EventLogDriver<EL, Fs> = RawJournalWriter<EventLogAdapter<EL>, Fs>; pub type EventLogDriver<EL> = RawJournalWriter<EventLogAdapter<EL>>;
/// The event log adapter /// The event log adapter
pub struct EventLogAdapter<EL: EventLogSpec>(PhantomData<EL>); pub struct EventLogAdapter<EL: EventLogSpec>(PhantomData<EL>);
type DispatchFn<G> = fn(&G, Vec<u8>) -> RuntimeResult<()>; type DispatchFn<G> = fn(&G, Vec<u8>) -> RuntimeResult<()>;
@ -96,16 +97,13 @@ impl<EL: EventLogSpec> RawJournalAdapter for EventLogAdapter<EL> {
fn initialize(_: &raw::JournalInitializer) -> Self { fn initialize(_: &raw::JournalInitializer) -> Self {
Self(PhantomData) Self(PhantomData)
} }
fn enter_context<'a, Fs: FSInterface>( fn enter_context<'a>(_: &'a mut RawJournalWriter<Self>) -> Self::Context<'a> {}
_: &'a mut RawJournalWriter<Self, Fs>,
) -> Self::Context<'a> {
}
fn parse_event_meta(meta: u64) -> Option<Self::EventMeta> { fn parse_event_meta(meta: u64) -> Option<Self::EventMeta> {
<<EL as EventLogSpec>::EventMeta as TaggedEnum>::try_from_raw(meta as u8) <<EL as EventLogSpec>::EventMeta as TaggedEnum>::try_from_raw(meta as u8)
} }
fn commit_direct<'a, Fs: FSInterface, E>( fn commit_direct<'a, E>(
&mut self, &mut self,
w: &mut TrackedWriter<Fs::File, Self::Spec>, w: &mut TrackedWriter<File, Self::Spec>,
ev: E, ev: E,
ctx: (), ctx: (),
) -> RuntimeResult<()> ) -> RuntimeResult<()>
@ -124,15 +122,12 @@ impl<EL: EventLogSpec> RawJournalAdapter for EventLogAdapter<EL> {
*/ */
w.tracked_write(&checksum)?; w.tracked_write(&checksum)?;
w.tracked_write(&plen)?; w.tracked_write(&plen)?;
w.tracked_write(&pl) e!(w.tracked_write(&pl))
} }
fn decode_apply<'a, Fs: FSInterface>( fn decode_apply<'a>(
gs: &Self::GlobalState, gs: &Self::GlobalState,
meta: Self::EventMeta, meta: Self::EventMeta,
file: &mut TrackedReader< file: &mut TrackedReader<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
let expected_checksum = u64::from_le_bytes(file.read_block()?); let expected_checksum = u64::from_le_bytes(file.read_block()?);
let plen = u64::from_le_bytes(file.read_block()?); let plen = u64::from_le_bytes(file.read_block()?);
@ -163,31 +158,28 @@ impl<EL: EventLogSpec> RawJournalAdapter for EventLogAdapter<EL> {
*/ */
/// Batch journal driver /// Batch journal driver
pub type BatchDriver<BA, Fs> = RawJournalWriter<BatchAdapter<BA>, Fs>; pub type BatchDriver<BA> = RawJournalWriter<BatchAdapter<BA>>;
/// Batch journal adapter /// Batch journal adapter
pub struct BatchAdapter<BA: BatchAdapterSpec>(PhantomData<BA>); pub struct BatchAdapter<BA: BatchAdapterSpec>(PhantomData<BA>);
#[cfg(test)] #[cfg(test)]
impl<BA: BatchAdapterSpec> BatchAdapter<BA> { impl<BA: BatchAdapterSpec> BatchAdapter<BA> {
/// Open a new batch journal /// Open a new batch journal
pub fn open<Fs: FSInterface>( pub fn open(name: &str, gs: &BA::GlobalState) -> RuntimeResult<BatchDriver<BA>>
name: &str,
gs: &BA::GlobalState,
) -> RuntimeResult<BatchDriver<BA, Fs>>
where where
BA::Spec: FileSpecV1<DecodeArgs = ()>, BA::Spec: FileSpecV1<DecodeArgs = ()>,
{ {
raw::open_journal::<BatchAdapter<BA>, Fs>(name, gs) raw::open_journal::<BatchAdapter<BA>>(name, gs)
} }
/// Create a new batch journal /// Create a new batch journal
pub fn create<Fs: FSInterface>(name: &str) -> RuntimeResult<BatchDriver<BA, Fs>> pub fn create(name: &str) -> RuntimeResult<BatchDriver<BA>>
where where
BA::Spec: FileSpecV1<EncodeArgs = ()>, BA::Spec: FileSpecV1<EncodeArgs = ()>,
{ {
raw::create_journal::<BatchAdapter<BA>, Fs>(name) raw::create_journal::<BatchAdapter<BA>>(name)
} }
/// Close a batch journal /// Close a batch journal
pub fn close<Fs: FSInterface>(me: &mut BatchDriver<BA, Fs>) -> RuntimeResult<()> { pub fn close(me: &mut BatchDriver<BA>) -> RuntimeResult<()> {
RawJournalWriter::close_driver(me) RawJournalWriter::close_driver(me)
} }
} }
@ -216,22 +208,16 @@ pub trait BatchAdapterSpec {
/// initialize the batch state /// initialize the batch state
fn initialize_batch_state(gs: &Self::GlobalState) -> Self::BatchState; fn initialize_batch_state(gs: &Self::GlobalState) -> Self::BatchState;
/// decode batch start metadata /// decode batch start metadata
fn decode_batch_metadata<Fs: FSInterface>( fn decode_batch_metadata(
gs: &Self::GlobalState, gs: &Self::GlobalState,
f: &mut TrackedReaderContext< f: &mut TrackedReaderContext<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
meta: Self::BatchType, meta: Self::BatchType,
) -> RuntimeResult<Self::BatchMetadata>; ) -> RuntimeResult<Self::BatchMetadata>;
/// decode new event and update state. if called, it is guaranteed that the event is not an early exit /// decode new event and update state. if called, it is guaranteed that the event is not an early exit
fn update_state_for_new_event<Fs: FSInterface>( fn update_state_for_new_event(
gs: &Self::GlobalState, gs: &Self::GlobalState,
bs: &mut Self::BatchState, bs: &mut Self::BatchState,
f: &mut TrackedReaderContext< f: &mut TrackedReaderContext<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
batch_info: &Self::BatchMetadata, batch_info: &Self::BatchMetadata,
event_type: Self::EventType, event_type: Self::EventType,
) -> RuntimeResult<()>; ) -> RuntimeResult<()>;
@ -253,30 +239,27 @@ impl<BA: BatchAdapterSpec> RawJournalAdapter for BatchAdapter<BA> {
fn initialize(_: &raw::JournalInitializer) -> Self { fn initialize(_: &raw::JournalInitializer) -> Self {
Self(PhantomData) Self(PhantomData)
} }
fn enter_context<'a, Fs: FSInterface>( fn enter_context<'a>(_: &'a mut RawJournalWriter<Self>) -> Self::Context<'a> {}
_: &'a mut RawJournalWriter<Self, Fs>,
) -> Self::Context<'a> {
}
fn parse_event_meta(meta: u64) -> Option<Self::EventMeta> { fn parse_event_meta(meta: u64) -> Option<Self::EventMeta> {
<<BA as BatchAdapterSpec>::BatchType as TaggedEnum>::try_from_raw(meta as u8) <<BA as BatchAdapterSpec>::BatchType as TaggedEnum>::try_from_raw(meta as u8)
} }
fn commit_direct<'a, Fs: FSInterface, E>( fn commit_direct<'a, E>(
&mut self, &mut self,
w: &mut TrackedWriter<Fs::File, Self::Spec>, w: &mut TrackedWriter<File, Self::Spec>,
ev: E, ev: E,
ctx: Self::CommitContext, ctx: Self::CommitContext,
) -> RuntimeResult<()> ) -> RuntimeResult<()>
where where
E: RawJournalAdapterEvent<Self>, E: RawJournalAdapterEvent<Self>,
{ {
ev.write_direct::<Fs>(w, ctx)?; ev.write_direct(w, ctx)?;
let checksum = w.reset_partial(); let checksum = w.reset_partial();
w.tracked_write(&checksum.to_le_bytes()) e!(w.tracked_write(&checksum.to_le_bytes()))
} }
fn decode_apply<'a, Fs: FSInterface>( fn decode_apply<'a>(
gs: &Self::GlobalState, gs: &Self::GlobalState,
meta: Self::EventMeta, meta: Self::EventMeta,
f: &mut TrackedReader<<<Fs as FSInterface>::File as FileInterface>::BufReader, Self::Spec>, f: &mut TrackedReader<Self::Spec>,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
let mut f = f.context(); let mut f = f.context();
{ {
@ -284,7 +267,7 @@ impl<BA: BatchAdapterSpec> RawJournalAdapter for BatchAdapter<BA> {
// read batch size // read batch size
let _stored_expected_commit_size = u64::from_le_bytes(f.read_block()?); let _stored_expected_commit_size = u64::from_le_bytes(f.read_block()?);
// read custom metadata // read custom metadata
let batch_md = <BA as BatchAdapterSpec>::decode_batch_metadata::<Fs>(gs, &mut f, meta)?; let batch_md = <BA as BatchAdapterSpec>::decode_batch_metadata(gs, &mut f, meta)?;
// now read in every event // now read in every event
let mut real_commit_size = 0; let mut real_commit_size = 0;
let mut batch_state = <BA as BatchAdapterSpec>::initialize_batch_state(gs); let mut batch_state = <BA as BatchAdapterSpec>::initialize_batch_state(gs);
@ -292,16 +275,16 @@ impl<BA: BatchAdapterSpec> RawJournalAdapter for BatchAdapter<BA> {
if real_commit_size == _stored_expected_commit_size { if real_commit_size == _stored_expected_commit_size {
break; break;
} }
let event_type = f.read_block().and_then(|[b]| { let event_type = <<BA as BatchAdapterSpec>::EventType as TaggedEnum>::try_from_raw(
<<BA as BatchAdapterSpec>::EventType as TaggedEnum>::try_from_raw(b) f.read_block().map(|[b]| b)?,
.ok_or(StorageError::RawJournalCorrupted.into()) )
})?; .ok_or(StorageError::RawJournalCorrupted)?;
// is this an early exit marker? if so, exit // is this an early exit marker? if so, exit
if <BA as BatchAdapterSpec>::is_early_exit(&event_type) { if <BA as BatchAdapterSpec>::is_early_exit(&event_type) {
break; break;
} }
// update batch state // update batch state
BA::update_state_for_new_event::<Fs>( BA::update_state_for_new_event(
gs, gs,
&mut batch_state, &mut batch_state,
&mut f, &mut f,

@ -33,7 +33,7 @@ use {
mem::unsafe_apis::memcpy, mem::unsafe_apis::memcpy,
storage::common::{ storage::common::{
checksum::SCrc64, checksum::SCrc64,
interface::fs_traits::{FSInterface, FileInterface}, interface::fs::File,
sdss::sdss_r1::{ sdss::sdss_r1::{
rw::{SdssFile, TrackedReader, TrackedWriter}, rw::{SdssFile, TrackedReader, TrackedWriter},
FileSpecV1, FileSpecV1,
@ -49,13 +49,11 @@ use {
*/ */
/// Create a new journal /// Create a new journal
pub fn create_journal<J: RawJournalAdapter, Fs: FSInterface>( pub fn create_journal<J: RawJournalAdapter>(log_path: &str) -> RuntimeResult<RawJournalWriter<J>>
log_path: &str,
) -> RuntimeResult<RawJournalWriter<J, Fs>>
where where
J::Spec: FileSpecV1<EncodeArgs = ()>, J::Spec: FileSpecV1<EncodeArgs = ()>,
{ {
let log = SdssFile::create::<Fs>(log_path)?; let log = SdssFile::create(log_path)?;
RawJournalWriter::new( RawJournalWriter::new(
JournalInitializer::new(<J::Spec as FileSpecV1>::SIZE as u64, SCrc64::new(), 0, 0), JournalInitializer::new(<J::Spec as FileSpecV1>::SIZE as u64, SCrc64::new(), 0, 0),
log, log,
@ -63,15 +61,15 @@ where
} }
/// Open an existing journal /// Open an existing journal
pub fn open_journal<J: RawJournalAdapter, Fs: FSInterface>( pub fn open_journal<J: RawJournalAdapter>(
log_path: &str, log_path: &str,
gs: &J::GlobalState, gs: &J::GlobalState,
) -> RuntimeResult<RawJournalWriter<J, Fs>> ) -> RuntimeResult<RawJournalWriter<J>>
where where
J::Spec: FileSpecV1<DecodeArgs = ()>, J::Spec: FileSpecV1<DecodeArgs = ()>,
{ {
let log = SdssFile::<_, J::Spec>::open::<Fs>(log_path)?; let log = SdssFile::<J::Spec>::open(log_path)?;
let (initializer, file) = RawJournalReader::<J, Fs>::scroll(log, gs)?; let (initializer, file) = RawJournalReader::<J>::scroll(log, gs)?;
RawJournalWriter::new(initializer, file) RawJournalWriter::new(initializer, file)
} }
@ -212,9 +210,9 @@ macro_rules! jtrace_reader {
pub trait RawJournalAdapterEvent<CA: RawJournalAdapter>: Sized { pub trait RawJournalAdapterEvent<CA: RawJournalAdapter>: Sized {
fn md(&self) -> u64; fn md(&self) -> u64;
fn write_direct<Fs: FSInterface>( fn write_direct(
self, self,
_: &mut TrackedWriter<Fs::File, <CA as RawJournalAdapter>::Spec>, _: &mut TrackedWriter<File, <CA as RawJournalAdapter>::Spec>,
_: <CA as RawJournalAdapter>::CommitContext, _: <CA as RawJournalAdapter>::CommitContext,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
unimplemented!() unimplemented!()
@ -247,15 +245,13 @@ pub trait RawJournalAdapter: Sized {
/// initialize this adapter /// initialize this adapter
fn initialize(j_: &JournalInitializer) -> Self; fn initialize(j_: &JournalInitializer) -> Self;
/// get a write context /// get a write context
fn enter_context<'a, Fs: FSInterface>( fn enter_context<'a>(adapter: &'a mut RawJournalWriter<Self>) -> Self::Context<'a>;
adapter: &'a mut RawJournalWriter<Self, Fs>,
) -> Self::Context<'a>;
/// parse event metadata /// parse event metadata
fn parse_event_meta(meta: u64) -> Option<Self::EventMeta>; fn parse_event_meta(meta: u64) -> Option<Self::EventMeta>;
/// commit event (direct preference) /// commit event (direct preference)
fn commit_direct<Fs: FSInterface, E>( fn commit_direct<E>(
&mut self, &mut self,
_: &mut TrackedWriter<Fs::File, Self::Spec>, _: &mut TrackedWriter<File, Self::Spec>,
_: E, _: E,
_: Self::CommitContext, _: Self::CommitContext,
) -> RuntimeResult<()> ) -> RuntimeResult<()>
@ -272,13 +268,10 @@ pub trait RawJournalAdapter: Sized {
unimplemented!() unimplemented!()
} }
/// decode and apply the event /// decode and apply the event
fn decode_apply<'a, Fs: FSInterface>( fn decode_apply<'a>(
gs: &Self::GlobalState, gs: &Self::GlobalState,
meta: Self::EventMeta, meta: Self::EventMeta,
file: &mut TrackedReader< file: &mut TrackedReader<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
) -> RuntimeResult<()>; ) -> RuntimeResult<()>;
} }
@ -473,9 +466,9 @@ pub(super) enum DriverEventKind {
*/ */
/// A low-level journal writer /// A low-level journal writer
pub struct RawJournalWriter<J: RawJournalAdapter, Fs: FSInterface> { pub struct RawJournalWriter<J: RawJournalAdapter> {
j: J, j: J,
log_file: TrackedWriter<<Fs as FSInterface>::File, <J as RawJournalAdapter>::Spec>, log_file: TrackedWriter<File, <J as RawJournalAdapter>::Spec>,
txn_id: u64, txn_id: u64,
known_txn_id: u64, known_txn_id: u64,
known_txn_offset: u64, // if offset is 0, txn id is unset known_txn_offset: u64, // if offset is 0, txn id is unset
@ -483,9 +476,9 @@ pub struct RawJournalWriter<J: RawJournalAdapter, Fs: FSInterface> {
const SERVER_EV_MASK: u64 = 1 << (u64::BITS - 1); const SERVER_EV_MASK: u64 = 1 << (u64::BITS - 1);
impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalWriter<J, Fs> { impl<J: RawJournalAdapter> RawJournalWriter<J> {
/// Initialize a new [`RawJournalWriter`] using a [`JournalInitializer`] /// Initialize a new [`RawJournalWriter`] using a [`JournalInitializer`]
pub fn new(j_: JournalInitializer, file: SdssFile<Fs::File, J::Spec>) -> RuntimeResult<Self> { pub fn new(j_: JournalInitializer, file: SdssFile<J::Spec>) -> RuntimeResult<Self> {
let mut me = Self { let mut me = Self {
log_file: TrackedWriter::with_cursor_and_checksum(file, j_.cursor(), j_.checksum()), log_file: TrackedWriter::with_cursor_and_checksum(file, j_.cursor(), j_.checksum()),
known_txn_id: j_.last_txn_id(), known_txn_id: j_.last_txn_id(),
@ -533,7 +526,7 @@ impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalWriter<J, Fs> {
log_file.tracked_write(&ev_md.to_le_bytes())?; log_file.tracked_write(&ev_md.to_le_bytes())?;
jtrace_writer!(CommitServerEventWroteMetadata); jtrace_writer!(CommitServerEventWroteMetadata);
// now hand over control to adapter impl // now hand over control to adapter impl
J::commit_direct::<Fs, _>(j, log_file, event, ctx)?; J::commit_direct(j, log_file, event, ctx)?;
} }
} }
jtrace_writer!(CommitServerEventAdapterCompleted); jtrace_writer!(CommitServerEventAdapterCompleted);
@ -557,7 +550,7 @@ impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalWriter<J, Fs> {
} }
} }
impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalWriter<J, Fs> { impl<J: RawJournalAdapter> RawJournalWriter<J> {
fn txn_context<T>( fn txn_context<T>(
&mut self, &mut self,
f: impl FnOnce(&mut Self, u128) -> RuntimeResult<T>, f: impl FnOnce(&mut Self, u128) -> RuntimeResult<T>,
@ -609,11 +602,8 @@ impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalWriter<J, Fs> {
} }
} }
pub struct RawJournalReader<J: RawJournalAdapter, Fs: FSInterface> { pub struct RawJournalReader<J: RawJournalAdapter> {
tr: TrackedReader< tr: TrackedReader<<J as RawJournalAdapter>::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
<J as RawJournalAdapter>::Spec,
>,
txn_id: u64, txn_id: u64,
last_txn_id: u64, last_txn_id: u64,
last_txn_offset: u64, last_txn_offset: u64,
@ -636,14 +626,11 @@ impl JournalStats {
} }
} }
impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalReader<J, Fs> { impl<J: RawJournalAdapter> RawJournalReader<J> {
pub fn scroll( pub fn scroll(
file: SdssFile<<Fs as FSInterface>::File, <J as RawJournalAdapter>::Spec>, file: SdssFile<<J as RawJournalAdapter>::Spec>,
gs: &J::GlobalState, gs: &J::GlobalState,
) -> RuntimeResult<( ) -> RuntimeResult<(JournalInitializer, SdssFile<J::Spec>)> {
JournalInitializer,
SdssFile<<Fs as FSInterface>::File, J::Spec>,
)> {
let reader = TrackedReader::with_cursor( let reader = TrackedReader::with_cursor(
file, file,
<<J as RawJournalAdapter>::Spec as FileSpecV1>::SIZE as u64, <<J as RawJournalAdapter>::Spec as FileSpecV1>::SIZE as u64,
@ -660,16 +647,13 @@ impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalReader<J, Fs> {
// NB: the last txn offset is important because it indicates that the log is new // NB: the last txn offset is important because it indicates that the log is new
me.last_txn_offset, me.last_txn_offset,
); );
let file = me.tr.into_inner::<Fs::File>()?; let file = me.tr.into_inner();
return Ok((initializer, file)); return Ok((initializer, file));
} }
} }
} }
fn new( fn new(
reader: TrackedReader< reader: TrackedReader<<J as RawJournalAdapter>::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
<J as RawJournalAdapter>::Spec,
>,
txn_id: u64, txn_id: u64,
last_txn_id: u64, last_txn_id: u64,
last_txn_offset: u64, last_txn_offset: u64,
@ -692,7 +676,7 @@ impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalReader<J, Fs> {
} }
} }
impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalReader<J, Fs> { impl<J: RawJournalAdapter> RawJournalReader<J> {
fn _apply_next_event_and_stop(&mut self, gs: &J::GlobalState) -> RuntimeResult<bool> { fn _apply_next_event_and_stop(&mut self, gs: &J::GlobalState) -> RuntimeResult<bool> {
let txn_id = u128::from_le_bytes(self.tr.read_block()?); let txn_id = u128::from_le_bytes(self.tr.read_block()?);
let meta = u64::from_le_bytes(self.tr.read_block()?); let meta = u64::from_le_bytes(self.tr.read_block()?);
@ -715,7 +699,7 @@ impl<J: RawJournalAdapter, Fs: FSInterface> RawJournalReader<J, Fs> {
// now parse the actual event // now parse the actual event
let Self { tr: reader, .. } = self; let Self { tr: reader, .. } = self;
// we do not consider a parsed event a success signal; so we must actually apply it // we do not consider a parsed event a success signal; so we must actually apply it
match J::decode_apply::<Fs>(gs, meta, reader) { match J::decode_apply(gs, meta, reader) {
Ok(()) => { Ok(()) => {
jtrace_reader!(ServerEventAppliedSuccess); jtrace_reader!(ServerEventAppliedSuccess);
Self::__refresh_known_txn(self); Self::__refresh_known_txn(self);

@ -33,13 +33,7 @@ use {
error::StorageError, error::StorageError,
fractal::error::ErrorContext, fractal::error::ErrorContext,
storage::{ storage::{
common::{ common::sdss::sdss_r1::rw::TrackedReader,
interface::{
fs_test::VirtualFS,
fs_traits::{FSInterface, FileInterface},
},
sdss::sdss_r1::rw::TrackedReader,
},
v2::raw::{ v2::raw::{
journal::raw::{JournalReaderTraceEvent, JournalWriterTraceEvent}, journal::raw::{JournalReaderTraceEvent, JournalWriterTraceEvent},
spec::SystemDatabaseV1, spec::SystemDatabaseV1,
@ -75,22 +69,19 @@ impl SimpleDB {
fn data(&self) -> std::cell::Ref<'_, Vec<String>> { fn data(&self) -> std::cell::Ref<'_, Vec<String>> {
self.data.borrow() self.data.borrow()
} }
fn clear( fn clear(&mut self, log: &mut RawJournalWriter<SimpleDBJournal>) -> RuntimeResult<()> {
&mut self,
log: &mut RawJournalWriter<SimpleDBJournal, VirtualFS>,
) -> RuntimeResult<()> {
log.commit_event(DbEventClear)?; log.commit_event(DbEventClear)?;
self.data.get_mut().clear(); self.data.get_mut().clear();
Ok(()) Ok(())
} }
fn pop(&mut self, log: &mut RawJournalWriter<SimpleDBJournal, VirtualFS>) -> RuntimeResult<()> { fn pop(&mut self, log: &mut RawJournalWriter<SimpleDBJournal>) -> RuntimeResult<()> {
self.data.get_mut().pop().unwrap(); self.data.get_mut().pop().unwrap();
log.commit_event(DbEventPop)?; log.commit_event(DbEventPop)?;
Ok(()) Ok(())
} }
fn push( fn push(
&mut self, &mut self,
log: &mut RawJournalWriter<SimpleDBJournal, VirtualFS>, log: &mut RawJournalWriter<SimpleDBJournal>,
new: impl ToString, new: impl ToString,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
let new = new.to_string(); let new = new.to_string();
@ -155,9 +146,7 @@ impl RawJournalAdapter for SimpleDBJournal {
fn initialize(_: &JournalInitializer) -> Self { fn initialize(_: &JournalInitializer) -> Self {
Self Self
} }
fn enter_context<'a, Fs: FSInterface>( fn enter_context<'a>(_: &'a mut RawJournalWriter<Self>) -> Self::Context<'a> {
_: &'a mut RawJournalWriter<Self, Fs>,
) -> Self::Context<'a> {
() ()
} }
fn parse_event_meta(meta: u64) -> Option<Self::EventMeta> { fn parse_event_meta(meta: u64) -> Option<Self::EventMeta> {
@ -176,13 +165,10 @@ impl RawJournalAdapter for SimpleDBJournal {
) { ) {
event.write_buffered(buf, ctx) event.write_buffered(buf, ctx)
} }
fn decode_apply<'a, Fs: FSInterface>( fn decode_apply<'a>(
gs: &Self::GlobalState, gs: &Self::GlobalState,
meta: Self::EventMeta, meta: Self::EventMeta,
file: &mut TrackedReader< file: &mut TrackedReader<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
match meta { match meta {
EventMeta::NewKey => { EventMeta::NewKey => {
@ -212,7 +198,7 @@ fn journal_open_close() {
const JOURNAL_NAME: &str = "journal_open_close"; const JOURNAL_NAME: &str = "journal_open_close";
{ {
// new boot // new boot
let mut j = create_journal::<SimpleDBJournal, VirtualFS>(JOURNAL_NAME).unwrap(); let mut j = create_journal::<SimpleDBJournal>(JOURNAL_NAME).unwrap();
assert_eq!( assert_eq!(
super::obtain_trace(), super::obtain_trace(),
intovec![JournalWriterTraceEvent::Initialized] intovec![JournalWriterTraceEvent::Initialized]
@ -233,8 +219,7 @@ fn journal_open_close() {
} }
{ {
// second boot // second boot
let mut j = let mut j = open_journal::<SimpleDBJournal>(JOURNAL_NAME, &SimpleDB::new()).unwrap();
open_journal::<SimpleDBJournal, VirtualFS>(JOURNAL_NAME, &SimpleDB::new()).unwrap();
assert_eq!( assert_eq!(
super::obtain_trace(), super::obtain_trace(),
intovec![ intovec![
@ -273,8 +258,7 @@ fn journal_open_close() {
} }
{ {
// third boot // third boot
let mut j = let mut j = open_journal::<SimpleDBJournal>(JOURNAL_NAME, &SimpleDB::new()).unwrap();
open_journal::<SimpleDBJournal, VirtualFS>(JOURNAL_NAME, &SimpleDB::new()).unwrap();
assert_eq!( assert_eq!(
super::obtain_trace(), super::obtain_trace(),
intovec![ intovec![
@ -328,7 +312,7 @@ fn journal_with_server_single_event() {
{ {
let mut db = SimpleDB::new(); let mut db = SimpleDB::new();
// new boot // new boot
let mut j = create_journal::<SimpleDBJournal, VirtualFS>(JOURNAL_NAME).unwrap(); let mut j = create_journal::<SimpleDBJournal>(JOURNAL_NAME).unwrap();
db.push(&mut j, "hello world").unwrap(); db.push(&mut j, "hello world").unwrap();
RawJournalWriter::close_driver(&mut j).unwrap(); RawJournalWriter::close_driver(&mut j).unwrap();
assert_eq!( assert_eq!(
@ -352,7 +336,7 @@ fn journal_with_server_single_event() {
{ {
let db = SimpleDB::new(); let db = SimpleDB::new();
// second boot // second boot
let mut j = open_journal::<SimpleDBJournal, VirtualFS>(JOURNAL_NAME, &db) let mut j = open_journal::<SimpleDBJournal>(JOURNAL_NAME, &db)
.set_dmsg_fn(|| format!("{:?}", super::obtain_trace())) .set_dmsg_fn(|| format!("{:?}", super::obtain_trace()))
.unwrap(); .unwrap();
assert_eq!(db.data().len(), 1); assert_eq!(db.data().len(), 1);
@ -401,7 +385,7 @@ fn journal_with_server_single_event() {
{ {
// third boot // third boot
let db = SimpleDB::new(); let db = SimpleDB::new();
let mut j = open_journal::<SimpleDBJournal, VirtualFS>(JOURNAL_NAME, &db).unwrap(); let mut j = open_journal::<SimpleDBJournal>(JOURNAL_NAME, &db).unwrap();
assert_eq!(db.data().len(), 1); assert_eq!(db.data().len(), 1);
assert_eq!(db.data()[0], "hello world"); assert_eq!(db.data()[0], "hello world");
assert_eq!( assert_eq!(
@ -460,7 +444,7 @@ fn journal_with_server_single_event() {
#[test] #[test]
fn multi_boot() { fn multi_boot() {
{ {
let mut j = create_journal::<SimpleDBJournal, VirtualFS>("multiboot").unwrap(); let mut j = create_journal::<SimpleDBJournal>("multiboot").unwrap();
let mut db = SimpleDB::new(); let mut db = SimpleDB::new();
db.push(&mut j, "key_a").unwrap(); db.push(&mut j, "key_a").unwrap();
db.push(&mut j, "key_b").unwrap(); db.push(&mut j, "key_b").unwrap();
@ -469,7 +453,7 @@ fn multi_boot() {
} }
{ {
let mut db = SimpleDB::new(); let mut db = SimpleDB::new();
let mut j = open_journal::<SimpleDBJournal, VirtualFS>("multiboot", &db).unwrap(); let mut j = open_journal::<SimpleDBJournal>("multiboot", &db).unwrap();
assert_eq!(db.data().as_ref(), vec!["key_a".to_string()]); assert_eq!(db.data().as_ref(), vec!["key_a".to_string()]);
db.clear(&mut j).unwrap(); db.clear(&mut j).unwrap();
db.push(&mut j, "myfinkey").unwrap(); db.push(&mut j, "myfinkey").unwrap();
@ -477,7 +461,7 @@ fn multi_boot() {
} }
{ {
let db = SimpleDB::new(); let db = SimpleDB::new();
let mut j = open_journal::<SimpleDBJournal, VirtualFS>("multiboot", &db).unwrap(); let mut j = open_journal::<SimpleDBJournal>("multiboot", &db).unwrap();
assert_eq!(db.data().as_ref(), vec!["myfinkey".to_string()]); assert_eq!(db.data().as_ref(), vec!["myfinkey".to_string()]);
RawJournalWriter::close_driver(&mut j).unwrap(); RawJournalWriter::close_driver(&mut j).unwrap();
} }

@ -39,10 +39,7 @@ use {
mem::unsafe_apis, mem::unsafe_apis,
storage::{ storage::{
common::{ common::{
interface::{ interface::fs::File,
fs_test::VirtualFS,
fs_traits::{FSInterface, FileInterface},
},
sdss::sdss_r1::rw::{TrackedReaderContext, TrackedWriter}, sdss::sdss_r1::rw::{TrackedReaderContext, TrackedWriter},
}, },
v2::raw::{ v2::raw::{
@ -155,22 +152,18 @@ impl TestDB {
fn _ref(&self) -> Ref<Vec<String>> { fn _ref(&self) -> Ref<Vec<String>> {
self.data.borrow() self.data.borrow()
} }
fn push( fn push(&self, log: &mut EventLogDriver<TestDBAdapter>, key: &str) -> RuntimeResult<()> {
&self,
log: &mut EventLogDriver<TestDBAdapter, VirtualFS>,
key: &str,
) -> RuntimeResult<()> {
log.commit_event(EventPush(key))?; log.commit_event(EventPush(key))?;
self._mut().push(key.into()); self._mut().push(key.into());
Ok(()) Ok(())
} }
fn pop(&self, log: &mut EventLogDriver<TestDBAdapter, VirtualFS>) -> RuntimeResult<()> { fn pop(&self, log: &mut EventLogDriver<TestDBAdapter>) -> RuntimeResult<()> {
assert!(!self._ref().is_empty()); assert!(!self._ref().is_empty());
log.commit_event(EventPop)?; log.commit_event(EventPop)?;
self._mut().pop().unwrap(); self._mut().pop().unwrap();
Ok(()) Ok(())
} }
fn clear(&self, log: &mut EventLogDriver<TestDBAdapter, VirtualFS>) -> RuntimeResult<()> { fn clear(&self, log: &mut EventLogDriver<TestDBAdapter>) -> RuntimeResult<()> {
log.commit_event(EventClear)?; log.commit_event(EventClear)?;
self._mut().clear(); self._mut().clear();
Ok(()) Ok(())
@ -179,7 +172,7 @@ impl TestDB {
fn open_log() -> ( fn open_log() -> (
TestDB, TestDB,
super::raw::RawJournalWriter<EventLogAdapter<TestDBAdapter>, VirtualFS>, super::raw::RawJournalWriter<EventLogAdapter<TestDBAdapter>>,
) { ) {
let db = TestDB::default(); let db = TestDB::default();
let log = open_journal("jrnl", &db).unwrap(); let log = open_journal("jrnl", &db).unwrap();
@ -265,11 +258,7 @@ impl BatchDB {
self.inner.borrow() self.inner.borrow()
} }
/// As soon as two changes occur, we sync to disk /// As soon as two changes occur, we sync to disk
fn push( fn push(&self, driver: &mut BatchDriver<BatchDBAdapter>, key: &str) -> RuntimeResult<()> {
&self,
driver: &mut BatchDriver<BatchDBAdapter, VirtualFS>,
key: &str,
) -> RuntimeResult<()> {
let mut me = self._mut(); let mut me = self._mut();
me.data.push(key.into()); me.data.push(key.into());
let changed = me.data.len() - me.last_flushed_at; let changed = me.data.len() - me.last_flushed_at;
@ -293,10 +282,10 @@ impl<'a> RawJournalAdapterEvent<BatchAdapter<BatchDBAdapter>> for BatchDBFlush<'
fn md(&self) -> u64 { fn md(&self) -> u64 {
BatchType::GenericBatch.dscr_u64() BatchType::GenericBatch.dscr_u64()
} }
fn write_direct<Fs: FSInterface>( fn write_direct(
self, self,
f: &mut TrackedWriter< f: &mut TrackedWriter<
Fs::File, File,
<BatchAdapter<BatchDBAdapter> as super::raw::RawJournalAdapter>::Spec, <BatchAdapter<BatchDBAdapter> as super::raw::RawJournalAdapter>::Spec,
>, >,
ctx: Rc<RefCell<BatchContext>>, ctx: Rc<RefCell<BatchContext>>,
@ -348,23 +337,17 @@ impl BatchAdapterSpec for BatchDBAdapter {
fn is_early_exit(ev: &Self::EventType) -> bool { fn is_early_exit(ev: &Self::EventType) -> bool {
BatchEventType::EarlyExit.eq(ev) BatchEventType::EarlyExit.eq(ev)
} }
fn decode_batch_metadata<Fs: FSInterface>( fn decode_batch_metadata(
_: &Self::GlobalState, _: &Self::GlobalState,
_: &mut TrackedReaderContext< _: &mut TrackedReaderContext<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
_: Self::BatchType, _: Self::BatchType,
) -> RuntimeResult<Self::BatchMetadata> { ) -> RuntimeResult<Self::BatchMetadata> {
Ok(()) Ok(())
} }
fn update_state_for_new_event<Fs: FSInterface>( fn update_state_for_new_event(
_: &Self::GlobalState, _: &Self::GlobalState,
bs: &mut Self::BatchState, bs: &mut Self::BatchState,
f: &mut TrackedReaderContext< f: &mut TrackedReaderContext<Self::Spec>,
<<Fs as FSInterface>::File as FileInterface>::BufReader,
Self::Spec,
>,
_: &Self::BatchMetadata, _: &Self::BatchMetadata,
event_type: Self::EventType, event_type: Self::EventType,
) -> RuntimeResult<()> { ) -> RuntimeResult<()> {
@ -421,8 +404,7 @@ fn batch_simple() {
} }
{ {
let db = BatchDB::new(); let db = BatchDB::new();
let mut batch_drv = let mut batch_drv = BatchAdapter::<BatchDBAdapter>::open("mybatch", &db).unwrap();
BatchAdapter::<BatchDBAdapter>::open::<VirtualFS>("mybatch", &db).unwrap();
assert_eq!( assert_eq!(
db._ref().data, db._ref().data,
["key1", "key2", "key3", "key4", "key5", "key6"] ["key1", "key2", "key3", "key4", "key5", "key6"]

Loading…
Cancel
Save