use selene_persist::{SnapshotBuilder, SnapshotConfig, SnapshotFinalizeOutcome};
use crate::{GraphResult, SharedGraph};
impl SharedGraph {
pub fn write_snapshot(&self, config: SnapshotConfig) -> GraphResult<SnapshotFinalizeOutcome> {
let mut builder = SnapshotBuilder::new(config);
for provider in self.index_providers() {
let provider_tag = provider.provider_tag();
for sub_tag in provider.declared_sub_tags() {
let bytes = provider.write_section(*sub_tag)?;
builder.add_section(provider_tag.0, sub_tag.0, bytes)?;
}
}
builder.finalize().map_err(Into::into)
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use selene_core::{Change, GraphId};
use selene_persist::{SectionCompression, SnapshotConfig, SnapshotReader, snapshot_path};
use crate::SharedGraph;
use crate::index_provider::{IndexProvider, ProviderError, ProviderTag, SubTag};
const TEST_PROVIDER: [u8; 4] = *b"TST1";
const TEST_SUB: [u8; 4] = *b"BODY";
const TEST_SUB_TAGS: &[SubTag] = &[SubTag(TEST_SUB)];
struct SnapshotOnlyProvider;
impl IndexProvider for SnapshotOnlyProvider {
fn provider_tag(&self) -> ProviderTag {
ProviderTag(TEST_PROVIDER)
}
fn read_section(&self, _sub_tag: SubTag, _bytes: &[u8]) -> Result<(), ProviderError> {
Ok(())
}
fn write_section(&self, sub_tag: SubTag) -> Result<Vec<u8>, ProviderError> {
assert_eq!(sub_tag, SubTag(TEST_SUB));
Ok(b"provider-body".to_vec())
}
fn on_change(&self, _change: &Change) -> Result<(), ProviderError> {
Ok(())
}
fn declared_sub_tags(&self) -> &[SubTag] {
TEST_SUB_TAGS
}
}
#[test]
fn write_snapshot_includes_registered_provider_sections() {
let dir = temp_dir("shared-snapshot");
let provider = Arc::new(SnapshotOnlyProvider);
let shared = SharedGraph::builder(GraphId::new(82_001))
.with_provider(provider as Arc<dyn IndexProvider>)
.build()
.unwrap();
let outcome = shared
.write_snapshot(SnapshotConfig {
dir: dir.clone(),
sequence: 7,
compression: SectionCompression::None,
fsync: false,
})
.unwrap();
assert_eq!(outcome.snapshot_seq, 7);
assert!(outcome.section_count > 1);
let mut reader = SnapshotReader::open(&snapshot_path(&dir, 7)).unwrap();
assert_eq!(
reader.read_section(TEST_PROVIDER, TEST_SUB).unwrap(),
b"provider-body"
);
}
fn temp_dir(name: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!(
"selene-graph-{name}-{}-{nanos}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir(&dir).unwrap();
dir
}
}