Chromium Code Reviews| 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 "math/rand" | |
| 14 "sort" | |
| 15 "sync" | |
| 16 "sync/atomic" | |
| 17 | |
| 18 "github.com/luci/gtreap" | |
|
Vadim Sh.
2016/12/28 01:33:40
do we need to fork it? IIRC, we forked gkvlite to
Vadim Sh.
2016/12/28 01:34:27
nvm, saw another CL :)
dnj
2016/12/28 03:01:06
Acknowledged. Yeah will actually put that forward
| |
| 19 ) | |
| 20 | |
| 21 // Store is a generaly-purpose concurrently-accessible copy-on-write store built | |
| 22 // on top of the github.com/steveyen/gtreap treap (tree + heap) implementation. | |
| 23 // | |
| 24 // A Store is composed of a series of named Collection instances, each of which | |
| 25 // can hold data elements. | |
| 26 // | |
| 27 // A Store is created by calling New, and is initially in a read/write mode. | |
| 28 // Derivative Stores can be created by calling a Store's Snapshot capability. | |
| 29 // These Stores will be read-only, meaning their collections and those | |
| 30 // collections' data data may only be accessed, not modified. | |
| 31 type Store struct { | |
| 32 compare gtreap.Compare | |
| 33 colls map[string]*Collection | |
| 34 collLock *sync.RWMutex | |
| 35 } | |
| 36 | |
| 37 // New creates a new read/write Store. | |
| 38 func New(c gtreap.Compare) *Store { | |
| 39 return &Store{ | |
| 40 compare: c, | |
| 41 collLock: &sync.RWMutex{}, | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 // IsReadOnly returns true if this Store is read-only. | |
| 46 func (s *Store) IsReadOnly() bool { return s.collLock == nil } | |
| 47 | |
| 48 func (s *Store) assertNotReadOnly() { | |
| 49 if s.IsReadOnly() { | |
| 50 panic("store is read-only") | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 // Snapshot creates a snapshot of the Store and all of its Collection instances. | |
|
Vadim Sh.
2016/12/28 01:33:40
nit: "Snapshot creates a read-only copy of the Sto
dnj
2016/12/28 03:01:06
Done.
| |
| 55 // Because a Store is copy-on-write, this is a cheap operation. | |
| 56 func (s *Store) Snapshot() *Store { | |
| 57 if s.IsReadOnly() { | |
| 58 return s | |
| 59 } | |
| 60 | |
| 61 s.collLock.RLock() | |
| 62 defer s.collLock.RUnlock() | |
| 63 | |
| 64 // Return a read-only Store with the new Collection set. | |
| 65 snap := &Store{ | |
| 66 compare: s.compare, | |
| 67 colls: make(map[string]*Collection, len(s.colls)), | |
| 68 collLock: nil, | |
| 69 } | |
| 70 | |
| 71 // Create a read-only Collection for each coll. | |
| 72 for k, coll := range s.colls { | |
| 73 newColl := &Collection{ | |
| 74 s: snap, | |
| 75 name: coll.name, | |
| 76 } | |
| 77 newColl.setRoot(coll.currentRoot()) | |
| 78 snap.colls[k] = newColl | |
| 79 } | |
| 80 return snap | |
| 81 } | |
| 82 | |
| 83 // GetCollectionNames returns the names of the Collections in this Store, sorted | |
| 84 // alphabetically. | |
| 85 func (s *Store) GetCollectionNames() []string { | |
| 86 if !s.IsReadOnly() { | |
| 87 s.collLock.RLock() | |
| 88 defer s.collLock.RUnlock() | |
| 89 } | |
| 90 | |
| 91 names := make([]string, 0, len(s.colls)) | |
| 92 for k := range s.colls { | |
| 93 names = append(names, k) | |
| 94 } | |
| 95 sort.Strings(names) | |
| 96 return names | |
| 97 } | |
| 98 | |
| 99 // GetCollection returns the Collection with the specified name. If no such | |
| 100 // Collection exists, GetCollection will return nil. | |
| 101 func (s *Store) GetCollection(name string) *Collection { | |
| 102 if !s.IsReadOnly() { | |
| 103 s.collLock.RLock() | |
| 104 defer s.collLock.RUnlock() | |
| 105 } | |
| 106 return s.colls[name] | |
| 107 } | |
| 108 | |
| 109 // GetOrCreateCollection returns a Collection with the specified name, creating | |
| 110 // it if it does not exist. If s is read-only, this will panic regardless of | |
| 111 // whether the named Collection exists. | |
| 112 func (s *Store) GetOrCreateCollection(name string) *Collection { | |
| 113 s.assertNotReadOnly() | |
| 114 | |
| 115 // Fast path: get the Collection using read lock. | |
| 116 coll := s.GetCollection(name) | |
| 117 if coll != nil { | |
| 118 return coll | |
| 119 } | |
| 120 | |
| 121 // Might have to create the Collection. Grab the write lock. | |
| 122 s.collLock.Lock() | |
| 123 defer s.collLock.Unlock() | |
| 124 | |
| 125 // Try to get the existing Collection again now that we hold the write l ock. | |
| 126 coll = s.colls[name] | |
| 127 if coll != nil { | |
| 128 return coll | |
| 129 } | |
| 130 | |
| 131 // Create a new read/write Collection. | |
| 132 coll = &Collection{ | |
| 133 s: s, | |
| 134 name: name, | |
| 135 putLock: &sync.Mutex{}, | |
| 136 } | |
| 137 coll.setRoot(gtreap.NewTreap(s.compare)) | |
| 138 | |
| 139 if s.colls == nil { | |
| 140 s.colls = make(map[string]*Collection) | |
| 141 } | |
| 142 s.colls[name] = coll | |
| 143 return coll | |
| 144 } | |
| 145 | |
| 146 // Collection is a collection of Items. | |
| 147 // | |
| 148 // Collections belonging to read/write Store instances are, themselves, | |
| 149 // read/write. Collections belonging to Snapshot instances are read-only. | |
| 150 type Collection struct { | |
| 151 s *Store | |
|
Vadim Sh.
2016/12/28 01:33:39
nit: this is unnecessary, just use 'putLock == nil
dnj
2016/12/28 03:01:06
Done.
| |
| 152 name string | |
| 153 | |
| 154 root *atomic.Value | |
|
Vadim Sh.
2016/12/28 01:33:39
I'm not sure atomic.Value is necessary since you u
dnj
2016/12/28 03:01:06
Readers will grab this value w/out taking out a lo
| |
| 155 putLock *sync.Mutex | |
| 156 } | |
| 157 | |
| 158 // Name returns this Collection's name. | |
| 159 func (c *Collection) Name() string { return c.name } | |
| 160 | |
| 161 // IsReadOnly returns true if this Collection is read-only. | |
| 162 func (c *Collection) IsReadOnly() bool { return c.s.IsReadOnly() } | |
| 163 | |
| 164 // Min returns the smallest item in the collection. | |
| 165 func (c *Collection) Min() gtreap.Item { return c.currentRoot().Min() } | |
| 166 | |
| 167 // Max returns the largest item in the collection. | |
| 168 func (c *Collection) Max() gtreap.Item { return c.currentRoot().Max() } | |
| 169 | |
| 170 // Get returns the item in the Store that matches i, or nil if no such item | |
| 171 // exists. | |
| 172 func (c *Collection) Get(i gtreap.Item) gtreap.Item { return c.currentRoot().Get (i) } | |
| 173 | |
| 174 // Put adds an item to the Store. | |
| 175 // | |
| 176 // If the Store is read-only, Put will panic. | |
| 177 func (c *Collection) Put(i gtreap.Item) { | |
| 178 c.s.assertNotReadOnly() | |
| 179 | |
| 180 // Lock around the entire Upsert operation to serialize Puts. | |
| 181 c.putLock.Lock() | |
| 182 c.setRoot(c.currentRoot().Upsert(i, rand.Int())) | |
|
Vadim Sh.
2016/12/28 01:33:40
nit: do rand.Int() outside the lock
dnj
2016/12/28 03:01:06
um ... yes done. /ashamed
| |
| 183 c.putLock.Unlock() | |
| 184 } | |
| 185 | |
| 186 // Delete deletes an item from the Collection, if such an item exists. | |
| 187 func (c *Collection) Delete(i gtreap.Item) { | |
| 188 c.s.assertNotReadOnly() | |
| 189 | |
| 190 c.putLock.Lock() | |
| 191 c.setRoot(c.currentRoot().Delete(i)) | |
| 192 c.putLock.Unlock() | |
| 193 } | |
| 194 | |
| 195 func (c *Collection) currentRoot() *gtreap.Treap { | |
| 196 if c.root == nil { | |
| 197 panic("collection has no root") | |
| 198 } | |
| 199 return c.root.Load().(*gtreap.Treap) | |
| 200 } | |
| 201 | |
| 202 func (c *Collection) setRoot(root *gtreap.Treap) { | |
| 203 if c.root == nil { | |
| 204 c.root = &atomic.Value{} | |
| 205 } | |
| 206 c.root.Store(root) | |
| 207 } | |
| 208 | |
| 209 // Iterator returns an iterator over the Store, starting at the supplied pivot | |
| 210 // item. | |
| 211 func (c *Collection) Iterator(pivot gtreap.Item) *gtreap.Iterator { | |
| 212 return c.currentRoot().Iterator(pivot) | |
| 213 } | |
| OLD | NEW |