@@ -78,7 +78,8 @@ type MappingsCache struct {
7878 adds atomic.Int64
7979
8080 // periodic saving
81- hasChanges atomic.Bool
81+ version uint64 // accessed under modifyMu
82+ lastSavedVersion uint64 // accessed under modifyMu
8283 periodicSaveInterval time.Duration
8384
8485 // custom FS
@@ -391,6 +392,7 @@ func (c *MappingsCache) AddValues(nowUnix uint32, pairs []MappingPair) {
391392 c .addItem (p .Str , p .Value , nowUnix )
392393 // ins++
393394 }
395+ c .version += 1
394396 // d4 := time.Since(a)
395397 // fmt.Printf("addPairsLocked len(pairs) len(items) dels ins: %d %d %d %d %v %v %v %v %v\n", len(pairs), len(items), dels, ins, d0, d1-d0, d2-d1, d3-d2, d4-d3)
396398 // 1024 2048 1023 1024 104.589µs 54.722µs 459.022µs 74.654µs 93.05µs
@@ -408,7 +410,6 @@ func (c *MappingsCache) addItem(k string, v int32, accessTS uint32) {
408410 c .addSumTSLocked (int64 (accessTS ))
409411 c .cache [k ] = cacheValue {value : v , accessTS : accessTS }
410412 c .adds .Add (1 )
411- c .hasChanges .Store (true ) // Mark that cache has changes
412413}
413414
414415func (c * MappingsCache ) removeItem (k string , v int32 , accessTS uint32 ) {
@@ -426,14 +427,13 @@ func (c *MappingsCache) removeItem(k string, v int32, accessTS uint32) {
426427 c .addSumTSLocked (- int64 (accessTS ))
427428 delete (c .cache , k )
428429 c .evicts .Add (1 )
429- c .hasChanges .Store (true ) // Mark that cache has changes
430430}
431431
432432func elementSizeDisk (k string ) int64 { // max, can be less if short string, requires no padding, etc.
433433 return 4 + int64 (len (k )) + 3 + 4 + 4 // strlen, string, padding, value accessTS
434434}
435435
436- func (c * MappingsCache ) Save () error {
436+ func (c * MappingsCache ) Save () ( bool , error ) {
437437 // We exclude writers so that they do not block on Lock() while code below runs in RLock().
438438 // If we allow this, all new readers (GetValue) block on RLock(), effectively waiting for Save to finish.
439439 c .modifyMu .Lock ()
@@ -442,6 +442,10 @@ func (c *MappingsCache) Save() error {
442442 c .mu .RLock ()
443443 defer c .mu .RUnlock ()
444444
445+ if c .version == c .lastSavedVersion {
446+ return false , nil
447+ }
448+
445449 saver := data_model.ChunkedStorageSaver {
446450 WriteAt : c .writeAt ,
447451 Truncate : c .truncate ,
@@ -471,17 +475,22 @@ func (c *MappingsCache) Save() error {
471475 })
472476 for _ , p := range items {
473477 if err := appendItem (p .str , p .val .value , p .val .accessTS ); err != nil {
474- return err
478+ return false , err
475479 }
476480 }
477481 } else {
478482 for k , p := range c .cache {
479483 if err := appendItem (k , p .value , p .accessTS ); err != nil {
480- return err
484+ return false , err
481485 }
482486 }
483487 }
484- return saver .FinishWrite (chunk )
488+ err := saver .FinishWrite (chunk )
489+ if err != nil {
490+ return false , err
491+ }
492+ c .lastSavedVersion = c .version
493+ return true , nil
485494}
486495
487496// callers are expected to ignore error from this method
@@ -532,19 +541,15 @@ func (c *MappingsCache) StartPeriodicSaving() {
532541}
533542
534543func (c * MappingsCache ) goPeriodicSaving () {
535- ticker := time .NewTicker (c .periodicSaveInterval )
536- defer ticker .Stop ()
537-
538- for range ticker .C {
539- // Only save if there are changes
540- if c .hasChanges .Load () {
541- err := c .Save ()
542- if err != nil {
543- log .Printf ("Periodic mappings cache save failed: %v" , err )
544- } else {
545- c .hasChanges .Store (false ) // Reset the flag after successful save
546- log .Printf ("Periodic mappings cache save completed" )
547- }
544+ for {
545+ sleepDuration := c .periodicSaveInterval + time .Duration (rand .Intn (int (c .periodicSaveInterval )))* time .Second
546+ time .Sleep (sleepDuration )
547+
548+ ok , err := c .Save ()
549+ if err != nil {
550+ log .Printf ("Periodic mappings cache save failed: %v" , err )
551+ } else if ok {
552+ log .Printf ("Periodic mappings cache save completed" )
548553 }
549554 }
550555}
0 commit comments