perobs 4.0.0 → 4.4.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/README.md +27 -16
- data/lib/perobs/Array.rb +66 -19
- data/lib/perobs/BTree.rb +106 -15
- data/lib/perobs/BTreeBlob.rb +4 -3
- data/lib/perobs/BTreeDB.rb +5 -4
- data/lib/perobs/BTreeNode.rb +482 -156
- data/lib/perobs/BTreeNodeLink.rb +10 -0
- data/lib/perobs/BigArray.rb +285 -0
- data/lib/perobs/BigArrayNode.rb +1002 -0
- data/lib/perobs/BigHash.rb +246 -0
- data/lib/perobs/BigTree.rb +197 -0
- data/lib/perobs/BigTreeNode.rb +873 -0
- data/lib/perobs/Cache.rb +48 -10
- 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 +155 -50
- data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
- data/lib/perobs/FlatFile.rb +519 -227
- data/lib/perobs/FlatFileBlobHeader.rb +113 -54
- data/lib/perobs/FlatFileDB.rb +49 -23
- data/lib/perobs/FuzzyStringMatcher.rb +175 -0
- data/lib/perobs/Hash.rb +127 -33
- 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 +46 -5
- data/lib/perobs/PersistentObjectCache.rb +57 -68
- data/lib/perobs/PersistentObjectCacheLine.rb +24 -12
- data/lib/perobs/ProgressMeter.rb +97 -0
- data/lib/perobs/SpaceManager.rb +273 -0
- data/lib/perobs/SpaceTree.rb +21 -12
- data/lib/perobs/SpaceTreeNode.rb +53 -61
- data/lib/perobs/Store.rb +264 -145
- data/lib/perobs/version.rb +1 -1
- data/lib/perobs.rb +2 -0
- data/perobs.gemspec +4 -4
- data/test/Array_spec.rb +15 -6
- data/test/BTree_spec.rb +6 -2
- data/test/BigArray_spec.rb +261 -0
- data/test/BigHash_spec.rb +152 -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 +198 -14
- data/test/FuzzyStringMatcher_spec.rb +261 -0
- data/test/Hash_spec.rb +13 -3
- data/test/IDList_spec.rb +77 -0
- data/test/LegacyDBs/LegacyDB.rb +155 -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/SpaceManager_spec.rb +176 -0
- data/test/SpaceTree_spec.rb +4 -1
- data/test/Store_spec.rb +305 -203
- data/test/spec_helper.rb +9 -4
- metadata +57 -16
- data/lib/perobs/BTreeNodeCache.rb +0 -109
- 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(
|
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,228 @@ 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
|
+
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
|
-
|
230
|
-
|
274
|
+
store = PEROBS::Store.new(@db_file)
|
275
|
+
store['person0'] = p0 = store.new(Person)
|
231
276
|
id0 = p0._id
|
232
|
-
p1 =
|
277
|
+
p1 = store.new(Person)
|
233
278
|
id1 = p1._id
|
234
|
-
p2 =
|
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(
|
240
|
-
expect(
|
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
|
-
|
243
|
-
|
244
|
-
|
245
|
-
expect(
|
246
|
-
expect(
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
expect(
|
258
|
-
|
259
|
-
|
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
|
-
|
264
|
-
|
265
|
-
|
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(
|
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
|
-
|
323
|
+
store = PEROBS::Store.new(@db_file)
|
273
324
|
begin
|
274
|
-
|
275
|
-
|
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(
|
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
|
-
|
286
|
-
|
340
|
+
store = PEROBS::Store.new(@db_file)
|
341
|
+
store['person1'] = p1 = store.new(Person)
|
287
342
|
p1.name = 'Joe'
|
288
343
|
begin
|
289
|
-
|
290
|
-
|
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(
|
297
|
-
expect(
|
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
|
-
|
302
|
-
|
303
|
-
|
360
|
+
store = PEROBS::Store.new(@db_file)
|
361
|
+
store.transaction do
|
362
|
+
store['person0'] = p0 = store.new(Person)
|
304
363
|
p0.name = 'Jimmy'
|
305
|
-
|
306
|
-
|
364
|
+
store.transaction do
|
365
|
+
store['person1'] = p1 = store.new(Person)
|
307
366
|
p1.name = 'Joe'
|
308
367
|
end
|
309
368
|
end
|
310
|
-
expect(
|
311
|
-
expect(
|
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
|
-
|
378
|
+
store = PEROBS::Store.new(@db_file)
|
316
379
|
begin
|
317
|
-
|
318
|
-
|
380
|
+
store.transaction do
|
381
|
+
store['person0'] = p0 = store.new(Person)
|
319
382
|
p0.name = 'Jimmy'
|
320
383
|
begin
|
321
|
-
|
322
|
-
|
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(
|
332
|
-
expect(
|
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
|
-
|
403
|
+
store = PEROBS::Store.new(@db_file)
|
337
404
|
begin
|
338
|
-
|
339
|
-
|
405
|
+
store.transaction do
|
406
|
+
store['person0'] = p0 = store.new(Person)
|
340
407
|
p0.name = 'Jimmy'
|
341
|
-
|
342
|
-
|
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(
|
350
|
-
expect(
|
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
|
-
|
355
|
-
|
356
|
-
|
425
|
+
store = PEROBS::Store.new(@db_file)
|
426
|
+
store.transaction do
|
427
|
+
store['person0'] = p0 = store.new(Person)
|
357
428
|
p0.name = 'Jimmy'
|
358
|
-
|
359
|
-
|
429
|
+
store.transaction do
|
430
|
+
store['person1'] = p1 = store.new(Person)
|
360
431
|
p1.name = 'Joe'
|
361
|
-
|
362
|
-
|
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(
|
368
|
-
expect(
|
369
|
-
expect(
|
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
|
-
|
374
|
-
|
375
|
-
|
448
|
+
store = PEROBS::Store.new(@db_file)
|
449
|
+
store.transaction do
|
450
|
+
store['person0'] = p0 = store.new(Person)
|
376
451
|
p0.name = 'Jimmy'
|
377
|
-
|
378
|
-
|
452
|
+
store.transaction do
|
453
|
+
store['person1'] = p1 = store.new(Person)
|
379
454
|
p1.name = 'Joe'
|
380
455
|
begin
|
381
|
-
|
382
|
-
|
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(
|
391
|
-
expect(
|
392
|
-
expect(
|
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
|
-
|
397
|
-
|
398
|
-
|
475
|
+
store = PEROBS::Store.new(@db_file)
|
476
|
+
store.transaction do
|
477
|
+
store['person0'] = p0 = store.new(Person)
|
399
478
|
p0.name = 'Jimmy'
|
400
|
-
|
401
|
-
|
479
|
+
store.transaction do
|
480
|
+
store['person1'] = p1 = store.new(Person)
|
402
481
|
p1.name = 'Joe'
|
403
482
|
begin
|
404
|
-
|
405
|
-
|
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(
|
415
|
-
expect(
|
416
|
-
expect(
|
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
|
-
|
421
|
-
expect(
|
422
|
-
|
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(
|
425
|
-
|
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(
|
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
|
-
|
435
|
-
|
436
|
-
|
437
|
-
expect(
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
expect(
|
442
|
-
expect(
|
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
|
-
|
536
|
+
store = PEROBS::Store.new(@db_file)
|
447
537
|
count = 10000
|
448
538
|
0.upto(count) do |i|
|
449
539
|
key = "Obj#{i}"
|
450
|
-
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
570
|
+
store = PEROBS::Store.new(@db_file, options)
|
477
571
|
ref = {}
|
478
572
|
|
479
573
|
deletions_since_last_gc = 0
|
480
|
-
0.upto(
|
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
|
-
|
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
|
-
|
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(
|
500
|
-
|
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(
|
602
|
+
expect(store[key]).not_to be_nil
|
509
603
|
value = key + 'C' * rand(996)
|
510
|
-
p =
|
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
|
-
|
518
|
-
stats =
|
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(
|
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
|
-
|
528
|
-
|
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
|
-
|
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
|
-
|
544
|
-
expect(
|
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(
|
644
|
+
expect(store[key].name).to eq(ref[key])
|
551
645
|
end
|
552
646
|
end
|
553
647
|
#ref.each do |k, v|
|
554
|
-
# expect(
|
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(
|
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
|
-
|
565
|
-
|
662
|
+
store = PEROBS::Store.new(@db_file)
|
663
|
+
store['person0'] = p0 = store.new(Person)
|
566
664
|
id0 = p0._id
|
567
|
-
p1 =
|
665
|
+
p1 = store.new(Person)
|
568
666
|
id1 = p1._id
|
569
|
-
p2 =
|
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 =
|
575
|
-
|
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(
|
581
|
-
expect(
|
582
|
-
expect(
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
expect(
|
589
|
-
expect(
|
590
|
-
expect(
|
591
|
-
expect(
|
592
|
-
expect(
|
593
|
-
expect(
|
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
|