@@ -33,40 +33,72 @@ package cache
33
33
34
34
import (
35
35
"errors"
36
+ "log"
37
+ "os"
36
38
"sync"
39
+ "time"
37
40
38
41
"github.com/jmoiron/sqlx"
39
42
"github.com/slub/labe/go/ckit/tabutils"
40
43
41
44
_ "github.com/mattn/go-sqlite3"
42
45
)
43
46
44
- var ErrCacheMiss = errors .New ("cache miss" )
47
+ var (
48
+ ErrCacheMiss = errors .New ("cache miss" )
49
+ ErrReadOnly = errors .New ("read only" )
50
+ DefaultMaxFileSize int64 = 1 << 36
51
+ )
45
52
46
53
// Cache is a minimalistic cache based on sqlite. In the future, values could
47
54
// be transparently compressed as well.
48
55
//
49
56
// TODO: set a limit on filesize, e.g. 100G; run periodic checks, whether
50
57
// maximum filesize is exceeded and switch to read-only mode, if necessary
51
58
type Cache struct {
52
- Path string
53
-
59
+ Path string
60
+ MaxFileSize int64
61
+ // Lock applies to both, db and readOnly.
54
62
sync.Mutex
55
- db * sqlx.DB
63
+ db * sqlx.DB
64
+ readOnly bool
56
65
}
57
66
58
67
func New (path string ) (* Cache , error ) {
59
68
conn , err := sqlx .Open ("sqlite3" , path )
60
69
if err != nil {
61
70
return nil , err
62
71
}
63
- c := & Cache {Path : path , db : conn }
72
+ c := & Cache {Path : path , db : conn , MaxFileSize : DefaultMaxFileSize }
64
73
if err := c .init (); err != nil {
65
74
return nil , err
66
75
}
76
+ c .startSizeWatcher ()
67
77
return c , nil
68
78
}
69
79
80
+ // startSizeWatcher sets up a goroutine that will watch the filesize
81
+ // periodically and will switch to read-only mode, if a given size has been
82
+ // exceeded.
83
+ func (c * Cache ) startSizeWatcher () {
84
+ go func () {
85
+ ticker := time .NewTicker (30 * time .Second )
86
+ for t := range ticker .C {
87
+ fi , err := os .Stat (c .Path )
88
+ if err != nil {
89
+ log .Printf ("could not stat file at %s, stopping watch thread" , c .Path )
90
+ break
91
+ }
92
+ if fi .Size () > c .MaxFileSize {
93
+ c .Lock ()
94
+ log .Printf ("switching cache at %s to read-only mode at %v" , c .Path , t )
95
+ c .readOnly = true
96
+ c .Unlock ()
97
+ }
98
+ }
99
+ }()
100
+ }
101
+
70
102
func (c * Cache ) init () error {
71
103
s := `
72
104
PRAGMA journal_mode = OFF;
@@ -106,6 +138,9 @@ func (c *Cache) ItemCount() (int, error) {
106
138
func (c * Cache ) Set (key string , value []byte ) error {
107
139
c .Lock ()
108
140
defer c .Unlock ()
141
+ if c .readOnly {
142
+ return ErrReadOnly
143
+ }
109
144
s := `INSERT into map (k, v) VALUES (?, ?)`
110
145
_ , err := c .db .Exec (s , key , value )
111
146
return err
0 commit comments