use std::{
path::Path,
sync::{Arc, RwLock},
};
use crate::{
engine::GraphCtx,
graph::{LogicalGraph, LogicalSnapshot},
gremlin::traversal::{ReadTraversal, WriteTraversal},
schema::{GraphOptions, Schema, SchemaManagement},
store::{traits::GraphStore, RocksOptions, RocksStorage},
types::{BatchScenario, StoreError},
};
pub struct Graph {
store: Arc<RocksStorage>,
schema: Arc<RwLock<Schema>>,
}
impl Graph {
pub fn open(path: impl AsRef<Path>) -> Result<Self, StoreError> {
Self::open_with_rocksdb_options(path, GraphOptions::default(), RocksOptions::default())
}
pub fn open_with_options(path: impl AsRef<Path>, options: GraphOptions) -> Result<Self, StoreError> {
Self::open_with_rocksdb_options(path, options, RocksOptions::default())
}
pub fn open_with_rocksdb_options(
path: impl AsRef<Path>,
schema_options: GraphOptions,
rocksdb_options: RocksOptions,
) -> Result<Self, StoreError> {
let store = Arc::new(RocksStorage::open(path, &rocksdb_options)?);
store.recover_bulk_load_crash()?;
let schema = store.load_schema(schema_options)?;
Ok(Self { store, schema: Arc::new(RwLock::new(schema)) })
}
pub fn open_management(&self) -> SchemaManagement {
SchemaManagement::new(Arc::clone(&self.store), Arc::clone(&self.schema))
}
#[cfg(test)]
pub(crate) fn schema(&self) -> Arc<RwLock<Schema>> {
Arc::clone(&self.schema)
}
pub fn edge_label_names(&self) -> Vec<String> {
self.schema.read().unwrap().edge_labels.iter().map(|(_, n)| n.to_string()).collect()
}
pub fn read(&self) -> ReadSession {
ReadSession { ctx: LogicalSnapshot::new(self.store.snapshot(), Arc::clone(&self.schema)) }
}
pub fn begin(&self) -> TxSession {
TxSession { ctx: LogicalGraph::new(self.store.begin(), Arc::clone(&self.schema)), committed: false }
}
pub fn close(self) -> Result<(), StoreError> {
match Arc::try_unwrap(self.store) {
Ok(_store) => Ok(()),
Err(arc) => {
drop(arc);
Ok(())
}
}
}
}
impl Clone for Graph {
fn clone(&self) -> Self {
Self { store: Arc::clone(&self.store), schema: Arc::clone(&self.schema) }
}
}
#[cfg(feature = "rocksdb-stats")]
impl Graph {
pub fn statistics(&self) -> Option<String> {
self.store.statistics()
}
}
pub struct ReadSession {
ctx: LogicalSnapshot<RocksStorage>,
}
impl ReadSession {
pub fn g(&mut self) -> ReadTraversal<'_> {
self.ctx.clear_caches();
ReadTraversal::new(&mut self.ctx as &mut dyn GraphCtx)
}
pub fn clear_caches(&mut self) {
self.ctx.clear_caches();
}
pub fn set_batch_size(&mut self, scenario: BatchScenario, size: u32) {
match scenario {
BatchScenario::ScanVertices => self.ctx.scan_config.scan_vertices_batch_size = size,
BatchScenario::ScanEdges => self.ctx.scan_config.scan_edges_batch_size = size,
BatchScenario::GetAdjacentEdges => self.ctx.scan_config.get_adjacent_edges_batch_size = size,
}
}
}
pub struct TxSession {
ctx: LogicalGraph<RocksStorage>,
committed: bool,
}
impl TxSession {
pub fn g(&mut self) -> WriteTraversal<'_> {
WriteTraversal::new(&mut self.ctx as &mut dyn GraphCtx)
}
pub fn commit(mut self) -> Result<(), StoreError> {
self.committed = true;
self.ctx.commit()
}
pub fn rollback(mut self) {
self.committed = true;
self.ctx.abort();
}
pub fn set_batch_size(&mut self, scenario: BatchScenario, size: u32) {
match scenario {
BatchScenario::ScanVertices => self.ctx.scan_config.scan_vertices_batch_size = size,
BatchScenario::ScanEdges => self.ctx.scan_config.scan_edges_batch_size = size,
BatchScenario::GetAdjacentEdges => self.ctx.scan_config.get_adjacent_edges_batch_size = size,
}
}
}
impl Drop for TxSession {
fn drop(&mut self) {
if !self.committed {
self.ctx.abort();
}
}
}