github.com/cs3org/reva/v2@v2.27.7/pkg/storage/cache/cache.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package cache
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    29  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    30  	"github.com/cs3org/reva/v2/pkg/store"
    31  	"github.com/shamaton/msgpack/v2"
    32  	microstore "go-micro.dev/v4/store"
    33  )
    34  
    35  var (
    36  	// DefaultStatCache is the memory store.
    37  	statCaches                = make(map[string]StatCache)
    38  	providerCaches            = make(map[string]ProviderCache)
    39  	createHomeCaches          = make(map[string]CreateHomeCache)
    40  	createPersonalSpaceCaches = make(map[string]CreatePersonalSpaceCache)
    41  	fileMetadataCaches        = make(map[string]FileMetadataCache)
    42  	mutex                     sync.Mutex
    43  )
    44  
    45  // Config contains the configuring for a cache
    46  type Config struct {
    47  	Store              string        `mapstructure:"cache_store"`
    48  	Nodes              []string      `mapstructure:"cache_nodes"`
    49  	Database           string        `mapstructure:"cache_database"`
    50  	Table              string        `mapstructure:"cache_table"`
    51  	TTL                time.Duration `mapstructure:"cache_ttl"`
    52  	Size               int           `mapstructure:"cache_size"`
    53  	DisablePersistence bool          `mapstructure:"cache_disable_persistence"`
    54  	AuthUsername       string        `mapstructure:"cache_auth_username"`
    55  	AuthPassword       string        `mapstructure:"cache_auth_password"`
    56  }
    57  
    58  // Cache handles key value operations on caches
    59  // It, and the interfaces derived from it, are currently being used
    60  // for building caches around go-micro stores, encoding the data
    61  // in the messsagepack format.
    62  type Cache interface {
    63  	PullFromCache(key string, dest interface{}) error
    64  	PushToCache(key string, src interface{}) error
    65  	List(opts ...microstore.ListOption) ([]string, error)
    66  	Delete(key string, opts ...microstore.DeleteOption) error
    67  	Close() error
    68  }
    69  
    70  // StatCache handles removing keys from a stat cache
    71  type StatCache interface {
    72  	Cache
    73  	RemoveStat(userID *userpb.UserId, res *provider.ResourceId)
    74  	RemoveStatContext(ctx context.Context, userID *userpb.UserId, res *provider.ResourceId)
    75  	GetKey(userID *userpb.UserId, ref *provider.Reference, metaDataKeys, fieldMaskPaths []string) string
    76  }
    77  
    78  // ProviderCache handles removing keys from a storage provider cache
    79  type ProviderCache interface {
    80  	Cache
    81  	RemoveListStorageProviders(res *provider.ResourceId)
    82  	GetKey(userID *userpb.UserId, spaceID string) string
    83  }
    84  
    85  // CreateHomeCache handles removing keys from a create home cache
    86  type CreateHomeCache interface {
    87  	Cache
    88  	RemoveCreateHome(res *provider.ResourceId)
    89  	GetKey(userID *userpb.UserId) string
    90  }
    91  
    92  // CreatePersonalSpaceCache handles removing keys from a create home cache
    93  type CreatePersonalSpaceCache interface {
    94  	Cache
    95  	GetKey(userID *userpb.UserId) string
    96  }
    97  
    98  // FileMetadataCache handles file metadata
    99  type FileMetadataCache interface {
   100  	Cache
   101  	RemoveMetadata(path string) error
   102  }
   103  
   104  // GetStatCache will return an existing StatCache for the given store, nodes, database and table
   105  // If it does not exist yet it will be created, different TTLs are ignored
   106  func GetStatCache(cfg Config) StatCache {
   107  	mutex.Lock()
   108  	defer mutex.Unlock()
   109  
   110  	key := strings.Join(append(append([]string{cfg.Store}, cfg.Nodes...), cfg.Database, cfg.Table), ":")
   111  	if statCaches[key] == nil {
   112  		statCaches[key] = NewStatCache(cfg)
   113  	}
   114  	return statCaches[key]
   115  }
   116  
   117  // GetProviderCache will return an existing ProviderCache for the given store, nodes, database and table
   118  // If it does not exist yet it will be created, different TTLs are ignored
   119  func GetProviderCache(cfg Config) ProviderCache {
   120  	mutex.Lock()
   121  	defer mutex.Unlock()
   122  
   123  	key := strings.Join(append(append([]string{cfg.Store}, cfg.Nodes...), cfg.Database, cfg.Table), ":")
   124  	if providerCaches[key] == nil {
   125  		providerCaches[key] = NewProviderCache(cfg)
   126  	}
   127  	return providerCaches[key]
   128  }
   129  
   130  // GetCreateHomeCache will return an existing CreateHomeCache for the given store, nodes, database and table
   131  // If it does not exist yet it will be created, different TTLs are ignored
   132  func GetCreateHomeCache(cfg Config) CreateHomeCache {
   133  	mutex.Lock()
   134  	defer mutex.Unlock()
   135  
   136  	key := strings.Join(append(append([]string{cfg.Store}, cfg.Nodes...), cfg.Database, cfg.Table), ":")
   137  	if createHomeCaches[key] == nil {
   138  		createHomeCaches[key] = NewCreateHomeCache(cfg)
   139  	}
   140  	return createHomeCaches[key]
   141  }
   142  
   143  // GetCreatePersonalSpaceCache will return an existing CreatePersonalSpaceCache for the given store, nodes, database and table
   144  // If it does not exist yet it will be created, different TTLs are ignored
   145  func GetCreatePersonalSpaceCache(cfg Config) CreatePersonalSpaceCache {
   146  	mutex.Lock()
   147  	defer mutex.Unlock()
   148  
   149  	key := strings.Join(append(append([]string{cfg.Store}, cfg.Nodes...), cfg.Database, cfg.Table), ":")
   150  	if createPersonalSpaceCaches[key] == nil {
   151  		createPersonalSpaceCaches[key] = NewCreatePersonalSpaceCache(cfg)
   152  	}
   153  	return createPersonalSpaceCaches[key]
   154  }
   155  
   156  // GetFileMetadataCache will return an existing GetFileMetadataCache for the given store, nodes, database and table
   157  // If it does not exist yet it will be created, different TTLs are ignored
   158  func GetFileMetadataCache(cfg Config) FileMetadataCache {
   159  	mutex.Lock()
   160  	defer mutex.Unlock()
   161  
   162  	key := strings.Join(append(append([]string{cfg.Store}, cfg.Nodes...), cfg.Database, cfg.Table), ":")
   163  	if fileMetadataCaches[key] == nil {
   164  		fileMetadataCaches[key] = NewFileMetadataCache(cfg)
   165  	}
   166  	return fileMetadataCaches[key]
   167  }
   168  
   169  // CacheStore holds cache store specific configuration
   170  type cacheStore struct {
   171  	s               microstore.Store
   172  	database, table string
   173  	ttl             time.Duration
   174  }
   175  
   176  // PullFromCache pulls a value from the configured database and table of the underlying store using the given key
   177  func (cache cacheStore) PullFromCache(key string, dest interface{}) error {
   178  	r, err := cache.s.Read(key, microstore.ReadFrom(cache.database, cache.table), microstore.ReadLimit(1))
   179  	if err != nil {
   180  		return err
   181  	}
   182  	if len(r) == 0 {
   183  		return fmt.Errorf("not found")
   184  	}
   185  
   186  	return msgpack.Unmarshal(r[0].Value, &dest)
   187  }
   188  
   189  // PushToCache pushes a key and value to the configured database and table of the underlying store
   190  func (cache cacheStore) PushToCache(key string, src interface{}) error {
   191  	b, err := msgpack.Marshal(src)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	record := &microstore.Record{
   197  		Key:    key,
   198  		Value:  b,
   199  		Expiry: cache.ttl,
   200  	}
   201  
   202  	return cache.s.Write(
   203  		record,
   204  		microstore.WriteTo(cache.database, cache.table),
   205  		microstore.WriteTTL(cache.ttl),
   206  	)
   207  }
   208  
   209  // List lists the keys on the configured database and table of the underlying store
   210  func (cache cacheStore) List(opts ...microstore.ListOption) ([]string, error) {
   211  	o := []microstore.ListOption{
   212  		microstore.ListFrom(cache.database, cache.table),
   213  	}
   214  	o = append(o, opts...)
   215  	keys, err := cache.s.List(o...)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	return keys, nil
   220  }
   221  
   222  // Delete deletes the given key on the configured database and table of the underlying store
   223  func (cache cacheStore) Delete(key string, opts ...microstore.DeleteOption) error {
   224  	o := []microstore.DeleteOption{
   225  		microstore.DeleteFrom(cache.database, cache.table),
   226  	}
   227  	o = append(o, opts...)
   228  	return cache.s.Delete(key, o...)
   229  }
   230  
   231  // Close closes the underlying store
   232  func (cache cacheStore) Close() error {
   233  	return cache.s.Close()
   234  }
   235  
   236  func getStore(cfg Config) microstore.Store {
   237  	return store.Create(
   238  		store.Store(cfg.Store),
   239  		microstore.Nodes(cfg.Nodes...),
   240  		microstore.Database(cfg.Database),
   241  		microstore.Table(cfg.Table),
   242  		store.TTL(cfg.TTL),
   243  		store.Size(cfg.Size),
   244  		store.DisablePersistence(cfg.DisablePersistence),
   245  		store.Authentication(cfg.AuthUsername, cfg.AuthPassword),
   246  	)
   247  }