2020-03-19 18:19:07 +00:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
2020-07-27 13:22:00 +01:00
|
|
|
"github.com/micro/go-micro/v3/store"
|
|
|
|
"github.com/micro/go-micro/v3/store/memory"
|
2020-03-19 18:19:07 +00:00
|
|
|
)
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// cache store is a store with caching to reduce IO where applicable.
|
|
|
|
// A memory store is used to cache reads from the given backing store.
|
|
|
|
// Reads are read through, writes are write-through
|
2020-03-19 18:19:07 +00:00
|
|
|
type cache struct {
|
2020-06-26 16:13:53 +01:00
|
|
|
m store.Store // the memory store
|
|
|
|
b store.Store // the backing store, could be file, cockroach etc
|
|
|
|
options store.Options
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// NewStore returns a new cache store
|
|
|
|
func NewStore(store store.Store, opts ...store.Option) store.Store {
|
|
|
|
cf := &cache{
|
|
|
|
m: memory.NewStore(opts...),
|
|
|
|
b: store,
|
2020-04-11 12:10:19 +01:00
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
return cf
|
2020-04-11 12:10:19 +01:00
|
|
|
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
func (c *cache) init(opts ...store.Option) error {
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&c.options)
|
|
|
|
}
|
2020-04-08 09:51:10 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// Init initialises the underlying stores
|
2020-04-11 12:10:19 +01:00
|
|
|
func (c *cache) Init(opts ...store.Option) error {
|
2020-06-26 16:13:53 +01:00
|
|
|
if err := c.init(opts...); err != nil {
|
|
|
|
return err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
if err := c.m.Init(opts...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return c.b.Init(opts...)
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// Options allows you to view the current options.
|
2020-03-19 18:19:07 +00:00
|
|
|
func (c *cache) Options() store.Options {
|
2020-06-26 16:13:53 +01:00
|
|
|
return c.options
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// Read takes a single key name and optional ReadOptions. It returns matching []*Record or an error.
|
2020-03-19 18:19:07 +00:00
|
|
|
func (c *cache) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
|
2020-06-26 16:13:53 +01:00
|
|
|
recs, err := c.m.Read(key, opts...)
|
|
|
|
if err != nil && err != store.ErrNotFound {
|
|
|
|
return nil, err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
if len(recs) > 0 {
|
|
|
|
return recs, nil
|
|
|
|
}
|
|
|
|
recs, err = c.b.Read(key, opts...)
|
|
|
|
if err == nil {
|
|
|
|
for _, rec := range recs {
|
|
|
|
if err := c.m.Write(rec); err != nil {
|
|
|
|
return nil, err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
return recs, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write() writes a record to the store, and returns an error if the record was not written.
|
|
|
|
// If the write succeeds in writing to memory but fails to write through to file, you'll receive an error
|
|
|
|
// but the value may still reside in memory so appropriate action should be taken.
|
|
|
|
func (c *cache) Write(r *store.Record, opts ...store.WriteOption) error {
|
|
|
|
if err := c.m.Write(r, opts...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return c.b.Write(r, opts...)
|
|
|
|
}
|
2020-03-19 18:19:07 +00:00
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// Delete removes the record with the corresponding key from the store.
|
|
|
|
// If the delete succeeds in writing to memory but fails to write through to file, you'll receive an error
|
|
|
|
// but the value may still reside in memory so appropriate action should be taken.
|
|
|
|
func (c *cache) Delete(key string, opts ...store.DeleteOption) error {
|
|
|
|
if err := c.m.Delete(key, opts...); err != nil {
|
|
|
|
return err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
return c.b.Delete(key, opts...)
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// List returns any keys that match, or an empty list with no error if none matched.
|
|
|
|
func (c *cache) List(opts ...store.ListOption) ([]string, error) {
|
|
|
|
keys, err := c.m.List(opts...)
|
|
|
|
if err != nil && err != store.ErrNotFound {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(keys) > 0 {
|
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
keys, err = c.b.List(opts...)
|
|
|
|
if err == nil {
|
|
|
|
for _, key := range keys {
|
|
|
|
recs, err := c.b.Read(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
for _, r := range recs {
|
|
|
|
if err := c.m.Write(r); err != nil {
|
|
|
|
return nil, err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
return keys, err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// Close the store and the underlying store
|
|
|
|
func (c *cache) Close() error {
|
|
|
|
if err := c.m.Close(); err != nil {
|
|
|
|
return err
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
2020-06-26 16:13:53 +01:00
|
|
|
return c.b.Close()
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 16:13:53 +01:00
|
|
|
// String returns the name of the implementation.
|
|
|
|
func (c *cache) String() string {
|
|
|
|
return "cache"
|
2020-03-19 18:19:07 +00:00
|
|
|
}
|