higgs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/ChangeLog +208 -0
  2. data/LICENSE +26 -0
  3. data/README +2 -0
  4. data/Rakefile +75 -0
  5. data/bin/higgs_backup +67 -0
  6. data/bin/higgs_dump_index +43 -0
  7. data/bin/higgs_dump_jlog +42 -0
  8. data/bin/higgs_verify +37 -0
  9. data/lib/cgi/session/higgs.rb +72 -0
  10. data/lib/higgs/block.rb +192 -0
  11. data/lib/higgs/cache.rb +117 -0
  12. data/lib/higgs/dbm.rb +55 -0
  13. data/lib/higgs/exceptions.rb +31 -0
  14. data/lib/higgs/flock.rb +77 -0
  15. data/lib/higgs/index.rb +164 -0
  16. data/lib/higgs/jlog.rb +159 -0
  17. data/lib/higgs/lock.rb +189 -0
  18. data/lib/higgs/storage.rb +1086 -0
  19. data/lib/higgs/store.rb +228 -0
  20. data/lib/higgs/tar.rb +390 -0
  21. data/lib/higgs/thread.rb +370 -0
  22. data/lib/higgs/tman.rb +513 -0
  23. data/lib/higgs/utils/bman.rb +285 -0
  24. data/lib/higgs/utils.rb +22 -0
  25. data/lib/higgs/version.rb +21 -0
  26. data/lib/higgs.rb +59 -0
  27. data/misc/cache_bench/cache_bench.rb +43 -0
  28. data/misc/dbm_bench/.strc +8 -0
  29. data/misc/dbm_bench/Rakefile +78 -0
  30. data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
  31. data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
  32. data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
  33. data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
  34. data/misc/dbm_bench/dbm_seq_read.rb +45 -0
  35. data/misc/dbm_bench/dbm_seq_write.rb +44 -0
  36. data/misc/dbm_bench/st_verify.rb +28 -0
  37. data/misc/io_bench/cksum_bench.rb +48 -0
  38. data/misc/io_bench/jlog_bench.rb +71 -0
  39. data/misc/io_bench/write_bench.rb +128 -0
  40. data/misc/thread_bench/lock_bench.rb +132 -0
  41. data/mkrdoc.rb +8 -0
  42. data/rdoc.yml +13 -0
  43. data/sample/count.rb +60 -0
  44. data/sample/dbmtest.rb +38 -0
  45. data/test/Rakefile +45 -0
  46. data/test/run.rb +32 -0
  47. data/test/test_block.rb +163 -0
  48. data/test/test_cache.rb +214 -0
  49. data/test/test_cgi_session.rb +142 -0
  50. data/test/test_flock.rb +162 -0
  51. data/test/test_index.rb +258 -0
  52. data/test/test_jlog.rb +180 -0
  53. data/test/test_lock.rb +320 -0
  54. data/test/test_online_backup.rb +169 -0
  55. data/test/test_storage.rb +439 -0
  56. data/test/test_storage_conf.rb +202 -0
  57. data/test/test_storage_init_opts.rb +89 -0
  58. data/test/test_store.rb +211 -0
  59. data/test/test_tar.rb +432 -0
  60. data/test/test_thread.rb +541 -0
  61. data/test/test_tman.rb +875 -0
  62. data/test/test_tman_init_opts.rb +56 -0
  63. data/test/test_utils_bman.rb +234 -0
  64. metadata +115 -0
