@@ -42,6 +42,7 @@ import {
4242 type EmbeddingProviderId ,
4343 type EmbeddingProviderRuntime ,
4444} from "./embeddings.js" ;
45+ import { runMemoryAtomicReindex } from "./manager-atomic-reindex.js" ;
4546import { openMemoryDatabaseAtPath } from "./manager-db.js" ;
4647import {
4748 applyMemoryFallbackProviderState ,
@@ -313,38 +314,6 @@ export abstract class MemoryManagerSyncOps {
313314 }
314315 }
315316
316- private async swapIndexFiles ( targetPath : string , tempPath : string ) : Promise < void > {
317- const backupPath = `${ targetPath } .backup-${ randomUUID ( ) } ` ;
318- await this . moveIndexFiles ( targetPath , backupPath ) ;
319- try {
320- await this . moveIndexFiles ( tempPath , targetPath ) ;
321- } catch ( err ) {
322- await this . moveIndexFiles ( backupPath , targetPath ) ;
323- throw err ;
324- }
325- await this . removeIndexFiles ( backupPath ) ;
326- }
327-
328- private async moveIndexFiles ( sourceBase : string , targetBase : string ) : Promise < void > {
329- const suffixes = [ "" , "-wal" , "-shm" ] ;
330- for ( const suffix of suffixes ) {
331- const source = `${ sourceBase } ${ suffix } ` ;
332- const target = `${ targetBase } ${ suffix } ` ;
333- try {
334- await fs . rename ( source , target ) ;
335- } catch ( err ) {
336- if ( ( err as NodeJS . ErrnoException ) . code !== "ENOENT" ) {
337- throw err ;
338- }
339- }
340- }
341- }
342-
343- private async removeIndexFiles ( basePath : string ) : Promise < void > {
344- const suffixes = [ "" , "-wal" , "-shm" ] ;
345- await Promise . all ( suffixes . map ( ( suffix ) => fs . rm ( `${ basePath } ${ suffix } ` , { force : true } ) ) ) ;
346- }
347-
348317 protected ensureSchema ( ) {
349318 const result = ensureMemoryIndexSchema ( {
350319 db : this . db ,
@@ -1159,62 +1128,64 @@ export abstract class MemoryManagerSyncOps {
11591128 let nextMeta : MemoryIndexMeta | null = null ;
11601129
11611130 try {
1162- this . seedEmbeddingCache ( originalDb ) ;
1163- const shouldSyncMemory = this . sources . has ( "memory" ) ;
1164- const shouldSyncSessions = this . shouldSyncSessions (
1165- { reason : params . reason , force : params . force } ,
1166- true ,
1167- ) ;
1168-
1169- if ( shouldSyncMemory ) {
1170- await this . syncMemoryFiles ( { needsFullReindex : true , progress : params . progress } ) ;
1171- this . dirty = false ;
1172- }
1173-
1174- if ( shouldSyncSessions ) {
1175- await this . syncSessionFiles ( { needsFullReindex : true , progress : params . progress } ) ;
1176- this . sessionsDirty = false ;
1177- this . sessionsDirtyFiles . clear ( ) ;
1178- } else if ( this . sessionsDirtyFiles . size > 0 ) {
1179- this . sessionsDirty = true ;
1180- } else {
1181- this . sessionsDirty = false ;
1182- }
1131+ nextMeta = await runMemoryAtomicReindex ( {
1132+ targetPath : dbPath ,
1133+ tempPath : tempDbPath ,
1134+ build : async ( ) => {
1135+ this . seedEmbeddingCache ( originalDb ) ;
1136+ const shouldSyncMemory = this . sources . has ( "memory" ) ;
1137+ const shouldSyncSessions = this . shouldSyncSessions (
1138+ { reason : params . reason , force : params . force } ,
1139+ true ,
1140+ ) ;
11831141
1184- nextMeta = {
1185- model : this . provider ?. model ?? "fts-only" ,
1186- provider : this . provider ?. id ?? "none" ,
1187- providerKey : this . providerKey ! ,
1188- sources : resolveConfiguredSourcesForMeta ( this . sources ) ,
1189- scopeHash : resolveConfiguredScopeHash ( {
1190- workspaceDir : this . workspaceDir ,
1191- extraPaths : this . settings . extraPaths ,
1192- multimodal : {
1193- enabled : this . settings . multimodal . enabled ,
1194- modalities : this . settings . multimodal . modalities ,
1195- maxFileBytes : this . settings . multimodal . maxFileBytes ,
1196- } ,
1197- } ) ,
1198- chunkTokens : this . settings . chunking . tokens ,
1199- chunkOverlap : this . settings . chunking . overlap ,
1200- ftsTokenizer : this . settings . store . fts . tokenizer ,
1201- } ;
1202- if ( ! nextMeta ) {
1203- throw new Error ( "Failed to compute memory index metadata for reindexing." ) ;
1204- }
1142+ if ( shouldSyncMemory ) {
1143+ await this . syncMemoryFiles ( { needsFullReindex : true , progress : params . progress } ) ;
1144+ this . dirty = false ;
1145+ }
12051146
1206- if ( this . vector . available && this . vector . dims ) {
1207- nextMeta . vectorDims = this . vector . dims ;
1208- }
1147+ if ( shouldSyncSessions ) {
1148+ await this . syncSessionFiles ( { needsFullReindex : true , progress : params . progress } ) ;
1149+ this . sessionsDirty = false ;
1150+ this . sessionsDirtyFiles . clear ( ) ;
1151+ } else if ( this . sessionsDirtyFiles . size > 0 ) {
1152+ this . sessionsDirty = true ;
1153+ } else {
1154+ this . sessionsDirty = false ;
1155+ }
12091156
1210- this . writeMeta ( nextMeta ) ;
1211- this . pruneEmbeddingCacheIfNeeded ?.( ) ;
1157+ const meta : MemoryIndexMeta = {
1158+ model : this . provider ?. model ?? "fts-only" ,
1159+ provider : this . provider ?. id ?? "none" ,
1160+ providerKey : this . providerKey ! ,
1161+ sources : resolveConfiguredSourcesForMeta ( this . sources ) ,
1162+ scopeHash : resolveConfiguredScopeHash ( {
1163+ workspaceDir : this . workspaceDir ,
1164+ extraPaths : this . settings . extraPaths ,
1165+ multimodal : {
1166+ enabled : this . settings . multimodal . enabled ,
1167+ modalities : this . settings . multimodal . modalities ,
1168+ maxFileBytes : this . settings . multimodal . maxFileBytes ,
1169+ } ,
1170+ } ) ,
1171+ chunkTokens : this . settings . chunking . tokens ,
1172+ chunkOverlap : this . settings . chunking . overlap ,
1173+ ftsTokenizer : this . settings . store . fts . tokenizer ,
1174+ } ;
1175+
1176+ if ( this . vector . available && this . vector . dims ) {
1177+ meta . vectorDims = this . vector . dims ;
1178+ }
12121179
1213- this . db . close ( ) ;
1214- originalDb . close ( ) ;
1215- originalDbClosed = true ;
1180+ this . writeMeta ( meta ) ;
1181+ this . pruneEmbeddingCacheIfNeeded ?.( ) ;
12161182
1217- await this . swapIndexFiles ( dbPath , tempDbPath ) ;
1183+ this . db . close ( ) ;
1184+ originalDb . close ( ) ;
1185+ originalDbClosed = true ;
1186+ return meta ;
1187+ } ,
1188+ } ) ;
12181189
12191190 this . db = openMemoryDatabaseAtPath ( dbPath , this . settings . store . vector . enabled ) ;
12201191 this . vectorReady = null ;
@@ -1226,7 +1197,6 @@ export abstract class MemoryManagerSyncOps {
12261197 try {
12271198 this . db . close ( ) ;
12281199 } catch { }
1229- await this . removeIndexFiles ( tempDbPath ) ;
12301200 restoreOriginalState ( ) ;
12311201 throw err ;
12321202 }
0 commit comments