higgs 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +82 -0
- data/README +3 -0
- data/lib/higgs/cache.rb +161 -4
- data/lib/higgs/lock.rb +72 -129
- data/lib/higgs/storage.rb +20 -19
- data/lib/higgs/thread.rb +14 -17
- data/lib/higgs/tman.rb +111 -55
- data/lib/higgs/version.rb +4 -4
- data/test/test_cache.rb +5 -3
- data/test/test_cache_mvcc.rb +444 -0
- data/test/test_lock.rb +126 -187
- data/test/test_replication.rb +2 -0
- data/test/test_storage.rb +4 -11
- data/test/test_tman.rb +22 -38
- data/test/test_tman_mvcc.rb +265 -0
- data/test/test_utils_bman.rb +3 -1
- metadata +4 -2
data/ChangeLog
CHANGED
@@ -1,3 +1,85 @@
|
|
1
|
+
2008-01-20 TOKI Yoshinori <toki@freedom.ne.jp>
|
2
|
+
|
3
|
+
* lib/higgs/version.rb: version 0.1.5.
|
4
|
+
|
5
|
+
* lib/higgs/tman.rb (Higgs::TransactionManager class): uncheck
|
6
|
+
nested transaction.
|
7
|
+
|
8
|
+
2008-01-14 TOKI Yoshinori <toki@freedom.ne.jp>
|
9
|
+
|
10
|
+
* merge from development branch of MVCC (higgs_develop_mvcc).
|
11
|
+
|
12
|
+
2008-01-11 TOKI Yoshinori <toki@freedom.ne.jp>
|
13
|
+
|
14
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): inner class name is
|
15
|
+
changed.
|
16
|
+
|
17
|
+
2008-01-09 TOKI Yoshinori <toki@freedom.ne.jp>
|
18
|
+
|
19
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): lock collision
|
20
|
+
reduced by read-write lock.
|
21
|
+
|
22
|
+
* lib/higgs/storage.rb (Higgs::Storage class): change number lock
|
23
|
+
for MVCC-cache access.
|
24
|
+
|
25
|
+
2008-01-05 TOKI Yoshinori <toki@freedom.ne.jp>
|
26
|
+
|
27
|
+
* lib/higgs/storage.rb (Higgs::Storage class): backing store on
|
28
|
+
update is obsolete. string_only method is obsolete.
|
29
|
+
|
30
|
+
* lib/higgs/tman.rb (Higgs::TransactionManager class): MVCC-transaction.
|
31
|
+
|
32
|
+
2008-01-04 TOKI Yoshinori <toki@freedom.ne.jp>
|
33
|
+
|
34
|
+
* lib/higgs/lock.rb: critical section in transaction.
|
35
|
+
|
36
|
+
* lib/higgs/lock.rb: Higgs::FineGrainLockManager class is
|
37
|
+
obsolete. Higgs::OptimisticLockManager class is new. lock of each
|
38
|
+
type. check_collision method is new. unlock method is obsolete.
|
39
|
+
|
40
|
+
2008-01-01 TOKI Yoshinori <toki@freedom.ne.jp>
|
41
|
+
|
42
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): resource of cache
|
43
|
+
accessor is releaseable in transaction.
|
44
|
+
|
45
|
+
2007-12-30 TOKI Yoshinori <toki@freedom.ne.jp>
|
46
|
+
|
47
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): added
|
48
|
+
write_old_values method to cache accessor. fixed a bug of each_key
|
49
|
+
method.
|
50
|
+
|
51
|
+
2007-12-24 TOKI Yoshinori <toki@freedom.ne.jp>
|
52
|
+
|
53
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): cache of each type.
|
54
|
+
|
55
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): MVCC-cache access
|
56
|
+
logic is implemented.
|
57
|
+
|
58
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class):
|
59
|
+
NOTE: MVCC-cache access order.
|
60
|
+
read access: 1. source storage. -> 2. cache entry.
|
61
|
+
write access: 1. cache entry. -> 2. source storage.
|
62
|
+
(it is assumed that multithreaded read-write access of ruby's Hash
|
63
|
+
is atomic)
|
64
|
+
|
65
|
+
* test/test_cache_mvcc.rb:
|
66
|
+
|
67
|
+
2007-12-05 TOKI Yoshinori <toki@freedom.ne.jp>
|
68
|
+
|
69
|
+
* lib/higgs/storage.rb (Higgs::Storage class): backing store on
|
70
|
+
update.
|
71
|
+
|
72
|
+
2007-12-01 TOKI Yoshinori <toki@freedom.ne.jp>
|
73
|
+
|
74
|
+
* lib/higgs/cache.rb (Higgs::MVCCCache class): cache for
|
75
|
+
Multi-Version Concurrency Control
|
76
|
+
|
77
|
+
* test/test_tman_mvcc.rb: test of MVCC for
|
78
|
+
Higgs::TransactionManager class.
|
79
|
+
|
80
|
+
* development branch of MVCC (higgs_develop_mvcc).
|
81
|
+
branched from trunk:r695.
|
82
|
+
|
1
83
|
2007-11-28 TOKI Yoshinori <toki@freedom.ne.jp>
|
2
84
|
|
3
85
|
* lib/higgs/version.rb: version 0.1.4.
|
data/README
CHANGED
data/lib/higgs/cache.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# = cache utilities
|
2
2
|
#
|
3
3
|
# Author:: $Author: toki $
|
4
|
-
# Date:: $Date:
|
5
|
-
# Revision:: $Revision:
|
4
|
+
# Date:: $Date: 2008-01-14 00:55:53 +0900 (Mon, 14 Jan 2008) $
|
5
|
+
# Revision:: $Revision: 742 $
|
6
6
|
#
|
7
7
|
# == license
|
8
8
|
# :include:LICENSE
|
@@ -16,7 +16,7 @@ module Higgs
|
|
16
16
|
# = cache by Least Recently Used strategy
|
17
17
|
class LRUCache
|
18
18
|
# for ident(1)
|
19
|
-
CVS_ID = '$Id: cache.rb
|
19
|
+
CVS_ID = '$Id: cache.rb 742 2008-01-13 15:55:53Z toki $'
|
20
20
|
|
21
21
|
extend Forwardable
|
22
22
|
|
@@ -73,7 +73,7 @@ module Higgs
|
|
73
73
|
# = multi-thread safe cache
|
74
74
|
class SharedWorkCache
|
75
75
|
# for ident(1)
|
76
|
-
CVS_ID = '$Id: cache.rb
|
76
|
+
CVS_ID = '$Id: cache.rb 742 2008-01-13 15:55:53Z toki $'
|
77
77
|
|
78
78
|
def initialize(cache={}, &work)
|
79
79
|
unless (work) then
|
@@ -109,6 +109,163 @@ module Higgs
|
|
109
109
|
r ? true : false
|
110
110
|
end
|
111
111
|
end
|
112
|
+
|
113
|
+
# = cache for Multi-Version Concurrency Control
|
114
|
+
class MVCCCache
|
115
|
+
extend Forwardable
|
116
|
+
|
117
|
+
def initialize
|
118
|
+
@lock = Mutex.new
|
119
|
+
@snapshots = {}
|
120
|
+
end
|
121
|
+
|
122
|
+
# for debug
|
123
|
+
def_delegator :@snapshots, :empty?
|
124
|
+
|
125
|
+
def write_old_values(cnum, write_list)
|
126
|
+
@lock.synchronize{
|
127
|
+
@snapshots.each_value do |snapshot|
|
128
|
+
lock = snapshot[:lock].write_lock
|
129
|
+
cache = snapshot[:cache]
|
130
|
+
lock.synchronize{
|
131
|
+
for ope, key, type, value in write_list
|
132
|
+
case (ope)
|
133
|
+
when :value
|
134
|
+
if (cache.key? key) then
|
135
|
+
if (cache[key] != :none && ! (cache[key].key? type)) then
|
136
|
+
cache[key][type] = { :value => value, :cnum => cnum }
|
137
|
+
end
|
138
|
+
else
|
139
|
+
cache[key] = { type => { :value => value, :cnum => cnum } }
|
140
|
+
end
|
141
|
+
when :none
|
142
|
+
unless (cache.key? key) then
|
143
|
+
cache[key] = :none
|
144
|
+
end
|
145
|
+
else
|
146
|
+
raise "unknown operation: #{ope}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
}
|
150
|
+
end
|
151
|
+
}
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
def ref_count_up(cnum)
|
156
|
+
@lock.synchronize{
|
157
|
+
@snapshots[cnum] = { :lock => ReadWriteLock.new, :cache => {}, :ref_count => 0 } unless (@snapshots.key? cnum)
|
158
|
+
@snapshots[cnum][:ref_count] += 1
|
159
|
+
return @snapshots[cnum][:lock].read_lock, @snapshots[cnum][:cache]
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
def ref_count_down(cnum)
|
164
|
+
@lock.synchronize{
|
165
|
+
@snapshots[cnum][:ref_count] -= 1
|
166
|
+
@snapshots.delete(cnum) if (@snapshots[cnum][:ref_count] == 0)
|
167
|
+
}
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
class Snapshot
|
172
|
+
def initialize(parent)
|
173
|
+
@parent = parent
|
174
|
+
end
|
175
|
+
|
176
|
+
def ref_count_up(cnum)
|
177
|
+
@cnum = cnum
|
178
|
+
@lock, @cache = @parent.ref_count_up(@cnum)
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
182
|
+
def ref_count_down
|
183
|
+
@cnum or raise 'not initialized'
|
184
|
+
@parent.ref_count_down(@cnum)
|
185
|
+
@cnum = @lock = @cache = nil
|
186
|
+
end
|
187
|
+
|
188
|
+
def change_number
|
189
|
+
@cnum
|
190
|
+
end
|
191
|
+
|
192
|
+
def cached?(key)
|
193
|
+
@cache.key? key
|
194
|
+
end
|
195
|
+
|
196
|
+
def cache_key?(key)
|
197
|
+
(@cache.key? key) && (@cache[key] != :none)
|
198
|
+
end
|
199
|
+
|
200
|
+
def each_cache_key
|
201
|
+
@cache.each_key do |key|
|
202
|
+
if (@cache[key] != :none) then
|
203
|
+
yield(key)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
|
209
|
+
def key?(store, key)
|
210
|
+
(store.key? key) && ! (cached? key) || (cache_key? key)
|
211
|
+
end
|
212
|
+
|
213
|
+
def keys(store)
|
214
|
+
key_set = {}
|
215
|
+
store.each_key do |key|
|
216
|
+
unless (cached? key) then
|
217
|
+
key_set[key] = true
|
218
|
+
end
|
219
|
+
end
|
220
|
+
each_cache_key do |key|
|
221
|
+
key_set[key] = true
|
222
|
+
end
|
223
|
+
key_set.keys
|
224
|
+
end
|
225
|
+
|
226
|
+
def each_key(store)
|
227
|
+
for key in keys(store)
|
228
|
+
yield(key)
|
229
|
+
end
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
def fetch(key, type)
|
234
|
+
@lock.synchronize{
|
235
|
+
if (@cache.key? key) then
|
236
|
+
if (@cache[key] != :none) then
|
237
|
+
if (cache_entry = @cache[key][type]) then
|
238
|
+
cache_entry[:value]
|
239
|
+
else
|
240
|
+
yield
|
241
|
+
end
|
242
|
+
else
|
243
|
+
nil
|
244
|
+
end
|
245
|
+
else
|
246
|
+
yield
|
247
|
+
end
|
248
|
+
}
|
249
|
+
end
|
250
|
+
|
251
|
+
def write_old_values(write_list)
|
252
|
+
@parent.write_old_values(@cnum, write_list)
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def transaction(cnum)
|
258
|
+
r = nil
|
259
|
+
snapshot = Snapshot.new(self)
|
260
|
+
snapshot.ref_count_up(cnum)
|
261
|
+
begin
|
262
|
+
r = yield(snapshot)
|
263
|
+
ensure
|
264
|
+
snapshot.ref_count_down
|
265
|
+
end
|
266
|
+
r
|
267
|
+
end
|
268
|
+
end
|
112
269
|
end
|
113
270
|
|
114
271
|
# Local Variables:
|
data/lib/higgs/lock.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
# = multi-thread lock manager
|
2
2
|
#
|
3
3
|
# Author:: $Author: toki $
|
4
|
-
# Date:: $Date:
|
5
|
-
# Revision:: $Revision:
|
4
|
+
# Date:: $Date: 2008-01-14 00:55:53 +0900 (Mon, 14 Jan 2008) $
|
5
|
+
# Revision:: $Revision: 742 $
|
6
6
|
#
|
7
7
|
# == license
|
8
8
|
# :include:LICENSE
|
9
9
|
#
|
10
10
|
|
11
|
-
require 'forwardable'
|
12
11
|
require 'higgs/cache'
|
13
12
|
require 'higgs/exceptions'
|
14
13
|
require 'singleton'
|
@@ -18,187 +17,131 @@ module Higgs
|
|
18
17
|
# = multi-thread lock manager
|
19
18
|
class LockManager
|
20
19
|
# for ident(1)
|
21
|
-
CVS_ID = '$Id: lock.rb
|
20
|
+
CVS_ID = '$Id: lock.rb 742 2008-01-13 15:55:53Z toki $'
|
22
21
|
|
23
22
|
include Exceptions
|
24
23
|
|
25
24
|
class Error < HiggsError
|
26
25
|
end
|
27
26
|
|
28
|
-
class
|
27
|
+
class CollisionError < Error
|
29
28
|
end
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
TRY_LOCK_INTERVAL = 0.1
|
34
|
-
|
35
|
-
RAND_GEN = proc{|seed|
|
36
|
-
n = seed
|
37
|
-
cycle = 0xFFFF
|
38
|
-
proc{
|
39
|
-
n = (n * 37549 + 12345) % cycle
|
40
|
-
n.to_f / cycle
|
41
|
-
}
|
42
|
-
}
|
30
|
+
class NoWorkLockHandler
|
31
|
+
include Singleton
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@try_lock_interval = options[:try_lock_interval] || TRY_LOCK_INTERVAL
|
48
|
-
@rand_gen = options[:random_number_generator] || RAND_GEN
|
49
|
-
end
|
33
|
+
def lock(key, type, cnum)
|
34
|
+
self
|
35
|
+
end
|
50
36
|
|
51
|
-
|
52
|
-
|
53
|
-
|
37
|
+
def check_collision # :yields: key
|
38
|
+
self
|
39
|
+
end
|
54
40
|
|
55
|
-
|
56
|
-
|
41
|
+
def critical
|
42
|
+
yield
|
43
|
+
end
|
57
44
|
end
|
58
45
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
while (c > 0)
|
63
|
-
if (lock.try_lock) then
|
64
|
-
return
|
65
|
-
end
|
66
|
-
c -= 1
|
46
|
+
class CriticalRegionLockHandler
|
47
|
+
def initialize(critical_lock)
|
48
|
+
@critical_lock = critical_lock
|
67
49
|
end
|
68
50
|
|
69
|
-
|
70
|
-
|
71
|
-
while (Time.now - t0 < attrs.try_lock_limit)
|
72
|
-
if (lock.try_lock) then
|
73
|
-
return
|
74
|
-
end
|
75
|
-
sleep(attrs.try_lock_interval * rand.call)
|
76
|
-
end
|
77
|
-
raise TryLockTimeoutError, 'expired'
|
78
|
-
else
|
79
|
-
# brave man who doesn't fear deadlock.
|
80
|
-
lock.lock
|
51
|
+
def lock(key, type, cnum)
|
52
|
+
self
|
81
53
|
end
|
82
|
-
end
|
83
|
-
end
|
84
54
|
|
85
|
-
|
86
|
-
|
87
|
-
|
55
|
+
def check_collision # :yields: key
|
56
|
+
self
|
57
|
+
end
|
88
58
|
|
89
|
-
|
90
|
-
|
91
|
-
|
59
|
+
def critical
|
60
|
+
@critical_lock.synchronize{ yield }
|
61
|
+
end
|
92
62
|
end
|
93
63
|
|
94
|
-
class
|
95
|
-
|
64
|
+
class CollisionCheckLockHandler < CriticalRegionLockHandler
|
65
|
+
def initialize(*args)
|
66
|
+
super
|
67
|
+
@cnum_map = {}
|
68
|
+
end
|
96
69
|
|
97
|
-
def lock(key)
|
70
|
+
def lock(key, type, cnum)
|
71
|
+
key_pair = [ key, type ]
|
72
|
+
if (@cnum_map[key_pair] && @cnum_map[key_pair] != cnum) then
|
73
|
+
raise "unexpected changed cnum at`#{key}(#{type})': #{@cnum_map[key].inspect} -> #{cnum.inspect}"
|
74
|
+
end
|
75
|
+
@cnum_map[key_pair] = cnum
|
98
76
|
self
|
99
77
|
end
|
100
78
|
|
101
|
-
def
|
79
|
+
def check_collision
|
80
|
+
for (key, type), cnum in @cnum_map
|
81
|
+
last_cnum = yield(key, type)
|
82
|
+
if (cnum != last_cnum) then
|
83
|
+
raise CollisionError, "`#{key}(#{type})' is changed (cnum: #{cnum.inspect} -> #{last_cnum.inspect}) by other transaction and this transaction may be retried."
|
84
|
+
end
|
85
|
+
end
|
102
86
|
self
|
103
87
|
end
|
104
88
|
end
|
105
89
|
|
106
|
-
def
|
107
|
-
|
108
|
-
lock = @rw_lock.read_lock
|
109
|
-
else
|
110
|
-
lock = @rw_lock.write_lock
|
111
|
-
end
|
112
|
-
r = nil
|
113
|
-
lock.synchronize{
|
114
|
-
r = yield(NoWorkLockHandler.instance)
|
115
|
-
}
|
116
|
-
r
|
90
|
+
def initialize
|
91
|
+
@tx_lock = ReadWriteLock.new
|
117
92
|
end
|
118
93
|
|
119
94
|
def exclusive
|
120
95
|
r = nil
|
121
|
-
@
|
96
|
+
@tx_lock.write_lock.synchronize{
|
122
97
|
r = yield
|
123
98
|
}
|
124
99
|
r
|
125
100
|
end
|
126
101
|
end
|
127
102
|
|
128
|
-
class
|
103
|
+
class GiantLockManager < LockManager
|
129
104
|
# for ident(1)
|
130
|
-
CVS_ID = '$Id: lock.rb
|
105
|
+
CVS_ID = '$Id: lock.rb 742 2008-01-13 15:55:53Z toki $'
|
131
106
|
|
132
107
|
def initialize(*args)
|
133
108
|
super
|
134
|
-
@
|
135
|
-
@cache = SharedWorkCache.new{|key| ReadWriteLock.new }
|
109
|
+
@write_lock = Mutex.new
|
136
110
|
end
|
137
111
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
144
|
-
|
145
|
-
def lock_list
|
146
|
-
@lock_map.values
|
147
|
-
end
|
148
|
-
|
149
|
-
def unlock(key)
|
150
|
-
if (lock = @lock_map.delete(key)) then
|
151
|
-
lock.unlock
|
112
|
+
def transaction(read_only=false)
|
113
|
+
r = nil
|
114
|
+
@tx_lock.read_lock.synchronize{
|
115
|
+
if (read_only) then
|
116
|
+
r = yield(NoWorkLockHandler.instance)
|
152
117
|
else
|
153
|
-
|
118
|
+
@write_lock.synchronize{
|
119
|
+
r = yield(NoWorkLockHandler.instance)
|
120
|
+
}
|
154
121
|
end
|
155
|
-
|
156
|
-
|
122
|
+
}
|
123
|
+
r
|
157
124
|
end
|
125
|
+
end
|
158
126
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
LockManager.try_lock(r_lock, @attrs)
|
163
|
-
@lock_map[key] = r_lock
|
164
|
-
self
|
165
|
-
end
|
166
|
-
end
|
127
|
+
class OptimisticLockManager < LockManager
|
128
|
+
# for ident(1)
|
129
|
+
CVS_ID = '$Id: lock.rb 742 2008-01-13 15:55:53Z toki $'
|
167
130
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
LockManager.try_lock(w_lock, @attrs)
|
172
|
-
@lock_map[key] = w_lock
|
173
|
-
self
|
174
|
-
end
|
131
|
+
def initialize(*args)
|
132
|
+
super
|
133
|
+
@critical_lock = Mutex.new
|
175
134
|
end
|
176
135
|
|
177
136
|
def transaction(read_only=false)
|
178
|
-
if (read_only) then
|
179
|
-
lock_handler = ReadOnlyLockHandler.new(self, @cache)
|
180
|
-
else
|
181
|
-
lock_handler = ReadWriteLockHandler.new(self, @cache)
|
182
|
-
end
|
183
|
-
|
184
137
|
r = nil
|
185
138
|
@tx_lock.read_lock.synchronize{
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
lock.unlock
|
191
|
-
end
|
139
|
+
if (read_only) then
|
140
|
+
lock_handler = CriticalRegionLockHandler.new(@critical_lock)
|
141
|
+
else
|
142
|
+
lock_handler = CollisionCheckLockHandler.new(@critical_lock)
|
192
143
|
end
|
193
|
-
|
194
|
-
|
195
|
-
r
|
196
|
-
end
|
197
|
-
|
198
|
-
def exclusive
|
199
|
-
r = nil
|
200
|
-
@tx_lock.write_lock.synchronize{
|
201
|
-
r = yield
|
144
|
+
r = yield(lock_handler)
|
202
145
|
}
|
203
146
|
r
|
204
147
|
end
|