perobs 4.0.0 → 4.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.
- checksums.yaml +4 -4
- data/lib/perobs.rb +1 -0
- data/lib/perobs/Array.rb +66 -19
- data/lib/perobs/BTree.rb +83 -12
- data/lib/perobs/BTreeBlob.rb +1 -1
- data/lib/perobs/BTreeDB.rb +2 -2
- data/lib/perobs/BTreeNode.rb +365 -85
- data/lib/perobs/BigArray.rb +267 -0
- data/lib/perobs/BigArrayNode.rb +998 -0
- data/lib/perobs/BigHash.rb +262 -0
- data/lib/perobs/BigTree.rb +184 -0
- data/lib/perobs/BigTreeNode.rb +873 -0
- data/lib/perobs/ConsoleProgressMeter.rb +61 -0
- data/lib/perobs/DataBase.rb +4 -3
- data/lib/perobs/DynamoDB.rb +57 -15
- data/lib/perobs/EquiBlobsFile.rb +143 -51
- data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
- data/lib/perobs/FlatFile.rb +363 -203
- data/lib/perobs/FlatFileBlobHeader.rb +98 -54
- data/lib/perobs/FlatFileDB.rb +42 -20
- data/lib/perobs/Hash.rb +58 -13
- data/lib/perobs/IDList.rb +144 -0
- data/lib/perobs/IDListPage.rb +107 -0
- data/lib/perobs/IDListPageFile.rb +180 -0
- data/lib/perobs/IDListPageRecord.rb +142 -0
- data/lib/perobs/Object.rb +18 -15
- data/lib/perobs/ObjectBase.rb +38 -4
- data/lib/perobs/PersistentObjectCache.rb +53 -67
- data/lib/perobs/PersistentObjectCacheLine.rb +24 -12
- data/lib/perobs/ProgressMeter.rb +97 -0
- data/lib/perobs/SpaceTree.rb +21 -12
- data/lib/perobs/SpaceTreeNode.rb +53 -61
- data/lib/perobs/Store.rb +71 -32
- data/lib/perobs/version.rb +1 -1
- data/perobs.gemspec +4 -4
- data/test/Array_spec.rb +15 -6
- data/test/BTree_spec.rb +5 -2
- data/test/BigArray_spec.rb +214 -0
- data/test/BigHash_spec.rb +144 -0
- data/test/BigTreeNode_spec.rb +153 -0
- data/test/BigTree_spec.rb +259 -0
- data/test/EquiBlobsFile_spec.rb +105 -1
- data/test/FNV_Hash_1a_64_spec.rb +59 -0
- data/test/FlatFileDB_spec.rb +63 -14
- data/test/Hash_spec.rb +1 -2
- data/test/IDList_spec.rb +77 -0
- data/test/LegacyDBs/LegacyDB.rb +151 -0
- data/test/LegacyDBs/version_3/class_map.json +1 -0
- data/test/LegacyDBs/version_3/config.json +1 -0
- data/test/LegacyDBs/version_3/database.blobs +0 -0
- data/test/LegacyDBs/version_3/database_spaces.blobs +0 -0
- data/test/LegacyDBs/version_3/index.blobs +0 -0
- data/test/LegacyDBs/version_3/version +1 -0
- data/test/LockFile_spec.rb +9 -6
- data/test/SpaceTree_spec.rb +4 -1
- data/test/Store_spec.rb +290 -199
- data/test/spec_helper.rb +9 -4
- metadata +47 -10
- data/lib/perobs/TreeDB.rb +0 -277
data/test/SpaceTree_spec.rb
CHANGED
@@ -27,13 +27,16 @@ require 'fileutils'
|
|
27
27
|
|
28
28
|
require 'spec_helper'
|
29
29
|
require 'perobs/SpaceTree'
|
30
|
+
require 'perobs/ProgressMeter'
|
30
31
|
|
31
32
|
describe PEROBS::SpaceTree do
|
32
33
|
|
33
34
|
before(:all) do
|
34
35
|
@db_dir = generate_db_name('SpaceTree')
|
35
36
|
FileUtils.mkdir_p(@db_dir)
|
36
|
-
@m = PEROBS::SpaceTree.new(@db_dir)
|
37
|
+
@m = PEROBS::SpaceTree.new(@db_dir, PEROBS::ProgressMeter.new)
|
38
|
+
PEROBS.log.level = Logger::ERROR
|
39
|
+
PEROBS.log.open($stderr)
|
37
40
|
end
|
38
41
|
|
39
42
|
after(:all) do
|
data/test/Store_spec.rb
CHANGED
@@ -33,7 +33,7 @@ class Person < PEROBS::Object
|
|
33
33
|
|
34
34
|
attr_persist :name, :zip, :bmi, :married, :related, :relatives
|
35
35
|
|
36
|
-
def initialize(
|
36
|
+
def initialize(p)
|
37
37
|
super
|
38
38
|
attr_init(:name, '')
|
39
39
|
attr_init(:bmi, 22.2)
|
@@ -46,7 +46,7 @@ class PersonN < PEROBS::Object
|
|
46
46
|
|
47
47
|
attr_persist :name, :zip, :bmi, :married, :related, :relatives
|
48
48
|
|
49
|
-
def initialize(
|
49
|
+
def initialize(p)
|
50
50
|
super
|
51
51
|
attr_init(:name, '')
|
52
52
|
attr_init(:bmi, 22.2)
|
@@ -59,9 +59,9 @@ class O0 < PEROBS::Object
|
|
59
59
|
|
60
60
|
attr_persist :child
|
61
61
|
|
62
|
-
def initialize(
|
62
|
+
def initialize(p)
|
63
63
|
super
|
64
|
-
self.child =
|
64
|
+
self.child = p.store.new(O1, myself)
|
65
65
|
end
|
66
66
|
|
67
67
|
end
|
@@ -84,22 +84,16 @@ describe PEROBS::Store do
|
|
84
84
|
end
|
85
85
|
|
86
86
|
after(:each) do
|
87
|
-
@store.gc
|
88
|
-
expect { @store.check }.to_not raise_error
|
89
|
-
expect { @store.delete_store }.to_not raise_error
|
90
|
-
end
|
91
|
-
|
92
|
-
after(:all) do
|
93
87
|
FileUtils.rm_rf(@db_file)
|
94
88
|
end
|
95
89
|
|
96
90
|
it 'should @store simple objects' do
|
97
|
-
|
98
|
-
|
91
|
+
store = PEROBS::Store.new(@db_file, { :serializer => :yaml })
|
92
|
+
store['john'] = john = store.new(Person)
|
99
93
|
john.name = 'John'
|
100
94
|
john.zip = 4060
|
101
95
|
john.bmi = 25.5
|
102
|
-
|
96
|
+
store['jane'] = jane = store.new(Person)
|
103
97
|
jane.name = 'Jane'
|
104
98
|
jane.related = john
|
105
99
|
jane.married = true
|
@@ -110,48 +104,56 @@ describe PEROBS::Store do
|
|
110
104
|
expect(john.bmi).to eq(25.5)
|
111
105
|
expect(john.married).to be false
|
112
106
|
expect(john.related).to be_nil
|
113
|
-
jane =
|
107
|
+
jane = store['jane']
|
114
108
|
expect(jane.name).to eq('Jane')
|
115
109
|
expect(jane.related).to eq(john)
|
116
110
|
expect(jane.married).to be true
|
111
|
+
|
112
|
+
capture_io { store.gc }
|
113
|
+
capture_io { expect { store.check }.to_not raise_error }
|
114
|
+
expect { store.delete_store }.to_not raise_error
|
117
115
|
end
|
118
116
|
|
119
117
|
it 'should @store and retrieve simple objects' do
|
120
118
|
[ :marshal, :json, :yaml ].each do |serializer|
|
121
119
|
FileUtils.rm_rf(@db_file)
|
122
|
-
|
123
|
-
|
120
|
+
store = PEROBS::Store.new(@db_file, { :serializer => serializer })
|
121
|
+
store['john'] = john = store.new(Person)
|
124
122
|
john.name = 'John'
|
125
123
|
john.zip = 4060
|
126
124
|
john.bmi = 25.5
|
127
|
-
|
125
|
+
store['jane'] = jane = store.new(Person)
|
128
126
|
jane.name = 'Jane'
|
129
127
|
jane.related = john
|
130
128
|
jane.married = true
|
131
129
|
jane.relatives = 'test'
|
132
130
|
|
133
|
-
|
131
|
+
capture_io { store.exit }
|
134
132
|
|
135
|
-
|
136
|
-
john =
|
133
|
+
store = PEROBS::Store.new(@db_file)
|
134
|
+
john = store['john']
|
137
135
|
expect(john.name).to eq('John')
|
138
136
|
expect(john.zip).to eq(4060)
|
139
137
|
expect(john.bmi).to eq(25.5)
|
140
138
|
expect(john.married).to be false
|
141
139
|
expect(john.related).to be_nil
|
142
|
-
jane =
|
140
|
+
jane = store['jane']
|
143
141
|
expect(jane.name).to eq('Jane')
|
144
142
|
expect(jane.related).to eq(john)
|
145
143
|
expect(jane.married).to be true
|
144
|
+
|
145
|
+
capture_io { store.gc }
|
146
|
+
capture_io { expect { store.check }.to_not raise_error }
|
147
|
+
expect { store.delete_store }.to_not raise_error
|
146
148
|
end
|
147
149
|
end
|
148
150
|
|
149
151
|
it 'should flush cached objects when necessary' do
|
150
|
-
|
152
|
+
store = PEROBS::Store.new(@db_file, :cache_bits => 3)
|
151
153
|
last_obj = nil
|
152
154
|
0.upto(20) do |i|
|
153
|
-
|
154
|
-
expect(
|
155
|
+
store["person#{i}"] = obj = store.new(Person)
|
156
|
+
expect(store["person#{i}"]).to eq(obj)
|
155
157
|
obj.name = "Person #{i}"
|
156
158
|
expect(obj.name).to eq("Person #{i}")
|
157
159
|
obj.related = last_obj
|
@@ -159,167 +161,216 @@ describe PEROBS::Store do
|
|
159
161
|
last_obj = obj
|
160
162
|
end
|
161
163
|
0.upto(20) do |i|
|
162
|
-
expect(
|
164
|
+
expect(store["person#{i}"].name).to eq("Person #{i}")
|
163
165
|
end
|
166
|
+
|
167
|
+
capture_io { store.gc }
|
168
|
+
capture_io { expect { store.check }.to_not raise_error }
|
169
|
+
expect { store.delete_store }.to_not raise_error
|
164
170
|
end
|
165
171
|
|
166
172
|
it 'should support renaming of classes' do
|
167
|
-
|
168
|
-
|
173
|
+
store = PEROBS::Store.new(@db_file)
|
174
|
+
store['john'] = john = store.new(Person)
|
169
175
|
john.name = 'John'
|
170
176
|
john.zip = 4060
|
171
177
|
john.bmi = 25.5
|
172
|
-
|
178
|
+
store['jane'] = jane = store.new(Person)
|
173
179
|
jane.name = 'Jane'
|
174
180
|
jane.related = john
|
175
181
|
jane.married = true
|
176
182
|
jane.relatives = 'test'
|
177
183
|
|
178
|
-
|
184
|
+
capture_io { store.exit }
|
179
185
|
|
180
|
-
|
181
|
-
|
182
|
-
john =
|
186
|
+
store = PEROBS::Store.new(@db_file)
|
187
|
+
store.rename_classes({ 'Person' => 'PersonN' })
|
188
|
+
john = store['john']
|
183
189
|
expect(john.name).to eq('John')
|
184
190
|
expect(john.zip).to eq(4060)
|
185
191
|
expect(john.bmi).to eq(25.5)
|
186
192
|
expect(john.married).to be false
|
187
193
|
expect(john.related).to be_nil
|
188
|
-
jane =
|
194
|
+
jane = store['jane']
|
189
195
|
expect(jane.name).to eq('Jane')
|
190
196
|
expect(jane.related).to eq(john)
|
191
197
|
expect(jane.married).to be true
|
198
|
+
|
199
|
+
capture_io { store.gc }
|
200
|
+
capture_io { expect { store.check }.to_not raise_error }
|
201
|
+
expect { store.delete_store }.to_not raise_error
|
192
202
|
end
|
193
203
|
|
194
204
|
it 'should detect modification to non-working objects' do
|
195
|
-
|
205
|
+
store = PEROBS::Store.new(@db_file, :cache_bits => 3)
|
196
206
|
0.upto(20) do |i|
|
197
|
-
|
207
|
+
store["person#{i}"] = obj = store.new(Person)
|
198
208
|
obj.name = "Person #{i}"
|
199
209
|
end
|
200
210
|
0.upto(20) do |i|
|
201
|
-
|
211
|
+
store["person#{i}"].name = "New Person #{i}"
|
202
212
|
end
|
203
|
-
|
204
|
-
|
213
|
+
capture_io { store.exit }
|
214
|
+
store = PEROBS::Store.new(@db_file)
|
205
215
|
0.upto(20) do |i|
|
206
|
-
expect(
|
216
|
+
expect(store["person#{i}"].name).to eq("New Person #{i}")
|
207
217
|
end
|
218
|
+
|
219
|
+
capture_io { store.gc }
|
220
|
+
capture_io { expect { store.check }.to_not raise_error }
|
221
|
+
expect { store.delete_store }.to_not raise_error
|
208
222
|
end
|
209
223
|
|
210
224
|
it 'should garbage collect unlinked objects' do
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
expect(
|
224
|
-
|
225
|
-
|
225
|
+
store = PEROBS::Store.new(@db_file)
|
226
|
+
persons = []
|
227
|
+
0.upto(20) do |i|
|
228
|
+
persons[i] = obj = store.new(Person)
|
229
|
+
obj.name = "person#{i}"
|
230
|
+
if i < 3
|
231
|
+
store["person#{i}"] = obj
|
232
|
+
else
|
233
|
+
persons[i - 3].related = obj
|
234
|
+
end
|
235
|
+
end
|
236
|
+
store.sync
|
237
|
+
expect(store.size).to eq(21)
|
238
|
+
|
239
|
+
store['person0'] = nil
|
240
|
+
capture_io { store.gc }
|
241
|
+
expect(store.size).to eq(14)
|
242
|
+
capture_io { expect { store.check }.to_not raise_error }
|
243
|
+
capture_io { store.exit }
|
244
|
+
store = PEROBS::Store.new(@db_file)
|
245
|
+
capture_io { expect { store.check }.to_not raise_error }
|
246
|
+
|
247
|
+
person = store['person1']
|
248
|
+
i = 0
|
249
|
+
while (person = person.related) do
|
250
|
+
i += 1
|
251
|
+
end
|
252
|
+
expect(i).to eq(6)
|
253
|
+
|
254
|
+
capture_io { store.gc }
|
255
|
+
capture_io { expect { store.check }.to_not raise_error }
|
256
|
+
expect { store.delete_store }.to_not raise_error
|
226
257
|
end
|
227
258
|
|
228
259
|
it 'should handle cyclicly linked objects' do
|
229
|
-
|
230
|
-
|
260
|
+
store = PEROBS::Store.new(@db_file)
|
261
|
+
store['person0'] = p0 = store.new(Person)
|
231
262
|
id0 = p0._id
|
232
|
-
p1 =
|
263
|
+
p1 = store.new(Person)
|
233
264
|
id1 = p1._id
|
234
|
-
p2 =
|
265
|
+
p2 = store.new(Person)
|
235
266
|
id2 = p2._id
|
236
267
|
p1.related = p2
|
237
268
|
p2.related = p1
|
238
269
|
p0.related = p1
|
239
|
-
expect(
|
240
|
-
expect(
|
270
|
+
capture_io { expect(store.check).to eq(0) }
|
271
|
+
capture_io { expect(store.gc).to eq(0) }
|
241
272
|
p0 = p1 = p2 = nil
|
242
|
-
|
273
|
+
capture_io { store.exit }
|
243
274
|
GC.start
|
244
|
-
|
245
|
-
expect(
|
246
|
-
expect(
|
247
|
-
expect(
|
275
|
+
store = PEROBS::Store.new(@db_file)
|
276
|
+
expect(store['person0']._id).to eq(id0)
|
277
|
+
expect(store['person0'].related._id).to eq(id1)
|
278
|
+
expect(store['person0'].related.related._id).to eq(id2)
|
248
279
|
|
249
|
-
|
250
|
-
expect(
|
280
|
+
store['person0'].related = nil
|
281
|
+
capture_io { expect(store.gc).to eq(2) }
|
251
282
|
GC.start
|
252
|
-
expect(
|
253
|
-
expect(
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
expect(
|
258
|
-
expect(
|
259
|
-
expect(
|
283
|
+
expect(store.object_by_id(id1)).to be_nil
|
284
|
+
expect(store.object_by_id(id2)).to be_nil
|
285
|
+
capture_io { store.exit }
|
286
|
+
|
287
|
+
store = PEROBS::Store.new(@db_file)
|
288
|
+
capture_io { expect(store.check).to eq(0) }
|
289
|
+
expect(store.object_by_id(id1)).to be_nil
|
290
|
+
expect(store.object_by_id(id2)).to be_nil
|
291
|
+
|
292
|
+
capture_io { store.gc }
|
293
|
+
capture_io { expect { store.check }.to_not raise_error }
|
294
|
+
expect { store.delete_store }.to_not raise_error
|
260
295
|
end
|
261
296
|
|
262
297
|
it 'should support a successful transaction' do
|
263
|
-
|
264
|
-
|
265
|
-
|
298
|
+
store = PEROBS::Store.new(@db_file)
|
299
|
+
store.transaction do
|
300
|
+
store['person0'] = p0 = store.new(Person)
|
266
301
|
p0.name = 'Jimmy'
|
267
302
|
end
|
268
|
-
expect(
|
303
|
+
expect(store['person0'].name).to eq('Jimmy')
|
304
|
+
|
305
|
+
capture_io { store.gc }
|
306
|
+
capture_io { expect { store.check }.to_not raise_error }
|
307
|
+
expect { store.delete_store }.to_not raise_error
|
269
308
|
end
|
270
309
|
|
271
310
|
it 'should handle a failed transaction 1' do
|
272
|
-
|
311
|
+
store = PEROBS::Store.new(@db_file)
|
273
312
|
begin
|
274
|
-
|
275
|
-
|
313
|
+
store.transaction do
|
314
|
+
store['person0'] = p0 = store.new(Person)
|
276
315
|
p0.name = 'Jimmy'
|
277
316
|
raise POSError
|
278
317
|
end
|
279
318
|
rescue POSError
|
280
319
|
end
|
281
|
-
expect(
|
320
|
+
expect(store['person0']).to be_nil
|
321
|
+
|
322
|
+
capture_io { store.gc }
|
323
|
+
capture_io { expect { store.check }.to_not raise_error }
|
324
|
+
expect { store.delete_store }.to_not raise_error
|
282
325
|
end
|
283
326
|
|
284
327
|
it 'should handle a failed transaction 2' do
|
285
|
-
|
286
|
-
|
328
|
+
store = PEROBS::Store.new(@db_file)
|
329
|
+
store['person1'] = p1 = store.new(Person)
|
287
330
|
p1.name = 'Joe'
|
288
331
|
begin
|
289
|
-
|
290
|
-
|
332
|
+
store.transaction do
|
333
|
+
store['person0'] = p0 = store.new(Person)
|
291
334
|
p0.name = 'Jimmy'
|
292
335
|
raise POSError
|
293
336
|
end
|
294
337
|
rescue POSError
|
295
338
|
end
|
296
|
-
expect(
|
297
|
-
expect(
|
339
|
+
expect(store['person1'].name).to eq('Joe')
|
340
|
+
expect(store['person0']).to be_nil
|
341
|
+
|
342
|
+
capture_io { store.gc }
|
343
|
+
capture_io { expect { store.check }.to_not raise_error }
|
344
|
+
expect { store.delete_store }.to_not raise_error
|
298
345
|
end
|
299
346
|
|
300
347
|
it 'should support a successful nested transaction' do
|
301
|
-
|
302
|
-
|
303
|
-
|
348
|
+
store = PEROBS::Store.new(@db_file)
|
349
|
+
store.transaction do
|
350
|
+
store['person0'] = p0 = store.new(Person)
|
304
351
|
p0.name = 'Jimmy'
|
305
|
-
|
306
|
-
|
352
|
+
store.transaction do
|
353
|
+
store['person1'] = p1 = store.new(Person)
|
307
354
|
p1.name = 'Joe'
|
308
355
|
end
|
309
356
|
end
|
310
|
-
expect(
|
311
|
-
expect(
|
357
|
+
expect(store['person0'].name).to eq('Jimmy')
|
358
|
+
expect(store['person1'].name).to eq('Joe')
|
359
|
+
|
360
|
+
capture_io { store.gc }
|
361
|
+
capture_io { expect { store.check }.to_not raise_error }
|
362
|
+
expect { store.delete_store }.to_not raise_error
|
312
363
|
end
|
313
364
|
|
314
365
|
it 'should handle a failed nested transaction 1' do
|
315
|
-
|
366
|
+
store = PEROBS::Store.new(@db_file)
|
316
367
|
begin
|
317
|
-
|
318
|
-
|
368
|
+
store.transaction do
|
369
|
+
store['person0'] = p0 = store.new(Person)
|
319
370
|
p0.name = 'Jimmy'
|
320
371
|
begin
|
321
|
-
|
322
|
-
|
372
|
+
store.transaction do
|
373
|
+
store['person1'] = p1 = store.new(Person)
|
323
374
|
p1.name = 'Joe'
|
324
375
|
raise POSError
|
325
376
|
end
|
@@ -328,58 +379,70 @@ describe PEROBS::Store do
|
|
328
379
|
end
|
329
380
|
rescue POSError
|
330
381
|
end
|
331
|
-
expect(
|
332
|
-
expect(
|
382
|
+
expect(store['person0'].name).to eq('Jimmy')
|
383
|
+
expect(store['person1']).to be_nil
|
384
|
+
|
385
|
+
capture_io { store.gc }
|
386
|
+
capture_io { expect { store.check }.to_not raise_error }
|
387
|
+
expect { store.delete_store }.to_not raise_error
|
333
388
|
end
|
334
389
|
|
335
390
|
it 'should handle a failed nested transaction 2' do
|
336
|
-
|
391
|
+
store = PEROBS::Store.new(@db_file)
|
337
392
|
begin
|
338
|
-
|
339
|
-
|
393
|
+
store.transaction do
|
394
|
+
store['person0'] = p0 = store.new(Person)
|
340
395
|
p0.name = 'Jimmy'
|
341
|
-
|
342
|
-
|
396
|
+
store.transaction do
|
397
|
+
store['person1'] = p1 = store.new(Person)
|
343
398
|
p1.name = 'Joe'
|
344
399
|
end
|
345
400
|
raise POSError
|
346
401
|
end
|
347
402
|
rescue POSError
|
348
403
|
end
|
349
|
-
expect(
|
350
|
-
expect(
|
404
|
+
expect(store['person0']).to be_nil
|
405
|
+
expect(store['person1']).to be_nil
|
406
|
+
|
407
|
+
capture_io { store.gc }
|
408
|
+
capture_io { expect { store.check }.to_not raise_error }
|
409
|
+
expect { store.delete_store }.to_not raise_error
|
351
410
|
end
|
352
411
|
|
353
412
|
it 'should support a successful 2-level nested transaction' do
|
354
|
-
|
355
|
-
|
356
|
-
|
413
|
+
store = PEROBS::Store.new(@db_file)
|
414
|
+
store.transaction do
|
415
|
+
store['person0'] = p0 = store.new(Person)
|
357
416
|
p0.name = 'Jimmy'
|
358
|
-
|
359
|
-
|
417
|
+
store.transaction do
|
418
|
+
store['person1'] = p1 = store.new(Person)
|
360
419
|
p1.name = 'Joe'
|
361
|
-
|
362
|
-
|
420
|
+
store.transaction do
|
421
|
+
store['person2'] = p2 = store.new(Person)
|
363
422
|
p2.name = 'Jane'
|
364
423
|
end
|
365
424
|
end
|
366
425
|
end
|
367
|
-
expect(
|
368
|
-
expect(
|
369
|
-
expect(
|
426
|
+
expect(store['person0'].name).to eq('Jimmy')
|
427
|
+
expect(store['person1'].name).to eq('Joe')
|
428
|
+
expect(store['person2'].name).to eq('Jane')
|
429
|
+
|
430
|
+
capture_io { store.gc }
|
431
|
+
capture_io { expect { store.check }.to_not raise_error }
|
432
|
+
expect { store.delete_store }.to_not raise_error
|
370
433
|
end
|
371
434
|
|
372
435
|
it 'should handle a failed 2-level nested transaction 1' do
|
373
|
-
|
374
|
-
|
375
|
-
|
436
|
+
store = PEROBS::Store.new(@db_file)
|
437
|
+
store.transaction do
|
438
|
+
store['person0'] = p0 = store.new(Person)
|
376
439
|
p0.name = 'Jimmy'
|
377
|
-
|
378
|
-
|
440
|
+
store.transaction do
|
441
|
+
store['person1'] = p1 = store.new(Person)
|
379
442
|
p1.name = 'Joe'
|
380
443
|
begin
|
381
|
-
|
382
|
-
|
444
|
+
store.transaction do
|
445
|
+
store['person2'] = p2 = store.new(Person)
|
383
446
|
p2.name = 'Jane'
|
384
447
|
raise POSError
|
385
448
|
end
|
@@ -387,22 +450,26 @@ describe PEROBS::Store do
|
|
387
450
|
end
|
388
451
|
end
|
389
452
|
end
|
390
|
-
expect(
|
391
|
-
expect(
|
392
|
-
expect(
|
453
|
+
expect(store['person0'].name).to eq('Jimmy')
|
454
|
+
expect(store['person1'].name).to eq('Joe')
|
455
|
+
expect(store['person2']).to be_nil
|
456
|
+
|
457
|
+
capture_io { store.gc }
|
458
|
+
capture_io { expect { store.check }.to_not raise_error }
|
459
|
+
expect { store.delete_store }.to_not raise_error
|
393
460
|
end
|
394
461
|
|
395
462
|
it 'should handle a failed 2-level nested transaction 2' do
|
396
|
-
|
397
|
-
|
398
|
-
|
463
|
+
store = PEROBS::Store.new(@db_file)
|
464
|
+
store.transaction do
|
465
|
+
store['person0'] = p0 = store.new(Person)
|
399
466
|
p0.name = 'Jimmy'
|
400
|
-
|
401
|
-
|
467
|
+
store.transaction do
|
468
|
+
store['person1'] = p1 = store.new(Person)
|
402
469
|
p1.name = 'Joe'
|
403
470
|
begin
|
404
|
-
|
405
|
-
|
471
|
+
store.transaction do
|
472
|
+
store['person2'] = p2 = store.new(Person)
|
406
473
|
p2.name = 'Jane'
|
407
474
|
raise POSError
|
408
475
|
end
|
@@ -411,55 +478,67 @@ describe PEROBS::Store do
|
|
411
478
|
p1.name = 'Jane'
|
412
479
|
end
|
413
480
|
end
|
414
|
-
expect(
|
415
|
-
expect(
|
416
|
-
expect(
|
481
|
+
expect(store['person0'].name).to eq('Jimmy')
|
482
|
+
expect(store['person1'].name).to eq('Jane')
|
483
|
+
expect(store['person2']).to be_nil
|
484
|
+
|
485
|
+
capture_io { store.gc }
|
486
|
+
capture_io { expect { store.check }.to_not raise_error }
|
487
|
+
expect { store.delete_store }.to_not raise_error
|
417
488
|
end
|
418
489
|
|
419
490
|
it 'should track in-memory objects properly' do
|
420
|
-
|
421
|
-
expect(
|
422
|
-
|
491
|
+
store = PEROBS::Store.new(@db_file)
|
492
|
+
expect(store.statistics[:in_memory_objects]).to eq(1)
|
493
|
+
store['person'] = store.new(Person)
|
423
494
|
# We have the root hash and the Person object.
|
424
|
-
expect(
|
425
|
-
|
495
|
+
expect(store.statistics[:in_memory_objects]).to eq(2)
|
496
|
+
store.sync
|
426
497
|
GC.start
|
427
498
|
# Now the Person should be gone from memory.
|
428
499
|
# Ruby 2.3 and later has changed the GC so that this does not work
|
429
500
|
# reliably anymore. The GC seems to operate lazyly.
|
430
|
-
#expect(
|
501
|
+
#expect(store.statistics[:in_memory_objects]).to eq(1)
|
502
|
+
|
503
|
+
capture_io { store.gc }
|
504
|
+
capture_io { expect { store.check }.to_not raise_error }
|
505
|
+
expect { store.delete_store }.to_not raise_error
|
431
506
|
end
|
432
507
|
|
433
508
|
it 'should handle nested constructors' do
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
expect(
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
expect(
|
442
|
-
expect(
|
509
|
+
store = PEROBS::Store.new(@db_file)
|
510
|
+
store['root'] = store.new(O0)
|
511
|
+
store.sync
|
512
|
+
capture_io { expect(store.check).to eq(0) }
|
513
|
+
capture_io { store.exit }
|
514
|
+
|
515
|
+
store = PEROBS::Store.new(@db_file)
|
516
|
+
capture_io { expect(store.check).to eq(0) }
|
517
|
+
expect(store['root'].child.parent).to eq(store['root'])
|
518
|
+
|
519
|
+
capture_io { store.gc }
|
520
|
+
capture_io { expect { store.check }.to_not raise_error }
|
521
|
+
expect { store.delete_store }.to_not raise_error
|
443
522
|
end
|
444
523
|
|
445
524
|
it 'should handle frequent updates of objects' do
|
446
|
-
|
525
|
+
store = PEROBS::Store.new(@db_file)
|
447
526
|
count = 10000
|
448
527
|
0.upto(count) do |i|
|
449
528
|
key = "Obj#{i}"
|
450
|
-
|
529
|
+
store[key] = p = store.new(Person)
|
451
530
|
p.name = "0:#{i}:" + 'X' * rand(64)
|
452
531
|
end
|
453
532
|
|
454
533
|
0.upto(10) do |iteration|
|
455
534
|
0.upto(count) do |i|
|
456
535
|
key = "Obj#{i}"
|
457
|
-
p =
|
536
|
+
p = store[key]
|
458
537
|
p.name = "#{iteration}:#{i}:" + 'X' * rand(64)
|
459
538
|
end
|
460
539
|
0.upto(count) do |i|
|
461
540
|
key = "Obj#{i}"
|
462
|
-
p =
|
541
|
+
p = store[key]
|
463
542
|
o_it, o_i, o_x = p.name.split(':')
|
464
543
|
if o_it.to_i != iteration
|
465
544
|
$stderr.puts "Mismatch of #{p._id} with value #{o_it}:#{i}"
|
@@ -467,37 +546,41 @@ describe PEROBS::Store do
|
|
467
546
|
expect(o_it.to_i).to eql(iteration)
|
468
547
|
expect(o_i.to_i).to eql(i)
|
469
548
|
end
|
470
|
-
|
549
|
+
capture_io { expect(store.check).to eql(0) }
|
471
550
|
end
|
551
|
+
|
552
|
+
capture_io { store.gc }
|
553
|
+
capture_io { expect { store.check }.to_not raise_error }
|
554
|
+
expect { store.delete_store }.to_not raise_error
|
472
555
|
end
|
473
556
|
|
474
557
|
it 'should survive a real world usage test' do
|
475
558
|
options = { :engine => PEROBS::FlatFileDB }
|
476
|
-
|
559
|
+
store = PEROBS::Store.new(@db_file, options)
|
477
560
|
ref = {}
|
478
561
|
|
479
562
|
deletions_since_last_gc = 0
|
480
|
-
0.upto(
|
563
|
+
0.upto(5000) do |i|
|
481
564
|
key = "o#{i}"
|
482
565
|
case rand(9)
|
483
566
|
when 0
|
484
567
|
# Add 'A' person
|
485
568
|
value = key + 'A' * rand(512)
|
486
|
-
|
569
|
+
store[key] = p = store.new(Person)
|
487
570
|
p.name = value
|
488
571
|
ref[key] = value
|
489
572
|
when 1
|
490
573
|
# Add 'B' person
|
491
574
|
value = key + 'B' * rand(32)
|
492
|
-
|
575
|
+
store[key] = p = store.new(Person)
|
493
576
|
p.name = value
|
494
577
|
ref[key] = value
|
495
578
|
when 2
|
496
579
|
# Delete a root entry
|
497
580
|
if ref.keys.length > 11
|
498
581
|
key = ref.keys[rand(ref.keys.length)]
|
499
|
-
expect(
|
500
|
-
|
582
|
+
expect(store[key]).not_to be_nil
|
583
|
+
store[key] = nil
|
501
584
|
ref.delete(key)
|
502
585
|
deletions_since_last_gc += 1
|
503
586
|
end
|
@@ -505,34 +588,34 @@ describe PEROBS::Store do
|
|
505
588
|
# Update a person entry
|
506
589
|
if ref.keys.length > 0
|
507
590
|
key = ref.keys[rand(ref.keys.length)]
|
508
|
-
expect(
|
591
|
+
expect(store[key]).not_to be_nil
|
509
592
|
value = key + 'C' * rand(996)
|
510
|
-
p =
|
593
|
+
p = store[key]
|
511
594
|
p.name = value
|
512
595
|
ref[key] = value
|
513
596
|
end
|
514
597
|
when 4
|
515
598
|
# Call garbage collector
|
516
599
|
if rand(60) == 0
|
517
|
-
|
518
|
-
stats =
|
600
|
+
capture_io { store.gc }
|
601
|
+
stats = store.statistics
|
519
602
|
expect(stats.marked_objects).to eq(ref.length)
|
520
603
|
expect(stats.swept_objects).to eq(deletions_since_last_gc)
|
521
604
|
deletions_since_last_gc = 0
|
522
|
-
expect(
|
605
|
+
capture_io { expect(store.gc).to eq(deletions_since_last_gc) }
|
523
606
|
end
|
524
607
|
when 5
|
525
608
|
# Sync store and reload
|
526
609
|
if rand(15) == 0
|
527
|
-
|
528
|
-
|
610
|
+
capture_io { store.exit }
|
611
|
+
store = PEROBS::Store.new(@db_file, options)
|
529
612
|
end
|
530
613
|
when 6
|
531
614
|
# Replace an entry with 'C' person
|
532
615
|
if ref.keys.length > 13
|
533
616
|
key = ref.keys[(ref.keys.length / 13).to_i]
|
534
617
|
value = key + 'D' * rand(1024)
|
535
|
-
|
618
|
+
store[key] = p = store.new(Person)
|
536
619
|
p.name = value
|
537
620
|
ref[key] = value
|
538
621
|
deletions_since_last_gc += 1
|
@@ -540,57 +623,65 @@ describe PEROBS::Store do
|
|
540
623
|
when 7
|
541
624
|
# Sync and check store
|
542
625
|
if rand(50) == 0
|
543
|
-
|
544
|
-
expect(
|
626
|
+
#store.sync
|
627
|
+
capture_io { expect(store.check(false)).to eq(0) }
|
545
628
|
end
|
546
629
|
when 8
|
547
630
|
# Compare a random entry with reference entry
|
548
631
|
if ref.keys.length > 0
|
549
632
|
key = ref.keys[rand(ref.keys.length)]
|
550
|
-
expect(
|
633
|
+
expect(store[key].name).to eq(ref[key])
|
551
634
|
end
|
552
635
|
end
|
553
636
|
#ref.each do |k, v|
|
554
|
-
# expect(
|
637
|
+
# expect(store[k].name).to eq(v), "failure in mode #{i}"
|
555
638
|
#end
|
556
639
|
end
|
557
640
|
|
558
641
|
ref.each do |k, v|
|
559
|
-
expect(
|
642
|
+
expect(store[k].name).to eq(v)
|
560
643
|
end
|
644
|
+
|
645
|
+
capture_io { store.gc }
|
646
|
+
capture_io { expect { store.check }.to_not raise_error }
|
647
|
+
expect { store.delete_store }.to_not raise_error
|
561
648
|
end
|
562
649
|
|
563
650
|
it 'should copy the database' do
|
564
|
-
|
565
|
-
|
651
|
+
store = PEROBS::Store.new(@db_file)
|
652
|
+
store['person0'] = p0 = store.new(Person)
|
566
653
|
id0 = p0._id
|
567
|
-
p1 =
|
654
|
+
p1 = store.new(Person)
|
568
655
|
id1 = p1._id
|
569
|
-
p2 =
|
656
|
+
p2 = store.new(Person)
|
570
657
|
id2 = p2._id
|
571
658
|
p1.related = p2
|
572
659
|
p2.related = p1
|
573
660
|
p0.related = p1
|
574
|
-
p3 =
|
575
|
-
|
661
|
+
p3 = store.new(PEROBS::Array)
|
662
|
+
store['persons'] = p3
|
576
663
|
p3 << p0
|
577
664
|
p3 << p1
|
578
665
|
p3 << p2
|
579
666
|
p0 = p1 = p2 = p3 = nil
|
580
|
-
expect(
|
581
|
-
expect(
|
582
|
-
expect(
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
expect(
|
589
|
-
expect(
|
590
|
-
expect(
|
591
|
-
expect(
|
592
|
-
expect(
|
593
|
-
expect(
|
667
|
+
expect(store['person0']._id).to eq(id0)
|
668
|
+
expect(store['person0'].related._id).to eq(id1)
|
669
|
+
expect(store['person0'].related.related._id).to eq(id2)
|
670
|
+
|
671
|
+
store.copy(@db_file_new, { :engine => PEROBS::FlatFileDB })
|
672
|
+
store.delete_store
|
673
|
+
|
674
|
+
store = PEROBS::Store.new(@db_file_new, { :engine => PEROBS::FlatFileDB })
|
675
|
+
expect(store['person0']._id).to eq(id0)
|
676
|
+
expect(store['person0'].related._id).to eq(id1)
|
677
|
+
expect(store['person0'].related.related._id).to eq(id2)
|
678
|
+
expect(store['persons'][0]).to eq(store['person0'])
|
679
|
+
expect(store['persons'][1]).to eq(store['person0'].related)
|
680
|
+
expect(store['persons'][2]).to eq(store['person0'].related.related)
|
681
|
+
|
682
|
+
capture_io { store.gc }
|
683
|
+
capture_io { expect { store.check }.to_not raise_error }
|
684
|
+
expect { store.delete_store }.to_not raise_error
|
594
685
|
end
|
595
686
|
|
596
687
|
end
|