gocache

package module
v0.0.0-...-3ad59aa Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2021 License: MIT Imports: 4 Imported by: 4

README

gocache

Go cache library that brings you multiple ways of managing your caches.

CI

Features

  • Provides a cache store abstraction layer.
  • Synchronize memory cache across multiple servers.
  • Multi-stage cache.

Installation

This library is currently in alpha version.

go get github.com/knocknote/gocache@develop 

Install additional libraries depending on the cache store you want to use.

Cache Store
Store Local/Remote Module
Sync.Map Local go get github.com/knocknote/gocache/store/memcache@develop
Ristretto Local go get github.com/knocknote/gocache/store/ristretto@develop
Memcached Remote go get github.com/knocknote/gocache/store/memcache@develop
Redis Remote go get github.com/knocknote/gocache/store/redis@develop
Synchronizer

Synchronizer provides local cache synchronization across multiple servers.
If the cache exists with the same key, the cache occurrence time is compared and the received data is updated if it is new.

Synchronizer Module
Redis Pub/Sub go get github.com/knocknote/gocache/synchronizer/redis@develop
Google Cloud Pub/Sub go get github.com/knocknote/gocache/synchronizer/cloudpubsub@develop

API

  • gocache has three core interfaces Cache, Synchronizer, Marshaler.
  • You can create any cache behavior by implementing these interfaces.
Cache
Method Description
Set Set data into cache.
Get Get data from cache. Even if it is redis or memcached, you can restore it to [] byte struct and get it.
Delete Delete the data corresponding to the specified key.
Clear Delete all data in the cache.
Synchronizer

Synchronizer is required to use local cache synchronization.

Method Description
Start Synchronization with other servers or initialization of the pubsub mechanism.
Stop Synchronization with other servers or termination processing of the pubsub mechanism.
AddSubscriber Add the processing when the published data is received.
RemoveSubscriber Remove the processing when the published data is received.
Publish Send data to another server's cache store or pubsub mechanism
Marshaler
  • Marshaler is needless to use local cache.
  • Use marshaler to marshal/unmarshal data from redis or memcached.
  • Standard library includes json and msgpack marshaler.
Method Description
Marshal struct -> []byte
Unmarshal []byte -> struct

Usage

Here is the repository example.

var defaultOption = gocache.Option{
  TTL: 30 * time.Minute,
}

type RepositoryProxy struct {
  cache  gocache.Cache
  target repository.Repository
}

func NewRepositoryProxy(cache gocache.Cache, target repository.Repository) *RepositoryProxy {
  return &RepositoryProxy{
    target: target,
    cache:  cache,
  }
}

func (r *RepositoryProxy) FindByID(ctx context.Context, userID string) (*model.User, error) {
  key := r.cacheKey(ctx, userID)
  var result model.User
  if v, err, unmarshalFn := r.cache.Get(ctx, key); err == nil {
    // if the cache is redis or memcached unmarshalFn is not null. `v` is the raw byte array
    if unmarshalFn != nil {
      return &result, unmarshalFn(&result)
    // if the cache is not redis and memcached unmarshalFn is null
    } else {
      result = v.(model.User)
      return &result, nil
    }
  }
  user, err := r.target.FindByID(ctx, userID)
  if err != nil {
      return nil, err
  }
  return user, r.cache.Set(ctx, key, user, &defaultOption)
}

func (r *RepositoryProxy) Insert(ctx context.Context, target *model.User) error {
  key := r.cacheKey(ctx, target.UserID)
  if err := r.cache.Set(ctx, key, *target, &defaultOption); err != nil {
    return err
  }
  return r.target.Insert(ctx, target)
}

func (r *RepositoryProxy) cacheKey(_ context.Context, userID string) string {
  return fmt.Sprintf("RepositoryProxy.FindByID(userId=%s)", userID)
}
Sync.Map (without cache synchronization)
import (
  gocache "github.com/knocknote/gocache/store/syncmap"
)
func DependencyInjection() {
  // first args = synchronizer 
  // second args is cleanup interval (-1 means no cleanup job start)
  // third args is default option(such as TTL)
  cache := gocache.NewCache(nil, -1, nil)
  repository := NewDefaultRepository()
  repositoryProxy := NewRepositoryProxy(cache, repository)
  // start cleanup worker
  _ = cache.Start(ctx)
}
Sync.Map (with cache synchronization)
import (
  gocache "github.com/knocknote/gocache/store/syncmap"
)

func DependencyInjection() {
  client, _ := pubsub.NewClient(context.Background(), "local-project")
  topic := client.Topic("topic")
  subscription := client.Subscription("subs")
  serializer := marshaler.NewMsgpack()
  synchronizer := cloudpubsub.NewSynchronizer(serializer, topic, subscription)
  // `cache.Set(key,value,..)` set local value and publish message to topic)
  cache := syncmap.NewCache(synchronizer, -1, nil)

  _ = cache.Start(context.Backend())
  // start to subscribe topic. when it receive message then update cache.
  _ = synchronizer.Start(context.Backend())
}
Ristretto(without cache synchronization)
import (
  store "github.com/dgraph-io/ristretto"
  gocache "github.com/knocknote/gocache/store/ristretto"
)

