@@ -103,6 +103,7 @@ pub struct CortexaDBStore {
103103 sync_thread : Option < JoinHandle < ( ) > > ,
104104 checkpoint_policy : CheckpointPolicy ,
105105 checkpoint_path : std:: path:: PathBuf ,
106+ hnsw_path : std:: path:: PathBuf ,
106107 checkpoint_control : Arc < ( Mutex < CheckpointRuntime > , Condvar ) > ,
107108 checkpoint_thread : Option < JoinHandle < ( ) > > ,
108109 capacity_policy : CapacityPolicy ,
@@ -243,14 +244,38 @@ impl CortexaDBStore {
243244 checkpoint_path : std:: path:: PathBuf ,
244245 index_mode : crate :: index:: hnsw:: IndexMode ,
245246 ) -> Result < Self > {
247+ let hnsw_path = checkpoint_path. with_extension ( "hnsw" ) ;
248+
246249 let hnsw_config = match index_mode {
247250 crate :: index:: hnsw:: IndexMode :: Exact => None ,
248251 crate :: index:: hnsw:: IndexMode :: Hnsw ( config) => Some ( config) ,
249252 } ;
253+
254+ let loaded_hnsw = if let Some ( config) = hnsw_config. as_ref ( ) {
255+ match crate :: index:: VectorIndex :: load_hnsw ( & hnsw_path, vector_dimension, config. clone ( ) )
256+ {
257+ Ok ( Some ( backend) ) => {
258+ eprintln ! ( "Loaded HNSW index from disk (fast recovery)" ) ;
259+ Some ( backend)
260+ }
261+ Ok ( None ) => {
262+ eprintln ! ( "No HNSW index file found, building fresh index" ) ;
263+ None
264+ }
265+ Err ( e) => {
266+ eprintln ! ( "Failed to load HNSW index, rebuilding: {}" , e) ;
267+ None
268+ }
269+ }
270+ } else {
271+ None
272+ } ;
273+
250274 let indexes = Self :: build_vector_index (
251275 engine. get_state_machine ( ) ,
252276 vector_dimension,
253277 hnsw_config. as_ref ( ) ,
278+ loaded_hnsw,
254279 ) ?;
255280 Self :: assert_vector_index_in_sync_inner ( engine. get_state_machine ( ) , & indexes) ?;
256281
@@ -297,6 +322,7 @@ impl CortexaDBStore {
297322 sync_thread,
298323 checkpoint_policy,
299324 checkpoint_path,
325+ hnsw_path,
300326 checkpoint_control,
301327 checkpoint_thread,
302328 capacity_policy,
@@ -320,6 +346,10 @@ impl CortexaDBStore {
320346 let last_applied_id = writer. engine . last_applied_id ( ) . 0 ;
321347 save_checkpoint ( & self . checkpoint_path , snapshot. state_machine ( ) , last_applied_id) ?;
322348
349+ if let Err ( e) = snapshot. indexes . vector_index ( ) . save_hnsw ( & self . hnsw_path ) {
350+ eprintln ! ( "Warning: Failed to save HNSW index: {}" , e) ;
351+ }
352+
323353 // Truncate WAL prefix — only keep entries written after the checkpoint.
324354 let wal_path = writer. engine . wal_path ( ) . to_path_buf ( ) ;
325355 WriteAheadLog :: truncate_prefix ( & wal_path, CommandId ( last_applied_id) ) ?;
@@ -515,6 +545,7 @@ impl CortexaDBStore {
515545 writer. engine . get_state_machine ( ) ,
516546 writer. indexes . vector . dimension ( ) ,
517547 None ,
548+ None ,
518549 ) ?;
519550
520551 let indexed = writer. indexes . vector . len ( ) ;
@@ -786,12 +817,25 @@ impl CortexaDBStore {
786817 state_machine : & StateMachine ,
787818 vector_dimension : usize ,
788819 hnsw_config : Option < & crate :: index:: hnsw:: HnswConfig > ,
820+ loaded_hnsw : Option < crate :: index:: hnsw:: HnswBackend > ,
789821 ) -> Result < IndexLayer > {
822+ let has_loaded_hnsw = loaded_hnsw. is_some ( ) ;
790823 let indexes = match hnsw_config {
791- Some ( config) => IndexLayer :: new_with_hnsw ( vector_dimension, config. clone ( ) ) ,
824+ Some ( config) => {
825+ if let Some ( loaded) = loaded_hnsw {
826+ IndexLayer :: new_with_loaded_hnsw ( vector_dimension, config. clone ( ) , Some ( loaded) )
827+ } else {
828+ IndexLayer :: new_with_hnsw ( vector_dimension, config. clone ( ) )
829+ }
830+ }
792831 None => IndexLayer :: new ( vector_dimension) ,
793832 } ;
794833 let mut indexes = indexes;
834+
835+ if has_loaded_hnsw {
836+ return Ok ( indexes) ;
837+ }
838+
795839 for entry in state_machine. all_memories ( ) {
796840 if let Some ( embedding) = entry. embedding . clone ( ) {
797841 indexes. vector_index_mut ( ) . index_in_namespace (
@@ -855,6 +899,12 @@ impl Drop for CortexaDBStore {
855899 if self . checkpoint_policy != CheckpointPolicy :: Disabled {
856900 let _ = self . checkpoint_now ( ) ;
857901 }
902+
903+ // Always save HNSW index on drop if it exists (automatic persistence)
904+ let snapshot = self . snapshot . load_full ( ) ;
905+ if let Err ( e) = snapshot. indexes . vector_index ( ) . save_hnsw ( & self . hnsw_path ) {
906+ eprintln ! ( "Warning: Failed to save HNSW on drop: {}" , e) ;
907+ }
858908 }
859909}
860910
0 commit comments