data/test/test_jlog.rb ADDED
@@ -0,0 +1,180 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'fileutils'
4
+ require 'higgs/block'
5
+ require 'higgs/jlog'
6
+ require 'test/unit'
7
+
8
+ module Higgs::Test
9
+ class JournalLoggerTest < Test::Unit::TestCase
10
+ include Higgs
11
+
12
+ # for ident(1)
13
+ CVS_ID = '$Id: test_jlog.rb 559 2007-09-25 15:20:20Z toki $'
14
+
15
+ def setup
16
+ @path = 't.jlog'
17
+ end
18
+
19
+ def teardown
20
+ FileUtils.rm_f(@path)
21
+ end
22
+
23
+ def test_jlog
24
+ log = JournalLogger.open(@path)
25
+ assert_equal(false, log.sync?)
26
+ assert_equal(0, log.size)
27
+ log.close
28
+
29
+ JournalLogger.each_log(@path) do |log|
30
+ assert_fail('not to reach')
31
+ end
32
+
33
+ log = JournalLogger.open(@path, false)
34
+ assert_equal(false, log.sync?)
35
+ log.write('foo')
36
+ assert_equal(1024, log.size)
37
+ log.close
38
+
39
+ count = 0
40
+ expected_values = [ 'foo' ]
41
+ JournalLogger.each_log(@path) do |log|
42
+ nth = "loop: #{count}"
43
+ assert(! expected_values.empty?, nth)
44
+ assert_equal(expected_values.shift, log, nth)
45
+ count += 1
46
+ end
47
+ assert(expected_values.empty?)
48
+
49
+ log = JournalLogger.open(@path, true)
50
+ assert_equal(true, log.sync?)
51
+ log.write(:bar)
52
+ assert_equal(2048, log.size)
53
+ log.close
54
+
55
+ count = 0
56
+ expected_values = [ 'foo', :bar ]
57
+ JournalLogger.each_log(@path) do |log|
58
+ nth = "loop: #{count}"
59
+ assert(! expected_values.empty?, nth)
60
+ assert_equal(expected_values.shift, log, nth)
61
+ count += 1
62
+ end
63
+ assert(expected_values.empty?)
64
+
65
+ log = JournalLogger.open(@path, false)
66
+ assert_equal(false, log.sync?)
67
+ log.write(777)
68
+ assert_equal(3072, log.size)
69
+ log.close
70
+
71
+ count = 0
72
+ expected_values = [ 'foo', :bar, 777 ]
73
+ JournalLogger.each_log(@path) do |log|
74
+ nth = "loop: #{count}"
75
+ assert(! expected_values.empty?, nth)
76
+ assert_equal(expected_values.shift, log, nth)
77
+ count += 1
78
+ end
79
+ assert(expected_values.empty?)
80
+
81
+ log = JournalLogger.open(@path, true)
82
+ assert_equal(true, log.sync?)
83
+ log.write('X' * Block::BLOCK_SIZE)
84
+ log.write('Y' * (Block::BLOCK_SIZE + 1))
85
+ log.close
86
+
87
+ count = 0
88
+ expected_values = [
89
+ 'foo', :bar, 777,
90
+ 'X' * Block::BLOCK_SIZE,
91
+ 'Y' * (Block::BLOCK_SIZE + 1)
92
+ ]
93
+ JournalLogger.each_log(@path) do |log|
94
+ nth = "loop: #{count}"
95
+ assert(! expected_values.empty?, nth)
96
+ assert_equal(expected_values.shift, log, nth)
97
+ count += 1
98
+ end
99
+ assert(expected_values.empty?)
100
+ end
101
+
102
+ def test_eof_mark
103
+ File.open(@path, 'w') {|w|
104
+ w.binmode
105
+ w << 'x' * Block::BLOCK_SIZE
106
+ JournalLogger.eof_mark(w)
107
+ }
108
+ assert_equal(true, (JournalLogger.has_eof_mark? @path))
109
+ end
110
+
111
+ def test_eof_mark_no_file
112
+ assert_equal(false, (File.exist? @path))
113
+ assert_equal(true, (JournalLogger.has_eof_mark? @path))
114
+ end
115
+
116
+ def test_eof_mark_empty_file
117
+ FileUtils.touch(@path)
118
+ assert_equal(false, (JournalLogger.has_eof_mark? @path))
119
+ end
120
+
121
+ def test_eof_mark_broken_file
122
+ File.open(@path, 'w') {|w|
123
+ w.binmode
124
+ w << 'x' * Block::BLOCK_SIZE
125
+ JournalLogger.eof_mark(w)
126
+ }
127
+ File.truncate(@path, File.stat(@path).size - 1)
128
+ assert_equal(false, (JournalLogger.has_eof_mark? @path))
129
+ end
130
+
131
+ def test_eof_mark_no_eof_mark
132
+ File.open(@path, 'w') {|w|
133
+ w.binmode
134
+ jlog = JournalLogger.new(w)
135
+ jlog.write(:no_EOF)
136
+ jlog.close(false)
137
+ }
138
+ assert_equal(false, (JournalLogger.has_eof_mark? @path))
139
+ end
140
+
141
+ def test_open_error
142
+ log = JournalLogger.open(@path)
143
+ log.write(:foo)
144
+ log.close(false)
145
+
146
+ assert_raise(RuntimeError) {
147
+ JournalLogger.open(@path)
148
+ }
149
+ end
150
+
151
+ def test_scan_log_error
152
+ log = JournalLogger.open(@path)
153
+ log.write(:foo)
154
+ log.write(:bar)
155
+ log.write(:baz)
156
+ log.close(false)
157
+ File.truncate(@path, File.stat(@path).size - 1)
158
+
159
+ File.open(@path) {|r|
160
+ r.binmode
161
+ count = 0
162
+ expected_values = [ :foo, :bar ]
163
+ assert_raise(Block::BrokenError) {
164
+ JournalLogger.scan_log(r) do |log|
165
+ nth = "loop: #{count}"
166
+ assert_equal(expected_values.shift, log, nth)
167
+ count += 1
168
+ end
169
+ }
170
+ assert(expected_values.empty?)
171
+ assert_equal(2048, r.tell)
172
+ }
173
+ end
174
+ end
175
+ end
176
+
177
+ # Local Variables:
178
+ # mode: Ruby
179
+ # indent-tabs-mode: nil
180
+ # End:
data/test/test_lock.rb ADDED
@@ -0,0 +1,320 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'higgs/lock'
4
+ require 'higgs/thread'
5
+ require 'test/unit'
6
+ require 'thwait'
7
+
8
+ module Higgs::Test
9
+ module LockManagerTest
10
+ include Higgs
11
+
12
+ # for ident(1)
13
+ CVS_ID = '$Id: test_lock.rb 559 2007-09-25 15:20:20Z toki $'
14
+
15
+ WORK_COUNT = 100
16
+ THREAD_COUNT = 10
17
+
18
+ def test_read_transaction_single_thread
19
+ v = "foo"
20
+ WORK_COUNT.times do
21
+ @lock_manager.transaction(true) {|lock_handler|
22
+ lock_handler.lock(:foo)
23
+ assert_equal("foo", v)
24
+ }
25
+ end
26
+ end
27
+
28
+ def test_write_transaction_single_thread
29
+ count = 0
30
+ WORK_COUNT.times do
31
+ @lock_manager.transaction{|lock_handler|
32
+ lock_handler.lock(:foo)
33
+ count += 1
34
+ }
35
+ end
36
+ assert_equal(WORK_COUNT, count)
37
+ end
38
+
39
+ def test_read_transaction_multithread
40
+ v = "foo"
41
+ th_grp = ThreadGroup.new
42
+ barrier = Barrier.new(THREAD_COUNT + 1)
43
+
44
+ THREAD_COUNT.times{|i|
45
+ th_grp.add Thread.new{
46
+ @lock_manager.transaction(true) {|lock_handler|
47
+ lock_handler.lock(:foo)
48
+ barrier.wait
49
+ WORK_COUNT.times do |j|
50
+ assert_equal("foo", v, "read transaction: #{i}.#{j}")
51
+ end
52
+ }
53
+ }
54
+ }
55
+
56
+ barrier.wait
57
+ for t in th_grp.list
58
+ t.join
59
+ end
60
+ end
61
+
62
+ def test_write_transaction_multithread
63
+ count = 0
64
+ th_grp = ThreadGroup.new
65
+ barrier = Barrier.new(THREAD_COUNT + 1)
66
+
67
+ THREAD_COUNT.times do
68
+ th_grp.add Thread.new{
69
+ barrier.wait
70
+ WORK_COUNT.times do
71
+ @lock_manager.transaction{|lock_handler|
72
+ lock_handler.lock(:foo)
73
+ count += 1
74
+ }
75
+ end
76
+ }
77
+ end
78
+
79
+ barrier.wait
80
+ for t in th_grp.list
81
+ t.join
82
+ end
83
+ assert_equal(THREAD_COUNT * WORK_COUNT, count)
84
+ end
85
+
86
+ def test_read_write_transaction_multithread
87
+ count = 0
88
+ th_grp = ThreadGroup.new
89
+ barrier = Barrier.new(THREAD_COUNT * 2 + 1)
90
+
91
+ THREAD_COUNT.times{|i|
92
+ th_grp.add Thread.new{
93
+ @lock_manager.transaction(true) {|lock_handler|
94
+ lock_handler.lock(:foo)
95
+ barrier.wait
96
+ WORK_COUNT.times do |j|
97
+ assert_equal(0, count, "read transaction: #{i}.#{j}")
98
+ end
99
+ }
100
+ }
101
+ }
102
+
103
+ THREAD_COUNT.times do
104
+ th_grp.add Thread.new{
105
+ barrier.wait
106
+ WORK_COUNT.times do
107
+ @lock_manager.transaction{|lock_handler|
108
+ lock_handler.lock(:foo)
109
+ count += 1
110
+ }
111
+ end
112
+ }
113
+ end
114
+
115
+ barrier.wait
116
+ for t in th_grp.list
117
+ t.join
118
+ end
119
+ assert_equal(THREAD_COUNT * WORK_COUNT, count)
120
+ end
121
+ end
122
+
123
+ class GiantLockManagerTest < Test::Unit::TestCase
124
+ include Higgs
125
+ include LockManagerTest
126
+
127
+ # for ident(1)
128
+ CVS_ID = '$Id: test_lock.rb 559 2007-09-25 15:20:20Z toki $'
129
+
130
+ def setup
131
+ @lock_manager = GiantLockManager.new
132
+ end
133
+
134
+ def test_write_read_transaction_multithread
135
+ count = 0
136
+ th_grp = ThreadGroup.new
137
+ barrier = Barrier.new(THREAD_COUNT + 2)
138
+
139
+ th_grp.add Thread.new{
140
+ @lock_manager.transaction{|lock_handler|
141
+ lock_handler.lock(:foo)
142
+ barrier.wait
143
+ THREAD_COUNT.times do
144
+ WORK_COUNT.times do
145
+ count += 1
146
+ end
147
+ end
148
+ }
149
+ }
150
+
151
+ THREAD_COUNT.times{|i|
152
+ th_grp.add Thread.new{
153
+ barrier.wait # different point from FineGrainLockManager
154
+ @lock_manager.transaction(true) {|lock_handler|
155
+ lock_handler.lock(:foo)
156
+ assert_equal(THREAD_COUNT * WORK_COUNT, count, "read transaction: #{i}")
157
+ }
158
+ }
159
+ }
160
+
161
+ barrier.wait
162
+ for t in th_grp.list
163
+ t.join
164
+ end
165
+ end
166
+ end
167
+
168
+ class FineGrainLockManagerTest < Test::Unit::TestCase
169
+ include Higgs
170
+ include LockManagerTest
171
+
172
+ # for ident(1)
173
+ CVS_ID = '$Id: test_lock.rb 559 2007-09-25 15:20:20Z toki $'
174
+
175
+ def setup
176
+ @lock_manager = FineGrainLockManager.new
177
+ end
178
+
179
+ def test_write_read_transaction_multithread
180
+ count = 0
181
+ th_grp = ThreadGroup.new
182
+ barrier = Barrier.new(THREAD_COUNT + 2)
183
+
184
+ th_grp.add Thread.new{
185
+ @lock_manager.transaction{|lock_handler|
186
+ lock_handler.lock(:foo)
187
+ barrier.wait
188
+ THREAD_COUNT.times do
189
+ WORK_COUNT.times do
190
+ count += 1
191
+ end
192
+ end
193
+ }
194
+ }
195
+
196
+ THREAD_COUNT.times{|i|
197
+ th_grp.add Thread.new{
198
+ @lock_manager.transaction(true) {|lock_handler|
199
+ barrier.wait # different point from GiantLockManager
200
+ lock_handler.lock(:foo)
201
+ assert_equal(THREAD_COUNT * WORK_COUNT, count, "read transaction: #{i}")
202
+ }
203
+ }
204
+ }
205
+
206
+ barrier.wait
207
+ for t in th_grp.list
208
+ t.join
209
+ end
210
+ end
211
+ end
212
+
213
+ class GiantLockManagerNoDeadLockTest < Test::Unit::TestCase
214
+ include Higgs
215
+
216
+ # for ident(1)
217
+ CVS_ID = '$Id: test_lock.rb 559 2007-09-25 15:20:20Z toki $'
218
+
219
+ WORK_COUNT = 1000
220
+
221
+ def setup
222
+ @lock_manager = GiantLockManager.new
223
+ end
224
+
225
+ def test_transaction_no_dead_lock
226
+ barrier = Barrier.new(3)
227
+
228
+ t1 = Thread.new{
229
+ barrier.wait
230
+ WORK_COUNT.times do
231
+ @lock_manager.transaction{|lock_handler|
232
+ lock_handler.lock(:foo)
233
+ lock_handler.lock(:bar)
234
+ }
235
+ end
236
+ }
237
+
238
+ t2 = Thread.new{
239
+ barrier.wait
240
+ WORK_COUNT.times do
241
+ @lock_manager.transaction{|lock_handler|
242
+ lock_handler.lock(:bar)
243
+ lock_handler.lock(:foo)
244
+ }
245
+ end
246
+ }
247
+
248
+ barrier.wait
249
+ t1.join
250
+ t2.join
251
+ end
252
+ end
253
+
254
+ class FineGrainLockManagerDeadLockTest < Test::Unit::TestCase
255
+ include Higgs
256
+
257
+ # for ident(1)
258
+ CVS_ID = '$Id: test_lock.rb 559 2007-09-25 15:20:20Z toki $'
259
+
260
+ def setup
261
+ @lock_manager = FineGrainLockManager.new(:spin_lock_count => 10,
262
+ :try_lock_limit => 0.1,
263
+ :try_lock_interval => 0.001)
264
+ end
265
+
266
+ def test_transaction_dead_lock
267
+ barrier = Barrier.new(3)
268
+
269
+ m1 = Mutex.new
270
+ end_of_t1 = false
271
+
272
+ m2 = Mutex.new
273
+ end_of_t2 = false
274
+
275
+ t1 = Thread.new{
276
+ barrier.wait
277
+ begin
278
+ until (m2.synchronize{ end_of_t2 })
279
+ @lock_manager.transaction{|lock_handler|
280
+ lock_handler.lock(:foo)
281
+ sleep(0.03)
282
+ lock_handler.lock(:bar)
283
+ }
284
+ end
285
+ ensure
286
+ m1.synchronize{ end_of_t1 = true }
287
+ end
288
+ }
289
+
290
+ t2 = Thread.new{
291
+ barrier.wait
292
+ begin
293
+ until (m1.synchronize{ end_of_t1 })
294
+ @lock_manager.transaction{|lock_handler|
295
+ lock_handler.lock(:bar)
296
+ sleep(0.07)
297
+ lock_handler.lock(:foo)
298
+ }
299
+ end
300
+ ensure
301
+ m2.synchronize{ end_of_t2 = true }
302
+ end
303
+ }
304
+
305
+ barrier.wait
306
+ assert_raise(LockManager::TryLockTimeoutError) {
307
+ t1.join
308
+ t2.join
309
+ }
310
+
311
+ # join threads without exception
312
+ ThreadsWait.all_waits(t1, t2)
313
+ end
314
+ end
315
+ end
316
+
317
+ # Local Variables:
318
+ # mode: Ruby
319
+ # indent-tabs-mode: nil
320
+ # End:
@@ -0,0 +1,169 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'drb'
4
+ require 'fileutils'
5
+ require 'higgs/index'
6
+ require 'higgs/storage'
7
+ require 'logger'
8
+ require 'test/unit'
9
+
10
+ module Higgs::Test
11
+ module OnlineBackupParams
12
+ STORAGE_ITEMS = (ENV['STORAGE_ITEMS'] || '100').to_i
13
+ COMMIT_ITEMS = (ENV['COMMIT_ITEMS'] || '10').to_i
14
+ MAX_ITEM_BYTES = (ENV['MAX_ITEM_BYTES'] || '16384').to_i
15
+ LEAST_COMMITS_PER_ROTATION = (ENV['LEAST_COMMITS_PER_ROTATION'] || '8').to_i
16
+ UPTIME_SECONDS = (ENV['UPTIME_SECONDS'] || '3').to_i
17
+ ITEM_CHARS = ('A'..'Z').to_a + ('a'..'z').to_a
18
+
19
+ if ($DEBUG) then
20
+ puts 'online backup test parameters...'
21
+ for name in constants
22
+ puts "#{name} = #{const_get(name)}"
23
+ end
24
+ puts ''
25
+ end
26
+ end
27
+
28
+ class OnlineBackupTest < Test::Unit::TestCase
29
+ include Higgs
30
+ include OnlineBackupParams
31
+
32
+ # for ident(1)
33
+ CVS_ID = '$Id: test_online_backup.rb 559 2007-09-25 15:20:20Z toki $'
34
+
35
+ def setup
36
+ srand(0)
37
+
38
+ @backup_dir = 'onbk_test_backup'
39
+ @backup_name = File.join(@backup_dir, 'foo')
40
+ FileUtils.rm_rf(@backup_dir) # for debug
41
+ FileUtils.mkdir_p(@backup_dir)
42
+
43
+ @restore_dir = 'onbk_test_restore'
44
+ @restore_name = File.join(@restore_dir, 'foo')
45
+ FileUtils.rm_rf(@restore_dir) # for debug
46
+ FileUtils.mkdir_p(@restore_dir)
47
+
48
+ @jlog_rotate_service_uri = 'druby://localhost:14142'
49
+
50
+ @start_latch = File.join(@backup_dir, '.start')
51
+ @stop_latch = File.join(@backup_dir, '.stop')
52
+ @stopped_latch = File.join(@backup_dir, '.stopped')
53
+ @end_latch = File.join(@backup_dir, '.end')
54
+ end
55
+
56
+ def teardown
57
+ FileUtils.rm_rf(@backup_dir) unless $DEBUG
58
+ FileUtils.rm_rf(@restore_dir) unless $DEBUG
59
+ end
60
+
61
+ def run_backup_storage
62
+ # step 0: storage starts with jlog_rotate_service_uri option
63
+ # (jlog_rotate_service_uri option is disabled by default)
64
+ st = Storage.new(@backup_name,
65
+ :jlog_rotate_max => 0,
66
+ :jlog_rotate_size => COMMIT_ITEMS * MAX_ITEM_BYTES * LEAST_COMMITS_PER_ROTATION,
67
+ :jlog_rotate_service_uri => @jlog_rotate_service_uri,
68
+ :logger => proc{|path|
69
+ logger = Logger.new(path, 1)
70
+ logger.level = Logger::DEBUG
71
+ logger
72
+ })
73
+
74
+ begin
75
+ FileUtils.touch(@start_latch)
76
+ until (File.exist? @stop_latch)
77
+ write_list = []
78
+ ope = [ :write, :system_properties, :custom_properties, :delete ][rand(3)]
79
+ key = rand(STORAGE_ITEMS)
80
+ case (ope)
81
+ when :write
82
+ value = rand(256).chr * rand(MAX_ITEM_BYTES)
83
+ write_list << [ ope, key, value ]
84
+ when :system_properties
85
+ next unless (st.key? key)
86
+ write_list << [ ope, key, { 'string_only' => [ true, false ][rand(2)] } ]
87
+ when :custom_properties
88
+ next unless (st.key? key)
89
+ value = ITEM_CHARS[rand(ITEM_CHARS.size)] * rand(MAX_ITEM_BYTES)
90
+ write_list << [ ope, key, { 'foo' => value } ]
91
+ when :delete
92
+ next unless (st.key? key)
93
+ write_list << [ ope, key ]
94
+ else
95
+ raise "unknown operation: #{ope}"
96
+ end
97
+ st.write_and_commit(write_list)
98
+ end
99
+ FileUtils.touch(@stopped_latch)
100
+ until (File.exist? @end_latch)
101
+ # spin lock
102
+ end
103
+ st.verify
104
+ ensure
105
+ st.shutdown
106
+ end
107
+ end
108
+ private :run_backup_storage
109
+
110
+ def test_online_backup
111
+ pid = fork{ run_backup_storage }
112
+ begin
113
+ until (File.exist? @start_latch)
114
+ # spin lock
115
+ end
116
+ jlog_rotate_service = DRbObject.new_with_uri(@jlog_rotate_service_uri)
117
+ sleep(UPTIME_SECONDS)
118
+
119
+ # step 1: backup index
120
+ jlog_rotate_service.call("#{@restore_name}.idx")
121
+
122
+ # step 2: backup data
123
+ FileUtils.cp("#{@backup_name}.tar", "#{@restore_name}.tar")
124
+
125
+ # transactions are stopped for comparison between original
126
+ # files and restored files. on real operation, transactions
127
+ # are not stopped.
128
+ FileUtils.touch(@stop_latch)
129
+ until (File.exist? @stopped_latch)
130
+ # spin lock
131
+ end
132
+
133
+ # step 3: rotate journal log
134
+ jlog_rotate_service.call(true)
135
+
136
+ # step 4: backup old journal logs
137
+ for path in Storage.rotate_entries("#{@backup_name}.jlog")
138
+ FileUtils.cp(path, File.join(@restore_dir, File.basename(path)))
139
+ FileUtils.rm(path)
140
+ end
141
+
142
+ # step 4: recover from backup
143
+ Storage.recover(@restore_name)
144
+
145
+ # recovered files are same as original files.
146
+ assert(FileUtils.cmp("#{@backup_name}.tar", "#{@restore_name}.tar"), 'tar')
147
+ assert_equal(Index.new.load("#{@backup_name}.idx").to_h,
148
+ Index.new.load("#{@restore_name}.idx").to_h, 'idx')
149
+ ensure
150
+ FileUtils.touch(@stop_latch)
151
+ FileUtils.touch(@end_latch)
152
+ Process.waitpid(pid)
153
+ end
154
+ assert_equal(0, $?.exitstatus)
155
+
156
+ st = Storage.new(@restore_name, :read_only => true)
157
+ begin
158
+ st.verify
159
+ ensure
160
+ st.shutdown
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ # Local Variables:
167
+ # mode: Ruby
168
+ # indent-tabs-mode: nil
169
+ # End: