perobs 4.1.0 → 4.2.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.
- checksums.yaml +5 -5
- data/lib/perobs/BTree.rb +33 -13
- data/lib/perobs/BTreeBlob.rb +3 -2
- data/lib/perobs/BTreeDB.rb +4 -3
- data/lib/perobs/BTreeNode.rb +107 -78
- data/lib/perobs/BTreeNodeLink.rb +10 -0
- data/lib/perobs/BigArray.rb +19 -1
- data/lib/perobs/BigArrayNode.rb +13 -9
- data/lib/perobs/BigHash.rb +8 -24
- data/lib/perobs/BigTree.rb +14 -1
- data/lib/perobs/BigTreeNode.rb +2 -2
- data/lib/perobs/Cache.rb +31 -6
- data/lib/perobs/EquiBlobsFile.rb +12 -1
- data/lib/perobs/FlatFile.rb +197 -45
- data/lib/perobs/FlatFileBlobHeader.rb +20 -5
- data/lib/perobs/FlatFileDB.rb +8 -4
- data/lib/perobs/FuzzyStringMatcher.rb +192 -0
- data/lib/perobs/Hash.rb +4 -0
- data/lib/perobs/IDListPageFile.rb +1 -2
- data/lib/perobs/ObjectBase.rb +1 -1
- data/lib/perobs/PersistentObjectCache.rb +7 -4
- data/lib/perobs/SpaceManager.rb +273 -0
- data/lib/perobs/SpaceTree.rb +1 -1
- data/lib/perobs/Store.rb +67 -25
- data/lib/perobs/version.rb +1 -1
- data/perobs.gemspec +2 -2
- data/test/BTree_spec.rb +1 -0
- data/test/BigArray_spec.rb +53 -6
- data/test/BigHash_spec.rb +8 -0
- data/test/FlatFileDB_spec.rb +108 -3
- data/test/FuzzyStringMatcher_spec.rb +171 -0
- data/test/LegacyDBs/LegacyDB.rb +4 -0
- data/test/SpaceManager_spec.rb +176 -0
- data/test/Store_spec.rb +2 -5
- metadata +12 -6
data/lib/perobs/SpaceTree.rb
CHANGED
@@ -54,7 +54,7 @@ module PEROBS
|
|
54
54
|
|
55
55
|
# Benchmark runs showed a cache size of 128 to be a good compromise
|
56
56
|
# between read and write performance trade-offs and memory consumption.
|
57
|
-
@cache = PersistentObjectCache.new(
|
57
|
+
@cache = PersistentObjectCache.new(256, -1, SpaceTreeNode, self)
|
58
58
|
end
|
59
59
|
|
60
60
|
# Open the SpaceTree file.
|
data/lib/perobs/Store.rb
CHANGED
@@ -46,8 +46,9 @@ require 'perobs/ConsoleProgressMeter'
|
|
46
46
|
# PErsistent Ruby OBject Store
|
47
47
|
module PEROBS
|
48
48
|
|
49
|
-
Statistics = Struct.new(:in_memory_objects, :root_objects,
|
50
|
-
:marked_objects, :swept_objects
|
49
|
+
Statistics = Struct.new(:in_memory_objects, :root_objects, :zombie_objects,
|
50
|
+
:marked_objects, :swept_objects,
|
51
|
+
:created_objects, :collected_objects)
|
51
52
|
|
52
53
|
# PEROBS::Store is a persistent storage system for Ruby objects. Regular
|
53
54
|
# Ruby objects are transparently stored in a back-end storage and retrieved
|
@@ -109,6 +110,7 @@ module PEROBS
|
|
109
110
|
class Store
|
110
111
|
|
111
112
|
attr_reader :db, :cache, :class_map
|
113
|
+
attr_writer :root_objects
|
112
114
|
|
113
115
|
# Create a new Store.
|
114
116
|
# @param data_base [String] the name of the database
|
@@ -143,6 +145,9 @@ module PEROBS
|
|
143
145
|
# It defaults to ProgressMeter which only logs into
|
144
146
|
# the log. Use ConsoleProgressMeter or a derived
|
145
147
|
# class for more fancy progress reporting.
|
148
|
+
# :no_root_objects : Create a new store without root objects. This only
|
149
|
+
# makes sense if you want to copy the objects of
|
150
|
+
# another store into this store.
|
146
151
|
def initialize(data_base, options = {})
|
147
152
|
# Create a backing store handler
|
148
153
|
@progressmeter = (options[:progressmeter] ||= ProgressMeter.new)
|
@@ -155,25 +160,32 @@ module PEROBS
|
|
155
160
|
# List of PEROBS objects that are currently available as Ruby objects
|
156
161
|
# hashed by their ID.
|
157
162
|
@in_memory_objects = {}
|
163
|
+
# List of objects that were destroyed already but were still found in
|
164
|
+
# the in_memory_objects list. _collect has not yet been called for them.
|
165
|
+
@zombie_objects = {}
|
158
166
|
|
159
167
|
# This objects keeps some counters of interest.
|
160
168
|
@stats = Statistics.new
|
169
|
+
@stats[:created_objects] = 0
|
170
|
+
@stats[:collected_objects] = 0
|
161
171
|
|
162
172
|
# The Cache reduces read and write latencies by keeping a subset of the
|
163
173
|
# objects in memory.
|
164
174
|
@cache = Cache.new(options[:cache_bits] || 16)
|
165
175
|
|
166
176
|
# The named (global) objects IDs hashed by their name
|
167
|
-
unless
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
"
|
177
|
+
unless options[:no_root_objects]
|
178
|
+
unless (@root_objects = object_by_id(0))
|
179
|
+
PEROBS.log.debug "Initializing the PEROBS store"
|
180
|
+
# The root object hash always has the object ID 0.
|
181
|
+
@root_objects = _construct_po(Hash, 0)
|
182
|
+
# Mark the root_objects object as modified.
|
183
|
+
@cache.cache_write(@root_objects)
|
184
|
+
end
|
185
|
+
unless @root_objects.is_a?(Hash)
|
186
|
+
PEROBS.log.fatal "Database corrupted: Root objects must be a Hash " +
|
187
|
+
"but is a #{@root_objects.class}"
|
188
|
+
end
|
177
189
|
end
|
178
190
|
end
|
179
191
|
|
@@ -185,7 +197,9 @@ module PEROBS
|
|
185
197
|
sync
|
186
198
|
|
187
199
|
# Create a new store with the specified directory and options.
|
188
|
-
|
200
|
+
new_options = options.clone
|
201
|
+
new_options[:no_root_objects] = true
|
202
|
+
new_db = Store.new(dir, new_options)
|
189
203
|
# Clear the cache.
|
190
204
|
new_db.sync
|
191
205
|
# Copy all objects of the existing store to the new store.
|
@@ -196,6 +210,7 @@ module PEROBS
|
|
196
210
|
obj._sync
|
197
211
|
i += 1
|
198
212
|
end
|
213
|
+
new_db.root_objects = new_db.object_by_id(0)
|
199
214
|
PEROBS.log.debug "Copied #{i} objects into new database at #{dir}"
|
200
215
|
# Flush the new store and close it.
|
201
216
|
new_db.exit
|
@@ -203,7 +218,6 @@ module PEROBS
|
|
203
218
|
true
|
204
219
|
end
|
205
220
|
|
206
|
-
|
207
221
|
# Close the store and ensure that all in-memory objects are written out to
|
208
222
|
# the storage backend. The Store object is no longer usable after this
|
209
223
|
# method was called.
|
@@ -216,10 +230,22 @@ module PEROBS
|
|
216
230
|
end
|
217
231
|
@cache.flush if @cache
|
218
232
|
@db.close if @db
|
219
|
-
@db = @class_map = @in_memory_objects = @stats = @cache = @root_objects =
|
220
|
-
nil
|
221
|
-
end
|
222
233
|
|
234
|
+
GC.start
|
235
|
+
if @stats
|
236
|
+
unless @stats[:created_objects] == @stats[:collected_objects] +
|
237
|
+
@in_memory_objects.length
|
238
|
+
PEROGS.log.fatal "Created objects count " +
|
239
|
+
"(#{@stats[:created_objects]})" +
|
240
|
+
" is not equal to the collected count " +
|
241
|
+
"(#{@stats[:collected_objects]}) + in_memory_objects count " +
|
242
|
+
"(#{@in_memory_objects.length})"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
@db = @class_map = @in_memory_objects = @zombie_objects =
|
247
|
+
@stats = @cache = @root_objects = nil
|
248
|
+
end
|
223
249
|
|
224
250
|
# You need to call this method to create new PEROBS objects that belong to
|
225
251
|
# this Store.
|
@@ -255,8 +281,8 @@ module PEROBS
|
|
255
281
|
# deletes the entire database.
|
256
282
|
def delete_store
|
257
283
|
@db.delete_database
|
258
|
-
@db = @class_map = @in_memory_objects = @
|
259
|
-
nil
|
284
|
+
@db = @class_map = @in_memory_objects = @zombie_objects =
|
285
|
+
@stats = @cache = @root_objects = nil
|
260
286
|
end
|
261
287
|
|
262
288
|
# Store the provided object under the given name. Use this to make the
|
@@ -358,10 +384,10 @@ module PEROBS
|
|
358
384
|
rescue RangeError => e
|
359
385
|
# Due to a race condition the object can still be in the
|
360
386
|
# @in_memory_objects list but has been collected already by the Ruby
|
361
|
-
# GC. In that case we need to load it again.
|
362
|
-
#
|
363
|
-
#
|
364
|
-
@in_memory_objects.delete(id)
|
387
|
+
# GC. In that case we need to load it again. The _collect() call
|
388
|
+
# will happen much later, potentially after we have registered a new
|
389
|
+
# object with the same ID.
|
390
|
+
@zombie_objects[id] = @in_memory_objects.delete(id)
|
365
391
|
end
|
366
392
|
end
|
367
393
|
|
@@ -510,7 +536,16 @@ module PEROBS
|
|
510
536
|
# @param obj [BasicObject] Object to register
|
511
537
|
# @param id [Integer] object ID
|
512
538
|
def _register_in_memory(obj, id)
|
539
|
+
unless obj.is_a?(ObjectBase)
|
540
|
+
PEROBS.log.fatal "You can only register ObjectBase objects"
|
541
|
+
end
|
542
|
+
if @in_memory_objects.include?(id)
|
543
|
+
PEROBS.log.fatal "The Store::_in_memory_objects list already " +
|
544
|
+
"contains an object for ID #{id}"
|
545
|
+
end
|
546
|
+
|
513
547
|
@in_memory_objects[id] = obj.object_id
|
548
|
+
@stats[:created_objects] += 1
|
514
549
|
end
|
515
550
|
|
516
551
|
# Remove the object from the in-memory list. This is an internal method
|
@@ -520,6 +555,10 @@ module PEROBS
|
|
520
555
|
def _collect(id, ruby_object_id)
|
521
556
|
if @in_memory_objects[id] == ruby_object_id
|
522
557
|
@in_memory_objects.delete(id)
|
558
|
+
@stats[:collected_objects] += 1
|
559
|
+
elsif @zombie_objects[id] == ruby_object_id
|
560
|
+
@zombie_objects.delete(id)
|
561
|
+
@stats[:collected_objects] += 1
|
523
562
|
end
|
524
563
|
end
|
525
564
|
|
@@ -527,6 +566,7 @@ module PEROBS
|
|
527
566
|
def statistics
|
528
567
|
@stats.in_memory_objects = @in_memory_objects.length
|
529
568
|
@stats.root_objects = @root_objects.length
|
569
|
+
@stats.zombie_objects = @zombie_objects.length
|
530
570
|
|
531
571
|
@stats
|
532
572
|
end
|
@@ -556,8 +596,10 @@ module PEROBS
|
|
556
596
|
# Sweep phase of a mark-and-sweep garbage collector. It will remove all
|
557
597
|
# unmarked objects from the store.
|
558
598
|
def sweep
|
559
|
-
@stats.swept_objects = @db.delete_unmarked_objects
|
560
|
-
|
599
|
+
@stats.swept_objects = @db.delete_unmarked_objects do |id|
|
600
|
+
@cache.evict(id)
|
601
|
+
end
|
602
|
+
GC.start
|
561
603
|
PEROBS.log.debug "#{@stats.swept_objects} objects collected"
|
562
604
|
@stats.swept_objects
|
563
605
|
end
|
data/lib/perobs/version.rb
CHANGED
data/perobs.gemspec
CHANGED
@@ -16,9 +16,9 @@ GEM_SPEC = Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
|
-
spec.required_ruby_version = '>=2.
|
19
|
+
spec.required_ruby_version = '>=2.4'
|
20
20
|
|
21
21
|
spec.add_development_dependency 'bundler', '~> 2.3'
|
22
22
|
spec.add_development_dependency 'yard', '~>0.9.12'
|
23
|
-
spec.add_development_dependency 'rake', '~>
|
23
|
+
spec.add_development_dependency 'rake', '~> 12.3.3'
|
24
24
|
end
|
data/test/BTree_spec.rb
CHANGED
data/test/BigArray_spec.rb
CHANGED
@@ -32,7 +32,7 @@ require 'spec_helper'
|
|
32
32
|
require 'perobs'
|
33
33
|
require 'perobs/BigArray'
|
34
34
|
|
35
|
-
NODE_ENTRIES =
|
35
|
+
NODE_ENTRIES = 6
|
36
36
|
|
37
37
|
describe PEROBS::BigArray do
|
38
38
|
|
@@ -59,12 +59,16 @@ describe PEROBS::BigArray do
|
|
59
59
|
expect(@a.length).to eq(0)
|
60
60
|
expect(@a.check).to be true
|
61
61
|
expect(@a[0]).to be nil
|
62
|
+
expect(@a.first).to be nil
|
63
|
+
expect(@a.last).to be nil
|
62
64
|
end
|
63
65
|
|
64
66
|
it 'should append the first element' do
|
65
67
|
@a << 0
|
66
68
|
expect(@a.empty?).to be false
|
67
69
|
expect(@a[0]).to eq(0)
|
70
|
+
expect(@a.first).to eq(0)
|
71
|
+
expect(@a.last).to eq(0)
|
68
72
|
expect(@a.length).to eq(1)
|
69
73
|
expect(@a.check).to be true
|
70
74
|
end
|
@@ -75,6 +79,8 @@ describe PEROBS::BigArray do
|
|
75
79
|
expect(@a.check).to be true
|
76
80
|
expect(@a.length).to eq(i + 1)
|
77
81
|
end
|
82
|
+
expect(@a.first).to eq(0)
|
83
|
+
expect(@a.last).to eq(10 * NODE_ENTRIES - 1)
|
78
84
|
end
|
79
85
|
|
80
86
|
it 'should insert at 0' do
|
@@ -111,7 +117,7 @@ describe PEROBS::BigArray do
|
|
111
117
|
|
112
118
|
it 'should support the [] operator' do
|
113
119
|
expect(@a[0]).to be nil
|
114
|
-
expect
|
120
|
+
expect(@a[-1]).to be nil
|
115
121
|
@a[0] = 0
|
116
122
|
expect(@a[0]).to eq(0)
|
117
123
|
1.upto(3 * NODE_ENTRIES) do |i|
|
@@ -124,7 +130,7 @@ describe PEROBS::BigArray do
|
|
124
130
|
0.upto(3 * NODE_ENTRIES) do |i|
|
125
131
|
expect(@a[-3 * NODE_ENTRIES + i - 1]).to eq(i)
|
126
132
|
end
|
127
|
-
expect
|
133
|
+
expect(@a[-3 * NODE_ENTRIES - 2]).to be nil
|
128
134
|
(3 * NODE_ENTRIES + 1).upto(4 * NODE_ENTRIES) do |i|
|
129
135
|
expect(@a[i]).to be nil
|
130
136
|
end
|
@@ -142,7 +148,7 @@ describe PEROBS::BigArray do
|
|
142
148
|
expect(@a.length).to eq(0)
|
143
149
|
expect(@a.check).to be true
|
144
150
|
|
145
|
-
n =
|
151
|
+
n = 5 * NODE_ENTRIES
|
146
152
|
0.upto(n) { |i| @a.insert(i, i) }
|
147
153
|
0.upto(n) do |i|
|
148
154
|
expect(@a.delete_at(0)).to eql(i)
|
@@ -155,20 +161,52 @@ describe PEROBS::BigArray do
|
|
155
161
|
expect(@a.check).to be true
|
156
162
|
end
|
157
163
|
|
158
|
-
n =
|
164
|
+
n = 15 * NODE_ENTRIES
|
159
165
|
0.upto(n - 1) { |i| @a.insert(i, i) }
|
160
166
|
expect(@a.delete_at(n + 2)).to be nil
|
161
167
|
expect(@a.delete_at(-(n + 2))).to be nil
|
162
168
|
expect(@a.size).to eql(n)
|
163
169
|
|
164
170
|
n.times do |i|
|
165
|
-
|
171
|
+
idx = rand(@a.size)
|
172
|
+
@a.delete_at(idx)
|
166
173
|
expect(@a.size).to be (n - 1 - i)
|
167
174
|
expect(@a.check).to be true
|
168
175
|
end
|
169
176
|
expect(@a.size).to eql(0)
|
170
177
|
end
|
171
178
|
|
179
|
+
it 'should fill the gaps' do
|
180
|
+
1.upto(4) do |i|
|
181
|
+
idx = i * NODE_ENTRIES * NODE_ENTRIES
|
182
|
+
@a[idx] = idx
|
183
|
+
expect(@a[idx - 1]).to be nil
|
184
|
+
expect(@a[idx + 1]).to be nil
|
185
|
+
expect(@a.check).to be true
|
186
|
+
end
|
187
|
+
expect(@a[0]).to be nil
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should insert after a gap' do
|
191
|
+
ref = []
|
192
|
+
10.times do |i|
|
193
|
+
idx = 10 + i * 3
|
194
|
+
@a[idx] = idx
|
195
|
+
ref[idx] = idx
|
196
|
+
expect(@a[idx]).to eql(idx)
|
197
|
+
expect(@a.check).to be true
|
198
|
+
end
|
199
|
+
10.times do |i|
|
200
|
+
idx = i * 5
|
201
|
+
@a[idx] = idx
|
202
|
+
ref[idx] = idx
|
203
|
+
expect(@a[idx]).to eql(idx)
|
204
|
+
expect(@a.check).to be true
|
205
|
+
end
|
206
|
+
expect(@a.check).to be true
|
207
|
+
expect(@a.to_a).to eql(ref)
|
208
|
+
end
|
209
|
+
|
172
210
|
it 'should iterate over all values' do
|
173
211
|
n = 3 * NODE_ENTRIES
|
174
212
|
0.upto(n) { |i| @a.insert(i, i) }
|
@@ -191,6 +229,15 @@ describe PEROBS::BigArray do
|
|
191
229
|
end
|
192
230
|
end
|
193
231
|
|
232
|
+
it 'should insert at the beginning' do
|
233
|
+
(5 * NODE_ENTRIES).downto(0) do |i|
|
234
|
+
@a.insert(0, i)
|
235
|
+
end
|
236
|
+
expect(@a.check).to be true
|
237
|
+
a = Array.new(5 * NODE_ENTRIES + 1) { |i| i }
|
238
|
+
expect(@a.to_a).to eq(a)
|
239
|
+
end
|
240
|
+
|
194
241
|
it 'should persist the data' do
|
195
242
|
db_name = generate_db_name(__FILE__ + "_persist")
|
196
243
|
store = PEROBS::Store.new(db_name)
|
data/test/BigHash_spec.rb
CHANGED
@@ -72,6 +72,13 @@ describe PEROBS::Hash do
|
|
72
72
|
expect(@h.keys).to eql([ 'foo' ])
|
73
73
|
end
|
74
74
|
|
75
|
+
it 'should store a few objects' do
|
76
|
+
20.times do |i|
|
77
|
+
@h["bar#{i}"] = "foo#{i}"
|
78
|
+
end
|
79
|
+
expect(@h.size).to eql(20)
|
80
|
+
end
|
81
|
+
|
75
82
|
it 'should return nil for unknown objects' do
|
76
83
|
expect(@h['bar']).to be_nil
|
77
84
|
end
|
@@ -127,6 +134,7 @@ describe PEROBS::Hash do
|
|
127
134
|
end
|
128
135
|
expect(h.check).to be true
|
129
136
|
expect(h.length).to eql(n)
|
137
|
+
expect(store.check).to eql(0)
|
130
138
|
store.exit
|
131
139
|
|
132
140
|
store = PEROBS::Store.new(db_name)
|
data/test/FlatFileDB_spec.rb
CHANGED
@@ -71,7 +71,7 @@ describe PEROBS::FlatFileDB do
|
|
71
71
|
expect { db2.open }.to raise_error(PEROBS::FatalError)
|
72
72
|
end
|
73
73
|
|
74
|
-
it 'should do a version upgrade' do
|
74
|
+
it 'should do a version upgrade from version 3' do
|
75
75
|
# Close the store
|
76
76
|
@store.exit
|
77
77
|
src_dir = File.join(File.dirname(__FILE__), 'LegacyDBs', 'version_3')
|
@@ -82,6 +82,21 @@ describe PEROBS::FlatFileDB do
|
|
82
82
|
capture_io { expect(db.check).to be true }
|
83
83
|
end
|
84
84
|
|
85
|
+
it 'should do a version upgrade from version 4.1' do
|
86
|
+
# Close the store
|
87
|
+
@store.exit
|
88
|
+
src_dir = File.join(File.dirname(__FILE__), 'LegacyDBs', 'version_4.1')
|
89
|
+
FileUtils.cp_r(Dir.glob(src_dir + '/*'), @db_dir)
|
90
|
+
|
91
|
+
db = LegacyDB.new(@db_dir)
|
92
|
+
capture_io { db.open }
|
93
|
+
capture_io { expect(db.repair).to eql(0) }
|
94
|
+
expect(File.exist?(File.join(@db_dir, 'space_index.blobs'))).to be true
|
95
|
+
expect(File.exist?(File.join(@db_dir, 'space_list.blobs'))).to be true
|
96
|
+
expect(File.exist?(File.join(@db_dir, 'database_spaces.blobs'))).to be false
|
97
|
+
capture_io { expect(db.check).to be true }
|
98
|
+
end
|
99
|
+
|
85
100
|
it 'should refuse a version downgrade' do
|
86
101
|
# Close the store
|
87
102
|
@store.exit
|
@@ -119,7 +134,7 @@ describe PEROBS::FlatFileDB do
|
|
119
134
|
expect(store['o'].b).to eql(42)
|
120
135
|
end
|
121
136
|
|
122
|
-
it 'should
|
137
|
+
it 'should discard a corrupted blob inside the database.blobs file' do
|
123
138
|
@store.exit
|
124
139
|
|
125
140
|
db = PEROBS::FlatFileDB.new(@db_dir)
|
@@ -142,7 +157,7 @@ describe PEROBS::FlatFileDB do
|
|
142
157
|
f.close
|
143
158
|
|
144
159
|
db.open
|
145
|
-
expect(db.check_db).to eql(
|
160
|
+
expect(db.check_db).to eql(1)
|
146
161
|
expect(db.check_db(true)).to eql(1)
|
147
162
|
db.close
|
148
163
|
db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
|
@@ -160,5 +175,95 @@ describe PEROBS::FlatFileDB do
|
|
160
175
|
db.close
|
161
176
|
end
|
162
177
|
|
178
|
+
it 'should discard a corrupted blob at the end of the database.blobs file' do
|
179
|
+
@store.exit
|
180
|
+
|
181
|
+
db = PEROBS::FlatFileDB.new(@db_dir)
|
182
|
+
db_file = File.join(@db_dir, 'database.blobs')
|
183
|
+
db.open
|
184
|
+
0.upto(5) do |i|
|
185
|
+
db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
|
186
|
+
end
|
187
|
+
db.close
|
188
|
+
|
189
|
+
f = File.truncate(db_file, File.size(db_file) - 20)
|
190
|
+
|
191
|
+
db.open
|
192
|
+
expect(db.check_db).to eql(2)
|
193
|
+
expect(db.check_db(true)).to eql(2)
|
194
|
+
db.close
|
195
|
+
db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
|
196
|
+
:log_level => Logger::ERROR })
|
197
|
+
db.open
|
198
|
+
expect(db.check_db).to eql(0)
|
199
|
+
|
200
|
+
0.upto(4) do |i|
|
201
|
+
expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
|
202
|
+
end
|
203
|
+
expect(db.get_object(6)).to be_nil
|
204
|
+
db.close
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should discard a corrupted header at the end of the database.blobs file' do
|
208
|
+
@store.exit
|
209
|
+
|
210
|
+
db = PEROBS::FlatFileDB.new(@db_dir)
|
211
|
+
db_file = File.join(@db_dir, 'database.blobs')
|
212
|
+
db.open
|
213
|
+
0.upto(5) do |i|
|
214
|
+
db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
|
215
|
+
end
|
216
|
+
db.close
|
217
|
+
|
218
|
+
f = File.truncate(db_file, File.size(db_file) - 200)
|
219
|
+
|
220
|
+
db.open
|
221
|
+
expect(db.check_db).to eql(1)
|
222
|
+
expect(db.check_db(true)).to eql(1)
|
223
|
+
db.close
|
224
|
+
db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
|
225
|
+
:log_level => Logger::ERROR })
|
226
|
+
db.open
|
227
|
+
expect(db.check_db).to eql(0)
|
228
|
+
|
229
|
+
0.upto(4) do |i|
|
230
|
+
expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
|
231
|
+
end
|
232
|
+
expect(db.get_object(6)).to be_nil
|
233
|
+
db.close
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should handle a lost blob at the end of the database.blobs file' do
|
237
|
+
@store.exit
|
238
|
+
|
239
|
+
db = PEROBS::FlatFileDB.new(@db_dir)
|
240
|
+
db_file = File.join(@db_dir, 'database.blobs')
|
241
|
+
db.open
|
242
|
+
0.upto(5) do |i|
|
243
|
+
db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
|
244
|
+
end
|
245
|
+
db.close
|
246
|
+
|
247
|
+
# This exactly removes the last blob (#6)
|
248
|
+
f = File.truncate(db_file, File.size(db_file) - 210)
|
249
|
+
|
250
|
+
db.open
|
251
|
+
expect(db.check_db).to eql(1)
|
252
|
+
# The repair won't find the missing blob since the blob file is without
|
253
|
+
# errors.
|
254
|
+
expect(db.check_db(true)).to eql(0)
|
255
|
+
db.close
|
256
|
+
db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
|
257
|
+
:log_level => Logger::ERROR })
|
258
|
+
db.open
|
259
|
+
expect(db.check_db).to eql(0)
|
260
|
+
|
261
|
+
0.upto(4) do |i|
|
262
|
+
expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
|
263
|
+
end
|
264
|
+
expect(db.get_object(6)).to be_nil
|
265
|
+
db.close
|
266
|
+
end
|
267
|
+
|
163
268
|
end
|
164
269
|
|