Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(664)

Side by Side Diff: common/data/treapstore/store.go

Issue 2607663002: Add treapstore, a treap-based in-memory data store (Closed)
Patch Set: Use treapstore for LogDog BigTable testing. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | common/data/treapstore/store_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « no previous file | common/data/treapstore/store_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698