OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. |
| 4 |
| 5 // Package treapstore is a lightweight append-only in-memory key-value store |
| 6 // built on top a treap (tree + heap) implementation. |
| 7 // |
| 8 // treapstore is specifically focused on supporting the in-memory datastore |
| 9 // implementation at "github.com/luci/gae/impl/memory". |
| 10 package treapstore |
| 11 |
| 12 import ( |
| 13 "fmt" |
| 14 "math/rand" |
| 15 "sort" |
| 16 "sync" |
| 17 |
| 18 "github.com/luci/gtreap" |
| 19 ) |
| 20 |
| 21 // emptyTreap is an empty gtreap.Treap. |
| 22 var emptyTreap = gtreap.NewTreap(nil) |
| 23 |
| 24 // Store is a general-purpose concurrently-accessible copy-on-write store built |
| 25 // on top of the github.com/steveyen/gtreap treap (tree + heap) implementation. |
| 26 // |
| 27 // A Store is composed of a series of named Collection instances, each of which |
| 28 // can hold data elements. |
| 29 // |
| 30 // A Store is created by calling New, and is initially in a read/write mode. |
| 31 // Derivative Stores can be created by calling a Store's Snapshot capability. |
| 32 // These Stores will be read-only, meaning their collections and those |
| 33 // collections' data data may only be accessed, not modified. |
| 34 // |
| 35 // A Store's zero value is a valid read-only empty Store, but is not terribly |
| 36 // useful. |
| 37 type Store struct { |
| 38 collLock *sync.RWMutex |
| 39 colls map[string]*Collection |
| 40 collNames []string // collNames is copy-on-write while holding collLock. |
| 41 } |
| 42 |
| 43 // New creates a new read/write Store. |
| 44 func New() *Store { |
| 45 return &Store{ |
| 46 collLock: &sync.RWMutex{}, |
| 47 } |
| 48 } |
| 49 |
| 50 // IsReadOnly returns true if this Store is read-only. |
| 51 func (s *Store) IsReadOnly() bool { return s.collLock == nil } |
| 52 |
| 53 func (s *Store) assertNotReadOnly() { |
| 54 if s.IsReadOnly() { |
| 55 panic("store is read-only") |
| 56 } |
| 57 } |
| 58 |
| 59 // Snapshot creates a read-only copy of the Store and all of its Collection |
| 60 // instances. Because a Store is copy-on-write, this is a cheap operation. |
| 61 func (s *Store) Snapshot() *Store { |
| 62 if s.IsReadOnly() { |
| 63 return s |
| 64 } |
| 65 |
| 66 s.collLock.RLock() |
| 67 defer s.collLock.RUnlock() |
| 68 |
| 69 // Return a read-only Store with the new Collection set. |
| 70 snap := &Store{ |
| 71 collLock: nil, |
| 72 colls: make(map[string]*Collection, len(s.colls)), |
| 73 collNames: s.collNames, |
| 74 } |
| 75 |
| 76 // Create a read-only Collection for each coll. |
| 77 for k, coll := range s.colls { |
| 78 newColl := &Collection{ |
| 79 name: coll.name, |
| 80 } |
| 81 newColl.setRoot(coll.currentRoot()) |
| 82 snap.colls[k] = newColl |
| 83 } |
| 84 return snap |
| 85 } |
| 86 |
| 87 // GetCollectionNames returns the names of the Collections in this Store, sorted |
| 88 // alphabetically. |
| 89 func (s *Store) GetCollectionNames() []string { |
| 90 if s.collLock != nil { |
| 91 s.collLock.RLock() |
| 92 defer s.collLock.RUnlock() |
| 93 } |
| 94 |
| 95 // Clone our collection names slice. |
| 96 if len(s.collNames) == 0 { |
| 97 return nil |
| 98 } |
| 99 return append([]string(nil), s.collNames...) |
| 100 } |
| 101 |
| 102 // GetCollection returns the Collection with the specified name. If no such |
| 103 // Collection exists, GetCollection will return nil. |
| 104 func (s *Store) GetCollection(name string) *Collection { |
| 105 if s.collLock != nil { |
| 106 s.collLock.RLock() |
| 107 defer s.collLock.RUnlock() |
| 108 } |
| 109 return s.colls[name] |
| 110 } |
| 111 |
| 112 // CreateCollection returns a Collection with the specified name. If the |
| 113 // collection already exists, or if s is read-only, CreateCollection will panic. |
| 114 func (s *Store) CreateCollection(name string, compare gtreap.Compare) *Collectio
n { |
| 115 s.assertNotReadOnly() |
| 116 |
| 117 s.collLock.Lock() |
| 118 defer s.collLock.Unlock() |
| 119 |
| 120 // Try to get the existing Collection again now that we hold the write l
ock. |
| 121 if _, ok := s.colls[name]; ok { |
| 122 panic(fmt.Errorf("collection %q already exists", name)) |
| 123 } |
| 124 |
| 125 // Create a new read/write Collection. |
| 126 coll := &Collection{ |
| 127 name: name, |
| 128 rootLock: &sync.RWMutex{}, |
| 129 } |
| 130 coll.setRoot(gtreap.NewTreap(compare)) |
| 131 |
| 132 if s.colls == nil { |
| 133 s.colls = make(map[string]*Collection) |
| 134 } |
| 135 s.colls[name] = coll |
| 136 s.collNames = s.insertCollectionName(name) |
| 137 return coll |
| 138 } |
| 139 |
| 140 // insertCollectionName returns a copy of s.collNames with name inserted |
| 141 // in its appropriate sorted position. |
| 142 func (s *Store) insertCollectionName(name string) []string { |
| 143 sidx := sort.SearchStrings(s.collNames, name) |
| 144 r := make([]string, 0, len(s.collNames)+1) |
| 145 return append(append(append(r, s.collNames[:sidx]...), name), s.collName
s[sidx:]...) |
| 146 } |
| 147 |
| 148 // Collection is a collection of Items. |
| 149 // |
| 150 // Collections belonging to read/write Store instances are, themselves, |
| 151 // read/write. Collections belonging to Snapshot instances are read-only. |
| 152 // |
| 153 // A Collection's zero value is a valid read-only empty Collection, which is not |
| 154 // terribly useful. |
| 155 type Collection struct { |
| 156 name string |
| 157 |
| 158 rootLock *sync.RWMutex |
| 159 root *gtreap.Treap |
| 160 } |
| 161 |
| 162 func (c *Collection) assertNotReadOnly() { |
| 163 if c.IsReadOnly() { |
| 164 panic("collection is read-only") |
| 165 } |
| 166 } |
| 167 |
| 168 // Name returns this Collection's name. |
| 169 func (c *Collection) Name() string { return c.name } |
| 170 |
| 171 // IsReadOnly returns true if this Collection is read-only. |
| 172 func (c *Collection) IsReadOnly() bool { return c.rootLock == nil } |
| 173 |
| 174 // Min returns the smallest item in the collection. |
| 175 func (c *Collection) Min() gtreap.Item { |
| 176 root := c.currentRoot() |
| 177 if root == nil { |
| 178 return nil |
| 179 } |
| 180 return root.Min() |
| 181 } |
| 182 |
| 183 // Max returns the largest item in the collection. |
| 184 func (c *Collection) Max() gtreap.Item { |
| 185 root := c.currentRoot() |
| 186 if root == nil { |
| 187 return nil |
| 188 } |
| 189 return root.Max() |
| 190 } |
| 191 |
| 192 // Get returns the item in the Store that matches i, or nil if no such item |
| 193 // exists. |
| 194 func (c *Collection) Get(i gtreap.Item) gtreap.Item { |
| 195 root := c.currentRoot() |
| 196 if root == nil { |
| 197 return nil |
| 198 } |
| 199 return root.Get(i) |
| 200 } |
| 201 |
| 202 // Put adds an item to the Store. |
| 203 // |
| 204 // If the Store is read-only, Put will panic. |
| 205 func (c *Collection) Put(i gtreap.Item) { |
| 206 c.assertNotReadOnly() |
| 207 |
| 208 // Lock around the entire Upsert operation to serialize Puts. |
| 209 priority := rand.Int() |
| 210 c.rootLock.Lock() |
| 211 c.root = c.root.Upsert(i, priority) |
| 212 c.rootLock.Unlock() |
| 213 } |
| 214 |
| 215 // Delete deletes an item from the Collection, if such an item exists. |
| 216 func (c *Collection) Delete(i gtreap.Item) { |
| 217 c.assertNotReadOnly() |
| 218 |
| 219 c.rootLock.Lock() |
| 220 c.root = c.root.Delete(i) |
| 221 c.rootLock.Unlock() |
| 222 } |
| 223 |
| 224 func (c *Collection) currentRoot() *gtreap.Treap { |
| 225 if c.rootLock != nil { |
| 226 c.rootLock.RLock() |
| 227 defer c.rootLock.RUnlock() |
| 228 } |
| 229 return c.root |
| 230 } |
| 231 |
| 232 func (c *Collection) setRoot(root *gtreap.Treap) { |
| 233 if c.rootLock != nil { |
| 234 c.rootLock.Lock() |
| 235 defer c.rootLock.Unlock() |
| 236 } |
| 237 c.root = root |
| 238 } |
| 239 |
| 240 // Iterator returns an iterator over the Store, starting at the supplied pivot |
| 241 // item. |
| 242 func (c *Collection) Iterator(pivot gtreap.Item) *gtreap.Iterator { |
| 243 root := c.currentRoot() |
| 244 if root == nil { |
| 245 root = emptyTreap |
| 246 } |
| 247 return root.Iterator(pivot) |
| 248 } |
OLD | NEW |