index_store: extract trait

This is a step towards making the index storage pluggable. The
interface will probably change a bit soon, but let's start with
functions that match the current implementation.

I called the current implementation the `DefaultIndexStore`. Calling
it `SimpleIndexStore` (like `SimpleOpStore` and `SimpleOpHeadsStore`)
didn't seem accurate.
This commit is contained in:
Martin von Zweigbergk 2023-02-26 11:52:52 -08:00 committed by Martin von Zweigbergk
parent 904e9c5520
commit 3e4e0dc916
3 changed files with 76 additions and 61 deletions

View file

@ -13,6 +13,7 @@
// limitations under the License.
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
@ -38,64 +39,33 @@ pub enum IndexWriteError {
Other(String),
}
pub struct IndexStore {
dir: PathBuf,
}
pub trait IndexStore: Send + Sync + Debug {
fn get_index_at_op(&self, op: &Operation, store: &Arc<Store>) -> Arc<ReadonlyIndex>;
impl IndexStore {
pub fn init(dir: &Path) -> Self {
std::fs::create_dir(dir.join("operations")).unwrap();
IndexStore {
dir: dir.to_owned(),
}
}
pub fn load(dir: &Path) -> IndexStore {
IndexStore {
dir: dir.to_owned(),
}
}
pub fn get_index_at_op(&self, op: &Operation, store: &Arc<Store>) -> Arc<ReadonlyIndex> {
let op_id_hex = op.id().hex();
let op_id_file = self.dir.join("operations").join(op_id_hex);
if op_id_file.exists() {
match self.load_index_at_operation(
store.commit_id_length(),
store.change_id_length(),
op.id(),
) {
Err(IndexLoadError::IndexCorrupt(_)) => {
// If the index was corrupt (maybe it was written in a different format),
// we just reindex.
// TODO: Move this message to a callback or something.
println!("The index was corrupt (maybe the format has changed). Reindexing...");
std::fs::remove_dir_all(self.dir.join("operations")).unwrap();
std::fs::create_dir(self.dir.join("operations")).unwrap();
self.index_at_operation(store, op).unwrap()
}
result => result.unwrap(),
}
} else {
self.index_at_operation(store, op).unwrap()
}
}
pub fn write_index(
fn write_index(
&self,
index: MutableIndex,
op_id: &OperationId,
) -> Result<Arc<ReadonlyIndex>, IndexWriteError> {
let index = index.save_in(self.dir.clone()).map_err(|err| {
IndexWriteError::Other(format!("Failed to write commit index file: {err:?}"))
})?;
self.associate_file_with_operation(&index, op_id)
.map_err(|err| {
IndexWriteError::Other(format!(
"Failed to associate commit index file with a operation {op_id:?}: {err:?}"
))
})?;
Ok(index)
) -> Result<Arc<ReadonlyIndex>, IndexWriteError>;
}
#[derive(Debug)]
pub struct DefaultIndexStore {
dir: PathBuf,
}
impl DefaultIndexStore {
pub fn init(dir: &Path) -> Self {
std::fs::create_dir(dir.join("operations")).unwrap();
DefaultIndexStore {
dir: dir.to_owned(),
}
}
pub fn load(dir: &Path) -> DefaultIndexStore {
DefaultIndexStore {
dir: dir.to_owned(),
}
}
fn load_index_at_operation(
@ -196,6 +166,50 @@ impl IndexStore {
}
}
impl IndexStore for DefaultIndexStore {
fn get_index_at_op(&self, op: &Operation, store: &Arc<Store>) -> Arc<ReadonlyIndex> {
let op_id_hex = op.id().hex();
let op_id_file = self.dir.join("operations").join(op_id_hex);
if op_id_file.exists() {
match self.load_index_at_operation(
store.commit_id_length(),
store.change_id_length(),
op.id(),
) {
Err(IndexLoadError::IndexCorrupt(_)) => {
// If the index was corrupt (maybe it was written in a different format),
// we just reindex.
// TODO: Move this message to a callback or something.
println!("The index was corrupt (maybe the format has changed). Reindexing...");
std::fs::remove_dir_all(self.dir.join("operations")).unwrap();
std::fs::create_dir(self.dir.join("operations")).unwrap();
self.index_at_operation(store, op).unwrap()
}
result => result.unwrap(),
}
} else {
self.index_at_operation(store, op).unwrap()
}
}
fn write_index(
&self,
index: MutableIndex,
op_id: &OperationId,
) -> Result<Arc<ReadonlyIndex>, IndexWriteError> {
let index = index.save_in(self.dir.clone()).map_err(|err| {
IndexWriteError::Other(format!("Failed to write commit index file: {err:?}"))
})?;
self.associate_file_with_operation(&index, op_id)
.map_err(|err| {
IndexWriteError::Other(format!(
"Failed to associate commit index file with a operation {op_id:?}: {err:?}"
))
})?;
Ok(index)
}
}
// Returns the ancestors of heads with parents and predecessors come before the
// commit itself
fn topo_order_earlier_first(

View file

@ -32,7 +32,7 @@ use crate::git_backend::GitBackend;
use crate::index::{
HexPrefix, Index, IndexEntry, IndexPosition, MutableIndex, PrefixResolution, ReadonlyIndex,
};
use crate::index_store::IndexStore;
use crate::index_store::{DefaultIndexStore, IndexStore};
use crate::local_backend::LocalBackend;
use crate::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};
use crate::op_store::{BranchTarget, OpStore, OperationId, RefTarget, WorkspaceId};
@ -80,7 +80,7 @@ pub struct ReadonlyRepo {
op_heads_store: Arc<dyn OpHeadsStore>,
operation: Operation,
settings: RepoSettings,
index_store: Arc<IndexStore>,
index_store: Arc<DefaultIndexStore>,
index: OnceCell<Arc<ReadonlyIndex>>,
// TODO: This should eventually become part of the index and not be stored fully in memory.
change_id_index: OnceCell<ChangeIdIndex>,
@ -158,7 +158,7 @@ impl ReadonlyRepo {
let index_path = repo_path.join("index");
fs::create_dir(&index_path).context(&index_path)?;
let index_store = Arc::new(IndexStore::init(&index_path));
let index_store = Arc::new(DefaultIndexStore::init(&index_path));
let view = View::new(root_view);
Ok(Arc::new(ReadonlyRepo {
@ -224,7 +224,7 @@ impl ReadonlyRepo {
&self.op_heads_store
}
pub fn index_store(&self) -> &Arc<IndexStore> {
pub fn index_store(&self) -> &Arc<DefaultIndexStore> {
&self.index_store
}
@ -457,7 +457,7 @@ pub struct RepoLoader {
store: Arc<Store>,
op_store: Arc<dyn OpStore>,
op_heads_store: Arc<dyn OpHeadsStore>,
index_store: Arc<IndexStore>,
index_store: Arc<DefaultIndexStore>,
}
impl RepoLoader {
@ -471,7 +471,7 @@ impl RepoLoader {
let op_store = Arc::from(store_factories.load_op_store(&repo_path.join("op_store"))?);
let op_heads_store =
Arc::from(store_factories.load_op_heads_store(&repo_path.join("op_heads"))?);
let index_store = Arc::new(IndexStore::load(&repo_path.join("index")));
let index_store = Arc::new(DefaultIndexStore::load(&repo_path.join("index")));
Ok(Self {
repo_path: repo_path.to_path_buf(),
repo_settings,
@ -490,7 +490,7 @@ impl RepoLoader {
&self.store
}
pub fn index_store(&self) -> &Arc<IndexStore> {
pub fn index_store(&self) -> &Arc<DefaultIndexStore> {
&self.index_store
}

View file

@ -17,6 +17,7 @@ use std::sync::Arc;
use crate::backend::Timestamp;
use crate::dag_walk::closest_common_node;
use crate::index::ReadonlyIndex;
use crate::index_store::IndexStore;
use crate::op_store;
use crate::op_store::OperationMetadata;
use crate::operation::Operation;