-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcache.go
158 lines (139 loc) · 4.13 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package badgercache
import (
"errors"
"io"
"time"
"github.com/dgraph-io/badger"
)
// Cache contains options to connect to a badger database
// a TTL of 0 does not expire keys
type Cache struct {
TTL time.Duration
opts *badger.Options
gcOpts *GarbageCollectionOptions
}
// Conn is a connection to a badger database
type Conn struct {
TTL time.Duration
db *badger.DB
ticker *time.Ticker // for GC loop
}
// GarbageCollectionOptions specifies settings for Badger garbage collection
type GarbageCollectionOptions struct {
Frequency time.Duration
DiscardRatio float64
}
// Stats displays stats about badger
type Stats map[string]interface{}
// DefaultGCOptions are the default GarbageCollectionOptions
var DefaultGCOptions = GarbageCollectionOptions{
Frequency: time.Minute,
DiscardRatio: 0.5,
}
// NewCache creates a new Cache
func NewCache(defaultTimeout time.Duration, opts *badger.Options, gcOpts *GarbageCollectionOptions) (*Cache, error) {
if defaultTimeout < time.Second && defaultTimeout > 0 {
return &Cache{}, errors.New("TTL must be >= 1 second. Badger uses Unix timestamps for expiries which operate in second resolution")
}
if opts == nil {
opts = &badger.DefaultOptions
}
if gcOpts == nil {
gcOpts = &DefaultGCOptions
}
return &Cache{opts: opts, gcOpts: gcOpts, TTL: defaultTimeout}, nil
}
// Open opens a new connection to Badger
func (c Cache) Open(name string) (*Conn, error) {
c.opts.Dir = name
c.opts.ValueDir = name
db, err := badger.Open(*c.opts)
if err != nil {
return &Conn{}, err
}
// start a GC loop
ticker := time.NewTicker(c.gcOpts.Frequency)
go func(t *time.Ticker, d *badger.DB) {
for range t.C {
again:
err := d.RunValueLogGC(c.gcOpts.DiscardRatio)
if err == nil {
goto again
}
}
}(ticker, db)
return &Conn{TTL: c.TTL, ticker: ticker, db: db}, nil
}
// Close closes the badger connection
func (c *Conn) Close() error {
c.ticker.Stop()
return c.db.Close()
}
// Write writes data to the cache with the default cache TTL
func (c *Conn) Write(k, v []byte) error {
return c.WriteTTL(k, v, c.TTL)
}
// WriteTTL writes data to the cache with an explicit TTL
// a TTL of 0 does not expire keys
func (c *Conn) WriteTTL(k, v []byte, ttl time.Duration) error {
return c.db.Update(func(txn *badger.Txn) error {
return setWithTTL(txn, k, v, ttl)
})
}
// Read retrieves data for a key from the cache
func (c *Conn) Read(k []byte) ([]byte, error) {
ret := []byte{}
err := c.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(k)
if err != nil {
return err
}
ret, err = item.Value()
return err
})
return ret, err
}
// Stats provides stats about the Badger database
func (c *Conn) Stats() (map[string]interface{}, error) {
lsm, vlog := c.db.Size()
return Stats{
"LSMSize": lsm,
"VLogSize": vlog,
}, nil
}
// Backup dumps a protobuf-encoded list of all entries in the database into the
// given writer, that are newer than the specified version. It returns a
// timestamp indicating when the entries were dumped which can be passed into a
// later invocation to generate an incremental dump, of entries that have been
// added/modified since the last invocation of DB.Backup()
//
// This can be used to backup the data in a database at a given point in time.
func (c *Conn) Backup(w io.Writer, since uint64) (upto uint64, err error) {
return c.db.Backup(w, since)
}
// Load reads a protobuf-encoded list of all entries from a reader and writes
// them to the database. This can be used to restore the database from a backup
// made by calling DB.Backup().
//
// DB.Load() should be called on a database that is not running any other
// concurrent transactions while it is running.
func (c *Conn) Load(r io.Reader) error {
return c.db.Load(r)
}
func setWithTTL(txn *badger.Txn, k, v []byte, ttl time.Duration) error {
// set the new value with TTL
if ttl < time.Second && ttl > 0 {
return errors.New("TTL must be >= 1 second. Badger uses Unix timestamps for expiries which operate in second resolution")
} else if ttl > 0 {
err := txn.SetWithTTL(k, v, ttl)
if err != nil {
return err
}
} else {
err := txn.Set(k, v)
if err != nil {
return err
}
}
return nil
}