perobs 4.0.0 → 4.4.0

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