higgs 0.1.0

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