go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/filter/dscache/dscache.go (about)

     1  // Copyright 2015 The LUCI Authors.
     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  package dscache
    16  
    17  import (
    18  	"context"
    19  	"time"
    20  )
    21  
    22  const (
    23  	// MutationLockTimeout is expiration time of a "lock" memcache entry that
    24  	// protects mutations (Put/Delete/Commit). It should be larger than the
    25  	// maximum expected duration of datastore mutating operations. Must have
    26  	// seconds precision.
    27  	MutationLockTimeout = 120 * time.Second
    28  
    29  	// RefreshLockTimeout is expiration time of a "lock" memcache entry that
    30  	// protects the cache refresh process (during Get). It should be larger than
    31  	// expected Get duration, but it's not a big deal if the lock expires sooner.
    32  	// Must have seconds precision.
    33  	RefreshLockTimeout = 20 * time.Second
    34  
    35  	// CacheDuration is the default duration that a cached entity will be retained
    36  	// (memcache contention notwithstanding). Must have seconds precision.
    37  	CacheDuration = time.Hour * 24
    38  
    39  	// CompressionThreshold is the number of bytes of entity value after which
    40  	// compression kicks in.
    41  	CompressionThreshold = 16 * 1024
    42  
    43  	// DefaultShards is the default number of key sharding to do.
    44  	DefaultShards = 1
    45  
    46  	// MaxShards is the maximum number of shards a single entity can have.
    47  	MaxShards = 256
    48  
    49  	// MemcacheVersion will be incremented in the event that the in-memcache
    50  	// representation of the cache data is modified.
    51  	MemcacheVersion = "1"
    52  
    53  	// KeyFormat is the format string used to generate memcache keys. It's
    54  	//   gae:<version>:<shard#>:<base64_std_nopad(sha1(datastore.Key))>
    55  	KeyFormat = "gae:" + MemcacheVersion + ":%x:%s"
    56  
    57  	// ValueSizeLimit is the maximum encoded size a datastore key+entry may
    58  	// occupy. If a datastore entity is too large, it will have an indefinite
    59  	// lock which will cause all clients to fetch it from the datastore.
    60  	//
    61  	// See https://cloud.google.com/appengine/docs/go/memcache/#Go_Limits
    62  	// 80 is approximately the internal GAE padding. 36 is maximum length of our
    63  	// keys.
    64  	ValueSizeLimit = (1024 * 1024) - 80 - 36
    65  
    66  	// CacheEnableMeta is the gae metadata key name for whether or not dscache
    67  	// is enabled for an entity type at all.
    68  	CacheEnableMeta = "dscache.enable"
    69  
    70  	// CacheExpirationMeta is the gae metadata key name for the default
    71  	// expiration time (in seconds) for an entity type.
    72  	CacheExpirationMeta = "dscache.expiration"
    73  
    74  	// NonceBytes is the number of bytes to use in the 'lock' nonce.
    75  	NonceBytes = 8
    76  )
    77  
    78  // CacheItem represents either a cached datastore entity or a placeholder lock
    79  // that "promises" that such entity is being fetched now (either by us or by
    80  // someone else).
    81  //
    82  // CacheItem is created by TryLockAndFetch. An item that represents a lock
    83  // can be "promoted" into either a data item or a permanent lock. Such promoted
    84  // items are stored by CompareAndSwap.
    85  type CacheItem interface {
    86  	// Key is the item's key as passed to TryLockAndFetch.
    87  	Key() string
    88  
    89  	// Nonce returns nil for data items or a lock nonce for lock items.
    90  	Nonce() []byte
    91  
    92  	// Data returns nil for lock items or an item's data for data items.
    93  	Data() []byte
    94  
    95  	// Prefix should be written to the data buffer passed to PromoteToData.
    96  	Prefix() []byte
    97  
    98  	// PromoteToData converts this lock item into a data item.
    99  	//
   100  	// `data` must start with whatever Prefix() returned.
   101  	//
   102  	// Panics if self is not a lock item.
   103  	PromoteToData(data []byte, exp time.Duration)
   104  
   105  	// PromoteToIndefiniteLock converts this lock into an indefinite lock.
   106  	//
   107  	// An indefinite lock means that the datastore item is not cacheable for some
   108  	// reasons and 'Get' should not try to cache it. Such locks are removed by
   109  	// PutLocks/DropLocks.
   110  	//
   111  	// Panics if self is not a lock item.
   112  	PromoteToIndefiniteLock()
   113  }
   114  
   115  // Cache abstracts a particular memcache implementation.
   116  //
   117  // This interface is tightly coupled to the dscache algorithm (rather than
   118  // trying to emulate a generic cache API) to allow the implementation to be as
   119  // efficient as possible.
   120  type Cache interface {
   121  	// PutLocks is called before mutating entities during Put/Delete/Commit.
   122  	//
   123  	// `keys` represent CacheItem keys of all shards of all to-be-mutated
   124  	// entities. The implementation should unconditionally write locks into all
   125  	// these keys keys.
   126  	//
   127  	// Errors are treated as fatal.
   128  	PutLocks(ctx context.Context, keys []string, timeout time.Duration) error
   129  
   130  	// DropLocks is called after finishing Put/Delete/Commit.
   131  	//
   132  	// The implementation should unconditionally remove these keys, thus unlocking
   133  	// them (if they were locked).
   134  	//
   135  	// Errors are logged, but ignored.
   136  	DropLocks(ctx context.Context, keys []string) error
   137  
   138  	// TryLockAndFetch is called before executing Get.
   139  	//
   140  	// Each key is either empty, or contains some random shard of a to-be-fetched
   141  	// entity (one such key per entity). For each non-empty key, if it doesn't
   142  	// exist yet, the implementation should try to write a lock with the nonce.
   143  	// It then should fetch all keys (whatever they might be).
   144  	//
   145  	// Should always return len(keys) items, even on errors. Items matching empty
   146  	// keys should be nil. Items that do not exist in the cache should also be
   147  	// represented by nils.
   148  	//
   149  	// Errors are logged, but ignored (i.e. treated as cache misses).
   150  	TryLockAndFetch(ctx context.Context, keys []string, nonce []byte, timeout time.Duration) ([]CacheItem, error)
   151  
   152  	// CompareAndSwap stores promoted items (see CacheItem) in place of locks
   153  	// they formerly represented iff the cache still has the same locks there.
   154  	//
   155  	// Errors are logged, but ignored.
   156  	CompareAndSwap(ctx context.Context, items []CacheItem) error
   157  }