gopkg.in/dedis/onet.v2@v2.0.0-20181115163211-c8f3724038a7/cache.go (about) 1 package onet 2 3 import ( 4 "sync" 5 "time" 6 7 uuid "github.com/satori/go.uuid" 8 "gopkg.in/dedis/onet.v2/log" 9 ) 10 11 // Key-value cache with an expiration time for each pair 12 // defined at the cache creation 13 // The implementation is robust against concurrent call 14 type cacheTTL struct { 15 entries map[uuid.UUID]*cacheTTLEntry 16 head *cacheTTLEntry 17 tail *cacheTTLEntry 18 entryExpiration time.Duration 19 size int 20 sync.Mutex 21 } 22 23 type cacheTTLEntry struct { 24 key uuid.UUID 25 item interface{} 26 expiration time.Time 27 prev *cacheTTLEntry 28 next *cacheTTLEntry 29 } 30 31 // create a generic cache and start the cleaning routine 32 func newCacheTTL(expiration time.Duration, size int) *cacheTTL { 33 if size == 0 { 34 log.Error("Cannot instantiate a cache with a size of 0") 35 return nil 36 } 37 38 return &cacheTTL{ 39 entries: make(map[uuid.UUID]*cacheTTLEntry), 40 entryExpiration: expiration, 41 size: size, 42 } 43 } 44 45 // add the item to the cache with the given key 46 func (c *cacheTTL) set(key uuid.UUID, item interface{}) { 47 entry := c.entries[key] 48 if entry != nil { 49 entry.expiration = time.Now().Add(c.entryExpiration) 50 entry.item = item 51 } else { 52 entry = &cacheTTLEntry{ 53 key: key, 54 item: item, 55 expiration: time.Now().Add(c.entryExpiration), 56 } 57 58 c.clean() // clean before checking the size 59 if len(c.entries) >= c.size && c.tail != nil { 60 // deletes the oldest entry and ignore edge cases with size = 0 61 delete(c.entries, c.tail.key) 62 c.tail = c.tail.next 63 c.tail.prev = nil 64 } 65 } 66 67 c.moveToHead(entry) 68 c.entries[key] = entry 69 } 70 71 // returns the cached item or nil 72 func (c *cacheTTL) get(key uuid.UUID) interface{} { 73 entry, ok := c.entries[key] 74 if ok && time.Now().Before(entry.expiration) { 75 entry.expiration = time.Now().Add(c.entryExpiration) 76 c.moveToHead(entry) 77 return entry.item 78 } 79 80 c.clean() // defensive cleaning 81 return nil 82 } 83 84 func (c *cacheTTL) moveToHead(e *cacheTTLEntry) { 85 if c.head == e { 86 // already at the top of the list 87 return 88 } 89 90 if c.tail == e && e.next != nil { 91 // item was the tail so we need to assign the new tail 92 c.tail = e.next 93 } 94 // remove the list entry from its previous position 95 if e.next != nil { 96 e.next.prev = e.prev 97 } 98 if e.prev != nil { 99 e.prev.next = e.next 100 } 101 102 // assign the entry at the top of the list 103 if c.head == nil { 104 c.head = e 105 if c.tail == nil { 106 c.tail = c.head 107 } 108 } else { 109 c.head.next = e 110 e.prev = c.head 111 e.next = nil 112 c.head = e 113 } 114 } 115 116 func (c *cacheTTL) clean() { 117 now := time.Now() 118 119 for c.tail != nil && now.After(c.tail.expiration) { 120 delete(c.entries, c.tail.key) 121 122 if c.head == c.tail { 123 c.head = nil 124 c.tail = nil 125 } else { 126 c.tail = c.tail.next 127 } 128 } 129 } 130 131 type treeCacheTTL struct { 132 *cacheTTL 133 } 134 135 func newTreeCache(expiration time.Duration, size int) *treeCacheTTL { 136 return &treeCacheTTL{ 137 cacheTTL: newCacheTTL(expiration, size), 138 } 139 } 140 141 // Set stores the given tree in the cache 142 func (c *treeCacheTTL) Set(tree *Tree) { 143 c.Lock() 144 c.set(uuid.UUID(tree.ID), tree) 145 c.Unlock() 146 } 147 148 // Get retrieves the tree with the given ID if it exists 149 // or returns nil 150 func (c *treeCacheTTL) Get(id TreeID) *Tree { 151 c.Lock() 152 defer c.Unlock() 153 154 tree := c.get(uuid.UUID(id)) 155 if tree != nil { 156 return tree.(*Tree) 157 } 158 159 return nil 160 } 161 162 // GetFromToken does the same as Get but with a token 163 func (c *treeCacheTTL) GetFromToken(token *Token) *Tree { 164 return c.Get(token.TreeID) 165 } 166 167 type rosterCacheTTL struct { 168 *cacheTTL 169 } 170 171 func newRosterCache(expiration time.Duration, size int) *rosterCacheTTL { 172 return &rosterCacheTTL{ 173 cacheTTL: newCacheTTL(expiration, size), 174 } 175 } 176 177 // Set stores the roster in the cache 178 func (c *rosterCacheTTL) Set(roster *Roster) { 179 c.Lock() 180 c.set(uuid.UUID(roster.ID), roster) 181 c.Unlock() 182 } 183 184 // Get retrieves the Roster with the given ID if it exists 185 // or it returns nil 186 func (c *rosterCacheTTL) Get(id RosterID) *Roster { 187 c.Lock() 188 defer c.Unlock() 189 190 roster := c.get(uuid.UUID(id)) 191 if roster != nil { 192 return roster.(*Roster) 193 } 194 195 return nil 196 } 197 198 // GetFromToken does the same as Get but with a token 199 func (c *rosterCacheTTL) GetFromToken(token *Token) *Roster { 200 return c.Get(token.RosterID) 201 } 202 203 // treeNodeCacheTTL is a cache that maps from token to treeNode. Since 204 // the mapping is not 1-1 (many Tokens can point to one TreeNode, but 205 // one token leads to one TreeNode), we have to do certain lookup, but 206 // that's better than searching the tree each time. 207 type treeNodeCacheTTL struct { 208 *cacheTTL 209 } 210 211 func newTreeNodeCache(expiration time.Duration, size int) *treeNodeCacheTTL { 212 return &treeNodeCacheTTL{ 213 cacheTTL: newCacheTTL(expiration, size), 214 } 215 } 216 217 func (c *treeNodeCacheTTL) Set(tree *Tree, treeNode *TreeNode) { 218 c.Lock() 219 220 var treeNodeMap map[TreeNodeID]*TreeNode 221 e := c.get(uuid.UUID(tree.ID)) 222 if e == nil { 223 treeNodeMap = make(map[TreeNodeID]*TreeNode) 224 } else { 225 treeNodeMap = e.(map[TreeNodeID]*TreeNode) 226 } 227 228 // add treenode 229 treeNodeMap[treeNode.ID] = treeNode 230 // add parent if not root 231 if treeNode.Parent != nil { 232 treeNodeMap[treeNode.Parent.ID] = treeNode.Parent 233 } 234 // add children 235 for _, c := range treeNode.Children { 236 treeNodeMap[c.ID] = c 237 } 238 // add cache 239 c.set(uuid.UUID(tree.ID), treeNodeMap) 240 c.Unlock() 241 } 242 243 func (c *treeNodeCacheTTL) GetFromToken(tok *Token) *TreeNode { 244 c.Lock() 245 defer c.Unlock() 246 247 if tok == nil { 248 return nil 249 } 250 251 e := c.get(uuid.UUID(tok.TreeID)) 252 if e == nil { 253 return nil 254 } 255 256 treeNodeMap := e.(map[TreeNodeID]*TreeNode) 257 tn, ok := treeNodeMap[tok.TreeNodeID] 258 if !ok { 259 // no treeNode cached for this token 260 return nil 261 } 262 return tn 263 }