@@ -3,6 +3,7 @@ package geoblock
33import (
44 "bytes"
55 "context"
6+ "fmt"
67 "log"
78 "os"
89 "path/filepath"
@@ -12,26 +13,61 @@ 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+ const defaultWriteCycle = 15
17+
18+ // Options configures cache initialization and persistence behavior.
19+ type Options struct {
20+ // CacheSize specifies the number of entries to store in memory.
21+ CacheSize int
22+
23+ // CachePath is the file path used for on-disk persistence.
24+ // Leave empty to disable persistence.
25+ CachePath string
26+
27+ // PersistInterval defines how often the cache is automatically
28+ // flushed to disk. Defaults to 15 seconds if zero.
29+ PersistInterval time.Duration
30+
31+ // Logger is used for diagnostic messages.
32+ Logger * log.Logger
33+
34+ // Name is included in log messages to identify the cache instance.
35+ Name string
36+ }
37+
38+ // InitializeCache creates a new LRU cache and, if a valid persistence
39+ // path is provided, starts a background goroutine to periodically
40+ // save snapshots to disk.
41+ //
42+ // The returned `CachePersist` can be used to mark the cache as dirty when
43+ // data changes. If persistence is disabled, it is a no-op.
44+ //
45+ // Callers should cancel the provided context to stop persistence
46+ // and ensure a final snapshot is written.
47+ func InitializeCache (ctx context.Context , opts Options ) (* lru.LRUCache , * CachePersist , error ) {
48+ if opts .PersistInterval <= 0 {
49+ opts .PersistInterval = defaultWriteCycle * time .Second
50+ }
51+
52+ cache , err := lru .NewLRUCache (opts .CacheSize )
1753 if err != nil {
18- logger . Fatal ( err )
54+ return nil , nil , fmt . Errorf ( "create LRU cache: %w" , err )
1955 }
2056
2157 var ipDB * CachePersist // stays nil if disabled
22- if path , err := ValidatePersistencePath (cachePath ); len (path ) > 0 {
58+ if path , err := ValidatePersistencePath (opts . CachePath ); len (path ) > 0 {
2359 // load existing cache
2460 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 )
61+ opts . Logger . Printf ("%s: could not load IP DB snapshot (%s): %v" , opts . Name , path , err )
2662 }
2763
2864 ipDB = & CachePersist {
2965 path : path ,
30- persistTicker : time .NewTicker (15 * time . Second ),
66+ persistTicker : time .NewTicker (opts . PersistInterval ),
3167 persistChannel : make (chan struct {}, 1 ),
3268 cache : cache ,
33- log : logger ,
34- name : name ,
69+ log : opts . Logger ,
70+ name : opts . Name ,
3571 }
3672
3773 go func (ctx context.Context , p * CachePersist ) {
@@ -49,16 +85,17 @@ func InitializeCache(ctx context.Context, logger *log.Logger, name string, cache
4985 }
5086 }(ctx , ipDB )
5187
52- logger . Printf ("%s: IP database persistence enabled -> %s" , name , path )
88+ opts . Logger . Printf ("%s: IP database persistence enabled -> %s" , opts . Name , path )
5389 } else if err != nil {
54- logger . Printf ("%s: IP database persistence disabled: %v" , name , err )
90+ opts . Logger . Printf ("%s: IP database persistence disabled: %v" , opts . Name , err )
5591 } else {
56- logger . Printf ("%s: IP database persistence disabled (no path)" , name )
92+ opts . Logger . Printf ("%s: IP database persistence disabled (no path)" , opts . Name )
5793 }
5894
59- return cache , ipDB
95+ return cache , ipDB , nil
6096}
6197
98+ // CachePersist periodically snapshots a cache to disk.
6299type CachePersist struct {
63100 path string
64101 persistTicker * time.Ticker
@@ -70,6 +107,7 @@ type CachePersist struct {
70107 name string
71108}
72109
110+ // MarkDirty marks the cache as modified and schedules a snapshot.
73111func (p * CachePersist ) MarkDirty () {
74112 if p == nil { // feature OFF
75113 return
@@ -81,6 +119,7 @@ func (p *CachePersist) MarkDirty() {
81119 }
82120}
83121
122+ // Snapshot writes the cache to disk if it has been marked dirty.
84123func (p * CachePersist ) snapshotToDisk () {
85124 if p == nil || atomic .LoadUint32 (& p .ipDirty ) == 0 {
86125 return
0 commit comments