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 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