refactor: add cache tests and remove cache merge task (#14)
This commit is contained in:
48
cache/cache.go
vendored
48
cache/cache.go
vendored
@@ -1,19 +1,15 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/l3uddz/nabarr/logger"
|
||||
"github.com/lefelys/state"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/xujiajun/nutsdb"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
log zerolog.Logger
|
||||
|
||||
st state.State
|
||||
db *nutsdb.DB
|
||||
}
|
||||
|
||||
@@ -33,54 +29,14 @@ func New(path string) (*Client, error) {
|
||||
|
||||
log := logger.New("trace").With().Logger()
|
||||
|
||||
// start cleaner
|
||||
st, tail := state.WithShutdown()
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-tail.End():
|
||||
ticker.Stop()
|
||||
tail.Done()
|
||||
return
|
||||
case <-ticker.C:
|
||||
// clean cache
|
||||
err := db.Update(func(tx *nutsdb.Tx) error {
|
||||
return db.Merge()
|
||||
})
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
log.Info().Msg("Cleaned cache")
|
||||
case err.Error() == "the number of files waiting to be merged is at least 2":
|
||||
// there were no data files to be merged
|
||||
default:
|
||||
// unexpected error
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed cleaning cache")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return &Client{
|
||||
log: log,
|
||||
st: st,
|
||||
db: db,
|
||||
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
// shutdown cleaner
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
if err := c.st.Shutdown(ctx); err != nil {
|
||||
c.log.Error().
|
||||
Err(err).
|
||||
Msg("Failed shutting down cache cleaner gracefully")
|
||||
}
|
||||
|
||||
// close cache
|
||||
return c.db.Close()
|
||||
}
|
||||
|
||||
45
cache/delete_test.go
vendored
Normal file
45
cache/delete_test.go
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClient_Delete(t *testing.T) {
|
||||
type fields struct {
|
||||
log zerolog.Logger
|
||||
}
|
||||
type args struct {
|
||||
bucket string
|
||||
key string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "key not present",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "delete",
|
||||
key: "test",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
log: tt.fields.log,
|
||||
db: newDb(t, "delete"),
|
||||
}
|
||||
if err := c.Delete(tt.args.bucket, tt.args.key); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
98
cache/get_test.go
vendored
Normal file
98
cache/get_test.go
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestClient_Get(t *testing.T) {
|
||||
type fields struct {
|
||||
log zerolog.Logger
|
||||
}
|
||||
type args struct {
|
||||
bucket string
|
||||
key string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
sleep time.Duration
|
||||
put bool
|
||||
ttl time.Duration
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no value",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "get",
|
||||
key: "test",
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "with value",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "get",
|
||||
key: "test",
|
||||
},
|
||||
sleep: 1 * time.Second,
|
||||
put: true,
|
||||
ttl: 2 * time.Second,
|
||||
want: []byte("test"),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no value post ttl",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "get",
|
||||
key: "test",
|
||||
},
|
||||
sleep: 1 * time.Second,
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
log: tt.fields.log,
|
||||
db: newDb(t, "get"),
|
||||
}
|
||||
|
||||
if tt.put {
|
||||
if err := c.Put(tt.args.bucket, tt.args.key, tt.want, tt.ttl); (err != nil) != tt.wantErr && tt.sleep == 0 {
|
||||
t.Errorf("Put() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(tt.sleep)
|
||||
|
||||
got, err := c.Get(tt.args.bucket, tt.args.key)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Get() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
if err := c.Close(); err != nil {
|
||||
t.Errorf("Close() error = %v, wantErr %v", err, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
98
cache/put_test.go
vendored
Normal file
98
cache/put_test.go
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestClient_Put(t *testing.T) {
|
||||
type fields struct {
|
||||
log zerolog.Logger
|
||||
}
|
||||
type args struct {
|
||||
bucket string
|
||||
key string
|
||||
val []byte
|
||||
ttl time.Duration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
sleep time.Duration
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "with ttl",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "put",
|
||||
key: "test",
|
||||
val: []byte("testing"),
|
||||
ttl: 50 * time.Millisecond,
|
||||
},
|
||||
want: []byte("testing"),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ttl timed out",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "put",
|
||||
key: "test",
|
||||
val: []byte("testing"),
|
||||
ttl: 1 * time.Second,
|
||||
},
|
||||
sleep: 2 * time.Second,
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no ttl",
|
||||
fields: fields{
|
||||
log: zerolog.Logger{},
|
||||
},
|
||||
args: args{
|
||||
bucket: "put",
|
||||
key: "test",
|
||||
val: []byte("testing"),
|
||||
ttl: 0,
|
||||
},
|
||||
want: []byte("testing"),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
log: tt.fields.log,
|
||||
db: newDb(t, "nabarr_put"),
|
||||
}
|
||||
|
||||
if err := c.Put(tt.args.bucket, tt.args.key, tt.args.val, tt.args.ttl); (err != nil) != tt.wantErr && tt.sleep == 0 {
|
||||
t.Errorf("Put() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
time.Sleep(tt.sleep)
|
||||
|
||||
got, err := c.Get(tt.args.bucket, tt.args.key)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Put() get error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Put() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
if err := c.Close(); err != nil {
|
||||
t.Errorf("Close() error = %v, wantErr %v", err, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
24
cache/test.go
vendored
Normal file
24
cache/test.go
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/xujiajun/nutsdb"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newDb(t *testing.T, dir string) *nutsdb.DB {
|
||||
db, err := nutsdb.Open(nutsdb.Options{
|
||||
Dir: filepath.Join(os.TempDir(), dir),
|
||||
EntryIdxMode: nutsdb.HintKeyValAndRAMIdxMode,
|
||||
SegmentSize: 8 * 1024 * 1024,
|
||||
NodeNum: 1,
|
||||
RWMode: nutsdb.FileIO,
|
||||
SyncEnable: true,
|
||||
StartFileLoadingMode: nutsdb.MMap,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("newDb(dir: %v) open error: %v", dir, err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
Reference in New Issue
Block a user