@@ -101,6 +101,13 @@ type DB struct {
101101 m map [int ]* ltx.FileInfo
102102 }
103103
104+ // Cached position from the latest L0 LTX file.
105+ // nil means cache is invalid; non-nil is the cached position.
106+ pos struct {
107+ sync.Mutex
108+ value * ltx.Pos
109+ }
110+
104111 fileInfo os.FileInfo // db info cached during init
105112 dirInfo os.FileInfo // parent dir info cached during init
106113
@@ -300,6 +307,8 @@ func (db *DB) ResetLocalState(ctx context.Context) error {
300307 db .maxLTXFileInfos .m = make (map [int ]* ltx.FileInfo )
301308 db .maxLTXFileInfos .Unlock ()
302309
310+ db .invalidatePosCache ()
311+
303312 db .Logger .Info ("local state reset complete, next sync will create fresh snapshot" )
304313 return nil
305314}
@@ -330,6 +339,9 @@ func (db *DB) deleteLocalLTXFile(level int, minTXID, maxTXID ltx.TXID) error {
330339 if err := os .Remove (path ); err != nil && ! os .IsNotExist (err ) {
331340 return err
332341 }
342+ if level == 0 {
343+ db .invalidatePosCache ()
344+ }
333345 return nil
334346}
335347
@@ -364,7 +376,15 @@ func (db *DB) DirInfo() os.FileInfo {
364376}
365377
366378// Pos returns the current replication position of the database.
379+ // The result is cached and invalidated when L0 LTX files change.
367380func (db * DB ) Pos () (ltx.Pos , error ) {
381+ db .pos .Lock ()
382+ defer db .pos .Unlock ()
383+
384+ if db .pos .value != nil {
385+ return * db .pos .value , nil
386+ }
387+
368388 minTXID , maxTXID , err := db .MaxLTX ()
369389 if err != nil {
370390 return ltx.Pos {}, err
@@ -382,7 +402,20 @@ func (db *DB) Pos() (ltx.Pos, error) {
382402 if err := dec .Verify (); err != nil {
383403 return ltx.Pos {}, fmt .Errorf ("ltx verification failed: %w" , err )
384404 }
385- return dec .PostApplyPos (), nil
405+
406+ pos := dec .PostApplyPos ()
407+ db .pos .value = & pos
408+
409+ return pos , nil
410+ }
411+
412+ // invalidatePosCache clears the cached position so the next call to Pos()
413+ // recomputes it from disk. Call this when L0 LTX files are deleted or
414+ // when the L0 directory is cleared.
415+ func (db * DB ) invalidatePosCache () {
416+ db .pos .Lock ()
417+ db .pos .value = nil
418+ db .pos .Unlock ()
386419}
387420
388421// Notify returns a channel that closes when the shadow WAL changes.
@@ -1195,6 +1228,7 @@ func (db *DB) checkDatabaseBehindReplica(ctx context.Context) error {
11951228 if err := os .RemoveAll (l0Dir ); err != nil && ! os .IsNotExist (err ) {
11961229 return fmt .Errorf ("remove L0 directory: %w" , err )
11971230 }
1231+ db .invalidatePosCache ()
11981232 if err := internal .MkdirAll (l0Dir , db .dirInfo ); err != nil {
11991233 return fmt .Errorf ("recreate L0 directory: %w" , err )
12001234 }
@@ -1235,6 +1269,7 @@ func (db *DB) checkDatabaseBehindReplica(ctx context.Context) error {
12351269 if err := os .Rename (tmpPath , localPath ); err != nil {
12361270 return fmt .Errorf ("rename L0 file: %w" , err )
12371271 }
1272+ db .invalidatePosCache ()
12381273
12391274 db .Logger .Info ("fetched latest L0 file from replica" ,
12401275 "min_txid" , minTXID ,
@@ -1632,6 +1667,7 @@ func (db *DB) sync(ctx context.Context, checkpointing bool, info syncInfo) (sync
16321667 db .maxLTXFileInfos .Lock ()
16331668 delete (db .maxLTXFileInfos .m , 0 ) // clear cache if in unknown state
16341669 db .maxLTXFileInfos .Unlock ()
1670+ db .invalidatePosCache ()
16351671 return false , fmt .Errorf ("rename ltx file: %w" , err )
16361672 }
16371673
@@ -1646,6 +1682,12 @@ func (db *DB) sync(ctx context.Context, checkpointing bool, info syncInfo) (sync
16461682 }
16471683 db .maxLTXFileInfos .Unlock ()
16481684
1685+ // Update cached position from the encoder.
1686+ encPos := enc .PostApplyPos ()
1687+ db .pos .Lock ()
1688+ db .pos .value = & encPos
1689+ db .pos .Unlock ()
1690+
16491691 // Track the logical end of WAL content for checkpoint decisions.
16501692 // This is the WALOffset + WALSize from the LTX we just created.
16511693 // Using this instead of file size prevents issue #997 where stale
@@ -2177,6 +2219,9 @@ func (db *DB) EnforceL0RetentionByTime(ctx context.Context) error {
21772219 db .Logger .Error ("failed to remove local l0 file" , "path" , localPath , "error" , err )
21782220 }
21792221 }
2222+ if len (deleted ) > 0 {
2223+ db .invalidatePosCache ()
2224+ }
21802225
21812226 db .Logger .Info ("l0 retention enforced" , "deleted_count" , len (deleted ), "max_l1_txid" , maxL1TXID )
21822227
0 commit comments