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 |