Index disk serialization without DB
This commit is contained in:
parent
a5061dfc92
commit
1a8bf91628
4 changed files with 49 additions and 21 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -14,6 +14,7 @@ TestConfig.toml
|
|||
*.sqlite
|
||||
**/*.sqlite-shm
|
||||
**/*.sqlite-wal
|
||||
collection.index
|
||||
polaris.log
|
||||
polaris.ndb
|
||||
polaris.pid
|
||||
|
|
|
@ -183,7 +183,7 @@ impl App {
|
|||
let auth_secret = settings_manager.get_auth_secret().await?;
|
||||
let ddns_manager = ddns::Manager::new(db.clone());
|
||||
let user_manager = user::Manager::new(db.clone(), auth_secret);
|
||||
let index_manager = index::Manager::new(db.clone()).await;
|
||||
let index_manager = index::Manager::new(&paths.data_dir_path).await?;
|
||||
let scanner = scanner::Scanner::new(
|
||||
index_manager.clone(),
|
||||
settings_manager.clone(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
|||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::app::{scanner, Error};
|
||||
use crate::db::DB;
|
||||
|
||||
mod browser;
|
||||
mod collection;
|
||||
|
@ -24,20 +23,28 @@ use storage::{store_song, AlbumKey, ArtistKey, GenreKey, InternPath, SongKey};
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Manager {
|
||||
db: DB,
|
||||
index: Arc<RwLock<Index>>, // Not a tokio RwLock as we want to do CPU-bound work with Index
|
||||
index_file_path: PathBuf,
|
||||
index: Arc<RwLock<Index>>, // Not a tokio RwLock as we want to do CPU-bound work with Index and lock this inside spawn_blocking()
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub async fn new(db: DB) -> Self {
|
||||
pub async fn new(directory: &Path) -> Result<Self, Error> {
|
||||
tokio::fs::create_dir_all(directory)
|
||||
.await
|
||||
.map_err(|e| Error::Io(directory.to_owned(), e))?;
|
||||
|
||||
let mut index_manager = Self {
|
||||
db,
|
||||
index_file_path: directory.join("collection.index"),
|
||||
index: Arc::default(),
|
||||
};
|
||||
|
||||
if let Err(e) = index_manager.try_restore_index().await {
|
||||
error!("Failed to restore index: {}", e);
|
||||
} else {
|
||||
info!("Restored collection index from disk");
|
||||
}
|
||||
index_manager
|
||||
|
||||
Ok(index_manager)
|
||||
}
|
||||
|
||||
pub async fn replace_index(&mut self, new_index: Index) {
|
||||
|
@ -57,22 +64,26 @@ impl Manager {
|
|||
Ok(s) => s,
|
||||
Err(_) => return Err(Error::IndexSerializationError),
|
||||
};
|
||||
sqlx::query!("UPDATE collection_index SET content = $1", serialized)
|
||||
.execute(self.db.connect().await?.as_mut())
|
||||
.await?;
|
||||
tokio::fs::write(&self.index_file_path, &serialized[..])
|
||||
.await
|
||||
.map_err(|e| Error::Io(self.index_file_path.clone(), e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_restore_index(&mut self) -> Result<bool, Error> {
|
||||
let serialized = sqlx::query_scalar!("SELECT content FROM collection_index")
|
||||
.fetch_one(self.db.connect().await?.as_mut())
|
||||
.await?;
|
||||
|
||||
let Some(serialized) = serialized else {
|
||||
info!("Database did not contain a collection to restore");
|
||||
return Ok(false);
|
||||
match tokio::fs::try_exists(&self.index_file_path).await {
|
||||
Ok(false) => {
|
||||
info!("No existing index to restore");
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true) => (),
|
||||
Err(e) => return Err(Error::Io(self.index_file_path.clone(), e)),
|
||||
};
|
||||
|
||||
let serialized = tokio::fs::read(&self.index_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::Io(self.index_file_path.clone(), e))?;
|
||||
|
||||
let index = match bitcode::deserialize(&serialized[..]) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(Error::IndexDeserializationError),
|
||||
|
@ -354,3 +365,20 @@ impl Default for Builder {
|
|||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
app::{index, test},
|
||||
test_name,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn can_persist_index() {
|
||||
let mut ctx = test::ContextBuilder::new(test_name!()).build().await;
|
||||
assert_eq!(ctx.index_manager.try_restore_index().await.unwrap(), false);
|
||||
let index = index::Builder::new().build();
|
||||
ctx.index_manager.persist_index(&index).await.unwrap();
|
||||
assert_eq!(ctx.index_manager.try_restore_index().await.unwrap(), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,10 +52,9 @@ impl ContextBuilder {
|
|||
}
|
||||
pub async fn build(self) -> Context {
|
||||
let db_path = self.test_directory.join("db.sqlite");
|
||||
let ndb_path = self.test_directory.join("polaris.ndb");
|
||||
|
||||
let db = DB::new(&db_path).await.unwrap();
|
||||
let ndb_manager = ndb::Manager::new(&ndb_path).unwrap();
|
||||
let ndb_manager = ndb::Manager::new(&self.test_directory).unwrap();
|
||||
let settings_manager = settings::Manager::new(db.clone());
|
||||
let auth_secret = settings_manager.get_auth_secret().await.unwrap();
|
||||
let user_manager = user::Manager::new(db.clone(), auth_secret);
|
||||
|
@ -67,7 +66,7 @@ impl ContextBuilder {
|
|||
vfs_manager.clone(),
|
||||
ddns_manager.clone(),
|
||||
);
|
||||
let index_manager = index::Manager::new(db.clone()).await;
|
||||
let index_manager = index::Manager::new(&self.test_directory).await.unwrap();
|
||||
let scanner = scanner::Scanner::new(
|
||||
index_manager.clone(),
|
||||
settings_manager.clone(),
|
||||
|
|
Loading…
Add table
Reference in a new issue