perobs 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|