func DependencyInjection() {
  targetStore, err := store.NewCache(&store.Config{
      NumCounters: 100,
      MaxCost:     1 << 30,
      BufferItems: 64,
  })
  // second args = synchronizer 
  // third args is default option(such as TTL / Cost)
  cache := gocache.NewCache(targetStore, nil, nil)
  repository := NewDefaultRepository()
  repositoryProxy := NewRepositoryProxy(cache, repository)
}
Ristretto (with cache synchronization)
import (
  "github.com/dgraph-io/ristretto"
  gocache "github.com/knocknote/gocache/store/ristretto"
)

func DependencyInjection() {
  targetStore, err := ristretto.NewCache(&store.Config{
      NumCounters: 100,
      MaxCost:     1 << 30,
      BufferItems: 64,
  })
  client, _ := pubsub.NewClient(context.Background(), "local-project")
  topic := client.Topic("topic")
  subscription := client.Subscription("subs")
  serializer := marshaler.NewMsgpack()
  synchronizer := cloudpubsub.NewSynchronizer(serializer, topic, subscription)
  // `cache.Set(key,value,..)` set local value and publish message to topic)
  cache := gocache.NewCache(targetStore, nil, nil)

  _ = cache.Start(context.Backend())
  // start to subscribe topic. when it receive message then update cache.
  _ = synchronizer.Start(context.Backend())
}
Memcached
import (
  "github.com/bradfitz/gomemcache/memcache"
  gocache "github.com/knocknote/gocache/store/memcache"
)

func DependencyInjection() {
  client := memcache.New("localhost:11211")
  clientProvider := func(ctx context.Context) *memcache.Client {
      return client
  }
  cache := gocache.NewCache(marshaler.NewMsgpack(), clientProvider, nil)
  repository := NewDefaultRepository()
  repositoryProxy := NewRepositoryProxy(cache, repository)
}
Redis
import (
  "github.com/go-redis/redis"
  gocache "github.com/knocknote/gocache/store/redis"
)

func DependencyInjection() {
  client := redis.NewClient(&redis.Options{
    Addr:     opt.Addr,
    Password: opt.Password,
    DB:       opt.DB,
  })
  clientProvider := func(ctx context.Context) (reader redis.Cmdable, writer redis.Cmdable) {
    return client, client
  }
  cache := gocache.NewCache(marshaler.NewMsgpack(), clientProvider, nil)
  repository := NewDefaultRepository()
  repositoryProxy := NewRepositoryProxy(cache, repository)
}
Composite Cache

Composite Cache provides cache chain.

import (
  "github.com/dgraph-io/ristretto"
  "github.com/go-redis/redis"
  "github.com/knocknote/gocache"
  gocacheristretto "github.com/knocknote/gocache/store/ristretto"
  gocacheredis "github.com/knocknote/gocache/store/redis"
)

func DependencyInjection() {
  // create L2 cache
  redisClient := redis.NewClient(&redis.Options{
    Addr:     opt.Addr,
    Password: opt.Password,
    DB:       opt.DB,
  })
  redisClientProvider := func(ctx context.Context) (reader redis.Cmdable, writer redis.Cmdable) {
    return client, client
  }
  l2cache := gocacheredis.NewCache(marshaler.NewMsgpack(), clientProvider, nil)

  // create L1 cache
  ristrettoStore, err := ristretto.NewCache(&store.Config{
    NumCounters: 100,
    MaxCost:     1 << 30,
    BufferItems: 64,
  }
  l1cache := ristretto.NewCache(ristrettoStore, nil, nil)
 
  // create composite cache
  cache := gocache.NewCompositeCache(l1cache, l2cache)

  repository := NewDefaultRepository()
  repositoryProxy := NewRepositoryProxy(cache, repository)
}

Change Date Provider and Logger

You can change default Logger and Date provider.

import (
  "github.com/knocknote/gocache"
)

func DependencyInjection() {
  gocache.Logger = any logger(such as *zap.SugaredLogger)
  gocache.Now = func(ctx context.Context) time.Time {
    return appContext.RequestScopedTime(ctx)
  }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Logger log = &defaultLogger{}
View Source
var Now = func(ctx context.Context) time.Time {
	return time.Now()
}

Functions

This section is empty.

Types

type Cache

type Cache interface {
	Set(ctx context.Context, key string, value interface{}, option *Option) error
	Get(ctx context.Context, key string) (interface{}, error, func(interface{}) error)
	Delete(ctx context.Context, key string) error
	Clear(ctx context.Context) error
}

type CompositeCache

type CompositeCache struct {
	// contains filtered or unexported fields
}

func NewCompositeCache

func NewCompositeCache(caches ...Cache) *CompositeCache

func (*CompositeCache) Clear

func (c *CompositeCache) Clear(ctx context.Context) error

func (*CompositeCache) Delete

func (c *CompositeCache) Delete(ctx context.Context, key string) error

func (*CompositeCache) Get

func (c *CompositeCache) Get(ctx context.Context, key string) (interface{}, error, func(interface{}) error)

func (*CompositeCache) Set

func (c *CompositeCache) Set(ctx context.Context, key string, value interface{}, option *Option) error

type Option

type Option struct {
	Cost int64
	TTL  time.Duration
}

Directories

Path Synopsis
store
redis Module
ristretto Module
cloudpubsub Module
redis Module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL