@@ -3,6 +3,7 @@ package geoblock
33import (
44 "bytes"
55 "context"
6+ "fmt"
67 "log"
78 "os"
89 "path/filepath"
@@ -12,26 +13,59 @@ import (
1213 lru "github.com/PascalMinder/geoblock/lrucache"
1314)
1415
15- func InitializeCache (ctx context.Context , logger * log.Logger , name string , cacheSize int , cachePath string ) (* lru.LRUCache , * CachePersist ) {
16- cache , err := lru .NewLRUCache (cacheSize )
16+ // Options configures cache initialization and persistence behavior.
17+ type Options struct {
18+ // CacheSize specifies the number of entries to store in memory.
19+ CacheSize int
20+
21+ // CachePath is the file path used for on-disk persistence.
22+ // Leave empty to disable persistence.
23+ CachePath string
24+
25+ // PersistInterval defines how often the cache is automatically
26+ // flushed to disk. Defaults to 15 seconds if zero.
27+ PersistInterval time.Duration
28+
29+ // Logger is used for diagnostic messages.
30+ Logger * log.Logger
31+
32+ // Name is included in log messages to identify the cache instance.
33+ Name string
34+ }
35+
36+ // InitializeCache creates a new LRU cache and, if a valid persistence
37+ // path is provided, starts a background goroutine to periodically
38+ // save snapshots to disk.
39+ //
40+ // The returned `CachePersist` can be used to mark the cache as dirty when
41+ // data changes. If persistence is disabled, it is a no-op.
42+ //
43+ // Callers should cancel the provided context to stop persistence
44+ // and ensure a final snapshot is written.
45+ func InitializeCache (ctx context.Context , opts Options ) (* lru.LRUCache , * CachePersist , error ) {
46+ if opts .PersistInterval <= 0 {
47+ opts .PersistInterval = 15 * time .Second
48+ }
49+
50+ cache , err := lru .NewLRUCache (opts .CacheSize )
1751 if err != nil {
18- logger . Fatal ( err )
52+ return nil , nil , fmt . Errorf ( "create LRU cache: %w" , err )
1953 }
2054
2155 var ipDB * CachePersist // stays nil if disabled
22- if path , err := ValidatePersistencePath (cachePath ); len (path ) > 0 {
56+ if path , err := ValidatePersistencePath (opts . CachePath ); len (path ) > 0 {
2357 // load existing cache
2458 if err := cache .ImportFromFile (path ); err != nil && ! os .IsNotExist (err ) {
25- logger . Printf ("%s: could not load IP DB snapshot (%s): %v" , name , path , err )
59+ opts . Logger . Printf ("%s: could not load IP DB snapshot (%s): %v" , opts . Name , path , err )
2660 }
2761
2862 ipDB = & CachePersist {
2963 path : path ,
3064 persistTicker : time .NewTicker (15 * time .Second ),
3165 persistChannel : make (chan struct {}, 1 ),
3266 cache : cache ,
33- log : logger ,
34- name : name ,
67+ log : opts . Logger ,
68+ name : opts . Name ,
3569 }
3670
3771 go func (ctx context.Context , p * CachePersist ) {
@@ -49,16 +83,17 @@ func InitializeCache(ctx context.Context, logger *log.Logger, name string, cache
4983 }
5084 }(ctx , ipDB )
5185
52- logger . Printf ("%s: IP database persistence enabled -> %s" , name , path )
86+ opts . Logger . Printf ("%s: IP database persistence enabled -> %s" , opts . Name , path )
5387 } else if err != nil {
54- logger . Printf ("%s: IP database persistence disabled: %v" , name , err )
88+ opts . Logger . Printf ("%s: IP database persistence disabled: %v" , opts . Name , err )
5589 } else {
56- logger . Printf ("%s: IP database persistence disabled (no path)" , name )
90+ opts . Logger . Printf ("%s: IP database persistence disabled (no path)" , opts . Name )
5791 }
5892
59- return cache , ipDB
93+ return cache , ipDB , nil
6094}
6195
96+ // CachePersist periodically snapshots a cache to disk.
6297type CachePersist struct {
6398 path string
6499 persistTicker * time.Ticker
@@ -70,6 +105,7 @@ type CachePersist struct {
70105 name string
71106}
72107
108+ // MarkDirty marks the cache as modified and schedules a snapshot.
73109func (p * CachePersist ) MarkDirty () {
74110 if p == nil { // feature OFF
75111 return
@@ -81,6 +117,7 @@ func (p *CachePersist) MarkDirty() {
81117 }
82118}
83119
120+ // Snapshot writes the cache to disk if it has been marked dirty.
84121func (p * CachePersist ) snapshotToDisk () {
85122 if p == nil || atomic .LoadUint32 (& p .ipDirty ) == 0 {
86123 return
0 commit comments