higgs 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,2 +1,5 @@
1
1
  please run `ruby mkrdoc.rb' or `rake rdoc', and see api/index.html by
2
2
  your browser.
3
+
4
+ Web: http://higgs.rubyforge.org
5
+ E-Mail: TOKI Yoshinori <toki@freedom.ne.jp>
@@ -1,8 +1,8 @@
1
1
  # = cache utilities
2
2
  #
3
3
  # Author:: $Author: toki $
4
- # Date:: $Date: 2007-10-31 00:36:09 +0900 (Wed, 31 Oct 2007) $
5
- # Revision:: $Revision: 659 $
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 659 2007-10-30 15:36:09Z toki $'
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 659 2007-10-30 15:36:09Z toki $'
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:
@@ -1,14 +1,13 @@
1
1
  # = multi-thread lock manager
2
2
  #
3
3
  # Author:: $Author: toki $
4
- # Date:: $Date: 2007-10-23 00:23:50 +0900 (Tue, 23 Oct 2007) $
5
- # Revision:: $Revision: 652 $
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 652 2007-10-22 15:23:50Z toki $'
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 TryLockTimeoutError < Error
27
+ class CollisionError < Error
29
28
  end
30
29
 
31
- SPIN_LOCK_COUNT = 100
32
- TRY_LOCK_LIMIT = 10
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
- def initialize(options={})
45
- @spin_lock_count = options[:spin_lock_count] || SPIN_LOCK_COUNT
46
- @try_lock_limit = options[:try_lock_limit] || TRY_LOCK_LIMIT
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
- attr_reader :spin_lock_count
52
- attr_reader :try_lock_limit
53
- attr_reader :try_lock_interval
37
+ def check_collision # :yields: key
38
+ self
39
+ end
54
40
 
55
- def new_rand(seed)
56
- @rand_gen.call(seed)
41
+ def critical
42
+ yield
43
+ end
57
44
  end
58
45
 
59
- def self.try_lock(lock, attrs)
60
- t0 = Time.now
61
- c = attrs.spin_lock_count
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
- if (attrs.try_lock_limit > 0) then
70
- rand = attrs.new_rand(::Thread.current.object_id ^ t0.to_i)
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
- class GiantLockManager < LockManager
86
- # for ident(1)
87
- CVS_ID = '$Id: lock.rb 652 2007-10-22 15:23:50Z toki $'
55
+ def check_collision # :yields: key
56
+ self
57
+ end
88
58
 
89
- def initialize(*args)
90
- super
91
- @rw_lock = ReadWriteLock.new
59
+ def critical
60
+ @critical_lock.synchronize{ yield }
61
+ end
92
62
  end
93
63
 
94
- class NoWorkLockHandler
95
- include Singleton
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 unlock(key)
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 transaction(read_only=false)
107
- if (read_only) then
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
- @rw_lock.write_lock.synchronize{
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 FineGrainLockManager < LockManager
103
+ class GiantLockManager < LockManager
129
104
  # for ident(1)
130
- CVS_ID = '$Id: lock.rb 652 2007-10-22 15:23:50Z toki $'
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
- @tx_lock = ReadWriteLock.new
135
- @cache = SharedWorkCache.new{|key| ReadWriteLock.new }
109
+ @write_lock = Mutex.new
136
110
  end
137
111
 
138
- class LockHandler
139
- def initialize(attrs, cache)
140
- @attrs = attrs
141
- @cache = cache
142
- @lock_map = {}
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
- raise "not locked key: #{key}"
118
+ @write_lock.synchronize{
119
+ r = yield(NoWorkLockHandler.instance)
120
+ }
154
121
  end
155
- self
156
- end
122
+ }
123
+ r
157
124
  end
125
+ end
158
126
 
159
- class ReadOnlyLockHandler < LockHandler
160
- def lock(key)
161
- r_lock = @cache[key].read_lock
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
- class ReadWriteLockHandler < LockHandler
169
- def lock(key)
170
- w_lock = @cache[key].write_lock
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
- begin
187
- r = yield(lock_handler)
188
- ensure
189
- for lock in lock_handler.lock_list
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