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 }