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  }