perobs 3.0.1 → 4.3.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 +19 -18
- data/lib/perobs.rb +2 -0
- data/lib/perobs/Array.rb +68 -21
- data/lib/perobs/BTree.rb +110 -54
- data/lib/perobs/BTreeBlob.rb +14 -13
- data/lib/perobs/BTreeDB.rb +11 -10
- data/lib/perobs/BTreeNode.rb +551 -197
- data/lib/perobs/BTreeNodeCache.rb +10 -8
- data/lib/perobs/BTreeNodeLink.rb +11 -1
- 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 +47 -22
- data/lib/perobs/ClassMap.rb +2 -2
- data/lib/perobs/ConsoleProgressMeter.rb +61 -0
- data/lib/perobs/DataBase.rb +4 -3
- data/lib/perobs/DynamoDB.rb +62 -20
- data/lib/perobs/EquiBlobsFile.rb +174 -59
- data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
- data/lib/perobs/FlatFile.rb +536 -242
- data/lib/perobs/FlatFileBlobHeader.rb +120 -84
- data/lib/perobs/FlatFileDB.rb +58 -27
- data/lib/perobs/FuzzyStringMatcher.rb +175 -0
- data/lib/perobs/Hash.rb +129 -35
- 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/LockFile.rb +3 -0
- data/lib/perobs/Object.rb +28 -20
- data/lib/perobs/ObjectBase.rb +53 -10
- data/lib/perobs/PersistentObjectCache.rb +142 -0
- data/lib/perobs/PersistentObjectCacheLine.rb +99 -0
- data/lib/perobs/ProgressMeter.rb +97 -0
- data/lib/perobs/SpaceManager.rb +273 -0
- data/lib/perobs/SpaceTree.rb +63 -47
- data/lib/perobs/SpaceTreeNode.rb +134 -115
- data/lib/perobs/SpaceTreeNodeLink.rb +1 -1
- data/lib/perobs/StackFile.rb +1 -1
- data/lib/perobs/Store.rb +180 -70
- data/lib/perobs/version.rb +1 -1
- data/perobs.gemspec +4 -4
- data/test/Array_spec.rb +48 -39
- data/test/BTreeDB_spec.rb +2 -2
- data/test/BTree_spec.rb +50 -1
- 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 -5
- data/test/FNV_Hash_1a_64_spec.rb +59 -0
- data/test/FlatFileDB_spec.rb +199 -15
- data/test/FuzzyStringMatcher_spec.rb +261 -0
- data/test/Hash_spec.rb +27 -16
- 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/Object_spec.rb +5 -5
- data/test/SpaceManager_spec.rb +176 -0
- data/test/SpaceTree_spec.rb +27 -9
- data/test/Store_spec.rb +353 -206
- data/test/perobs_spec.rb +7 -3
- data/test/spec_helper.rb +9 -4
- metadata +59 -16
- data/lib/perobs/SpaceTreeNodeCache.rb +0 -76
- data/lib/perobs/TreeDB.rb +0 -277
    
        data/test/EquiBlobsFile_spec.rb
    CHANGED
    
    | @@ -27,13 +27,17 @@ require 'fileutils' | |
| 27 27 |  | 
| 28 28 | 
             
            require 'spec_helper'
         | 
| 29 29 | 
             
            require 'perobs/EquiBlobsFile'
         | 
| 30 | 
            +
            require 'perobs/ProgressMeter'
         | 
| 30 31 |  | 
| 31 32 | 
             
            describe PEROBS::EquiBlobsFile do
         | 
| 32 33 |  | 
| 33 34 | 
             
              before(:all) do
         | 
| 35 | 
            +
                PEROBS.log.level = Logger::ERROR
         | 
| 36 | 
            +
                PEROBS.log.open($stderr)
         | 
| 34 37 | 
             
                @db_dir = generate_db_name('EquiBlobsFile')
         | 
| 35 38 | 
             
                FileUtils.mkdir_p(@db_dir)
         | 
| 36 | 
            -
                @bf = PEROBS::EquiBlobsFile.new(@db_dir, 'EquiBlobsFile', | 
| 39 | 
            +
                @bf = PEROBS::EquiBlobsFile.new(@db_dir, 'EquiBlobsFile',
         | 
| 40 | 
            +
                                                PEROBS::ProgressMeter.new, 8)
         | 
| 37 41 | 
             
              end
         | 
| 38 42 |  | 
| 39 43 | 
             
              after(:all) do
         | 
| @@ -134,6 +138,12 @@ describe PEROBS::EquiBlobsFile do | |
| 134 138 | 
             
                expect(@bf.check).to be true
         | 
| 135 139 | 
             
              end
         | 
| 136 140 |  | 
| 141 | 
            +
              it 'should support deleting reserved blobs' do
         | 
| 142 | 
            +
                adr = @bf.free_address
         | 
| 143 | 
            +
                @bf.delete_blob(adr)
         | 
| 144 | 
            +
                expect(@bf.check).to be true
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
             | 
| 137 147 | 
             
              it 'should support clearing the file' do
         | 
| 138 148 | 
             
                @bf.clear
         | 
| 139 149 | 
             
                expect(@bf.total_entries).to eql(0)
         | 
| @@ -179,10 +189,6 @@ describe PEROBS::EquiBlobsFile do | |
| 179 189 | 
             
                expect(@bf.check).to be true
         | 
| 180 190 | 
             
              end
         | 
| 181 191 |  | 
| 182 | 
            -
              it 'should not allow erase when open' do
         | 
| 183 | 
            -
                expect{ capture_io{ @bf.erase } }.to raise_error(PEROBS::FatalError)
         | 
| 184 | 
            -
              end
         | 
| 185 | 
            -
             | 
| 186 192 | 
             
              it 'should support erasing the file' do
         | 
| 187 193 | 
             
                @bf.close
         | 
| 188 194 | 
             
                @bf.erase
         | 
| @@ -195,5 +201,99 @@ describe PEROBS::EquiBlobsFile do | |
| 195 201 | 
             
                expect(@bf.check).to be true
         | 
| 196 202 | 
             
              end
         | 
| 197 203 |  | 
| 204 | 
            +
              it 'should support custom offsets' do
         | 
| 205 | 
            +
                @bf.close
         | 
| 206 | 
            +
                @bf.erase
         | 
| 207 | 
            +
                @bf.clear_custom_data
         | 
| 208 | 
            +
                @bf.register_custom_data('foo', 42)
         | 
| 209 | 
            +
                @bf.register_custom_data('bar', 43)
         | 
| 210 | 
            +
                @bf.open
         | 
| 211 | 
            +
                expect(@bf.total_entries).to eql(0)
         | 
| 212 | 
            +
                expect(@bf.total_spaces).to eql(0)
         | 
| 213 | 
            +
                expect(@bf.check).to be true
         | 
| 214 | 
            +
                expect(@bf.free_address).to eql(1)
         | 
| 215 | 
            +
                @bf.store_blob(1,'11111111')
         | 
| 216 | 
            +
                expect(@bf.free_address).to eql(2)
         | 
| 217 | 
            +
                @bf.store_blob(2,'22222222')
         | 
| 218 | 
            +
                expect(@bf.free_address).to eql(3)
         | 
| 219 | 
            +
                @bf.store_blob(3,'33333333')
         | 
| 220 | 
            +
                expect(@bf.check).to be true
         | 
| 221 | 
            +
                expect(@bf.total_entries).to eql(3)
         | 
| 222 | 
            +
                expect(@bf.total_spaces).to eql(0)
         | 
| 223 | 
            +
                @bf.delete_blob(2)
         | 
| 224 | 
            +
                expect(@bf.check).to be true
         | 
| 225 | 
            +
                expect(@bf.total_entries).to eql(2)
         | 
| 226 | 
            +
                expect(@bf.total_spaces).to eql(1)
         | 
| 227 | 
            +
                expect(@bf.free_address).to eql(2)
         | 
| 228 | 
            +
                @bf.store_blob(2,'44444444')
         | 
| 229 | 
            +
                expect(@bf.total_entries).to eql(3)
         | 
| 230 | 
            +
                expect(@bf.total_spaces).to eql(0)
         | 
| 231 | 
            +
                expect(@bf.check).to be true
         | 
| 232 | 
            +
              end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
              it 'should support a mix of adds and deletes' do
         | 
| 235 | 
            +
                @bf.close
         | 
| 236 | 
            +
                @bf.erase
         | 
| 237 | 
            +
                @bf.clear_custom_data
         | 
| 238 | 
            +
                @bf.register_custom_data('foo', 42)
         | 
| 239 | 
            +
                @bf.register_custom_data('bar', 43)
         | 
| 240 | 
            +
                @bf.open
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                entries = {}
         | 
| 243 | 
            +
                1000.times do |i|
         | 
| 244 | 
            +
                  rand(30).times do
         | 
| 245 | 
            +
                    adr = @bf.free_address
         | 
| 246 | 
            +
                    expect(entries[adr]).to be nil
         | 
| 247 | 
            +
                    val = rand(2 ** 64)
         | 
| 248 | 
            +
                    @bf.store_blob(adr, [ val ].pack('Q'))
         | 
| 249 | 
            +
                    entries[adr] = val
         | 
| 250 | 
            +
                    #expect(@bf.check).to be true
         | 
| 251 | 
            +
                  end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                  rand(15).times do
         | 
| 254 | 
            +
                    unless entries.empty?
         | 
| 255 | 
            +
                      addresses = entries.keys
         | 
| 256 | 
            +
                      adr = addresses[rand(addresses.length)]
         | 
| 257 | 
            +
                      expect(@bf.retrieve_blob(adr).unpack('Q').first).to eql(entries[adr])
         | 
| 258 | 
            +
                      @bf.delete_blob(adr)
         | 
| 259 | 
            +
                      entries.delete(adr)
         | 
| 260 | 
            +
                      #expect(@bf.check).to be true
         | 
| 261 | 
            +
                    end
         | 
| 262 | 
            +
                  end
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                  rand(5).times do
         | 
| 265 | 
            +
                    unless entries.empty?
         | 
| 266 | 
            +
                      addresses = entries.keys
         | 
| 267 | 
            +
                      adr = addresses[rand(addresses.length)]
         | 
| 268 | 
            +
                      val = rand(2 ** 64)
         | 
| 269 | 
            +
                      @bf.store_blob(adr, [ val ].pack('Q'))
         | 
| 270 | 
            +
                      entries[adr] = val
         | 
| 271 | 
            +
                      #expect(@bf.check).to be true
         | 
| 272 | 
            +
                    end
         | 
| 273 | 
            +
                  end
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                  if rand(100) == 0
         | 
| 276 | 
            +
                    expect(@bf.check).to be true
         | 
| 277 | 
            +
                  end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                  if rand(100) == 0
         | 
| 280 | 
            +
                    @bf.first_entry = i
         | 
| 281 | 
            +
                    @bf.set_custom_data('foo', i)
         | 
| 282 | 
            +
                  end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                  if rand(50) == 0
         | 
| 285 | 
            +
                    @bf.close
         | 
| 286 | 
            +
                    @bf.open
         | 
| 287 | 
            +
                  end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                  if rand(500) == 0
         | 
| 290 | 
            +
                    @bf.clear
         | 
| 291 | 
            +
                    entries = {}
         | 
| 292 | 
            +
                  end
         | 
| 293 | 
            +
                end
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                expect(@bf.check).to be true
         | 
| 296 | 
            +
              end
         | 
| 297 | 
            +
             | 
| 198 298 | 
             
            end
         | 
| 199 299 |  | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Copyright (c) 2019 by Chris Schlaeger <chris@taskjuggler.org>
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # This file contains tests for Array that are similar to the tests for the
         | 
| 6 | 
            +
            # Array implementation in MRI. The ideas of these tests were replicated in
         | 
| 7 | 
            +
            # this code.
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # MIT License
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Permission is hereby granted, free of charge, to any person obtaining
         | 
| 12 | 
            +
            # a copy of this software and associated documentation files (the
         | 
| 13 | 
            +
            # "Software"), to deal in the Software without restriction, including
         | 
| 14 | 
            +
            # without limitation the rights to use, copy, modify, merge, publish,
         | 
| 15 | 
            +
            # distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 16 | 
            +
            # permit persons to whom the Software is furnished to do so, subject to
         | 
| 17 | 
            +
            # the following conditions:
         | 
| 18 | 
            +
            #
         | 
| 19 | 
            +
            # The above copyright notice and this permission notice shall be
         | 
| 20 | 
            +
            # included in all copies or substantial portions of the Software.
         | 
| 21 | 
            +
            #
         | 
| 22 | 
            +
            # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 23 | 
            +
            # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 24 | 
            +
            # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 25 | 
            +
            # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 26 | 
            +
            # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 27 | 
            +
            # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 28 | 
            +
            # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            require 'spec_helper'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            require 'perobs'
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            describe PEROBS::FNV_Hash_1a_64 do
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              it 'should generate stable hashes for Strings' do
         | 
| 37 | 
            +
                refs = [
         | 
| 38 | 
            +
                  [ 'foo', 15902901984413996407 ],
         | 
| 39 | 
            +
                  [ 'foo', 15902901984413996407 ],
         | 
| 40 | 
            +
                  [ 'bar', 16101355973854746 ],
         | 
| 41 | 
            +
                  [ 'foobar', 9625390261332436968 ],
         | 
| 42 | 
            +
                  [ 'PEROBS rocks your application!', 4089220442501866848 ],
         | 
| 43 | 
            +
                  [ 'Permission is hereby granted, free of charge, to any person ' +
         | 
| 44 | 
            +
                    'obtaining a copy of this software and associated documentation ' +
         | 
| 45 | 
            +
                    'files (the "Software"), to deal in the Software without ' +
         | 
| 46 | 
            +
                    'restriction, including without limitation the rights to use, ' +
         | 
| 47 | 
            +
                    'copy, modify, merge, publish, distribute, sublicense, and/or ' +
         | 
| 48 | 
            +
                    'sell copies of the Software, and to permit persons to whom the ' +
         | 
| 49 | 
            +
                    'Software is furnished to do so, subject to the following conditions:',
         | 
| 50 | 
            +
                    17637146001033534275 ]
         | 
| 51 | 
            +
                ]
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                refs.each do |v|
         | 
| 54 | 
            +
                  expect(PEROBS::FNV_Hash_1a_64::digest(v[0])).to eql(v[1])
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            end
         | 
| 59 | 
            +
             | 
    
        data/test/FlatFileDB_spec.rb
    CHANGED
    
    | @@ -28,10 +28,11 @@ require 'fileutils' | |
| 28 28 | 
             
            require 'spec_helper'
         | 
| 29 29 | 
             
            require 'perobs/FlatFileDB'
         | 
| 30 30 | 
             
            require 'perobs/Store'
         | 
| 31 | 
            +
            require 'LegacyDBs/LegacyDB'
         | 
| 31 32 |  | 
| 32 33 | 
             
            class FlatFileDB_O < PEROBS::Object
         | 
| 33 34 |  | 
| 34 | 
            -
               | 
| 35 | 
            +
              attr_persist :a, :b, :c
         | 
| 35 36 |  | 
| 36 37 | 
             
              def initialize(store)
         | 
| 37 38 | 
             
                super
         | 
| @@ -47,7 +48,12 @@ describe PEROBS::FlatFileDB do | |
| 47 48 | 
             
              before(:each) do
         | 
| 48 49 | 
             
                @db_dir = generate_db_name(__FILE__)
         | 
| 49 50 | 
             
                FileUtils.mkdir_p(@db_dir)
         | 
| 50 | 
            -
                @ | 
| 51 | 
            +
                @store_options = {
         | 
| 52 | 
            +
                  :engine => PEROBS::FlatFileDB,
         | 
| 53 | 
            +
                  :log => $stderr,
         | 
| 54 | 
            +
                  :log_level => Logger::ERROR
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
                @store = PEROBS::Store.new(@db_dir, @store_options)
         | 
| 51 57 | 
             
              end
         | 
| 52 58 |  | 
| 53 59 | 
             
              after(:each) do
         | 
| @@ -65,19 +71,30 @@ describe PEROBS::FlatFileDB do | |
| 65 71 | 
             
                expect { db2.open }.to raise_error(PEROBS::FatalError)
         | 
| 66 72 | 
             
              end
         | 
| 67 73 |  | 
| 68 | 
            -
              it 'should do a version upgrade' do
         | 
| 74 | 
            +
              it 'should do a version upgrade from version 3' do
         | 
| 69 75 | 
             
                # Close the store
         | 
| 70 | 
            -
                @store['o'] = @store.new(FlatFileDB_O)
         | 
| 71 76 | 
             
                @store.exit
         | 
| 77 | 
            +
                src_dir = File.join(File.dirname(__FILE__), 'LegacyDBs', 'version_3')
         | 
| 78 | 
            +
                FileUtils.cp_r(Dir.glob(src_dir + '/*'), @db_dir)
         | 
| 72 79 |  | 
| 73 | 
            -
                 | 
| 74 | 
            -
                 | 
| 75 | 
            -
                 | 
| 80 | 
            +
                db = LegacyDB.new(@db_dir)
         | 
| 81 | 
            +
                capture_io { db.open }
         | 
| 82 | 
            +
                capture_io { expect(db.check).to be true }
         | 
| 83 | 
            +
              end
         | 
| 76 84 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
                 | 
| 79 | 
            -
                 | 
| 80 | 
            -
                 | 
| 85 | 
            +
              it 'should do a version upgrade from version 4.1' do
         | 
| 86 | 
            +
                # Close the store
         | 
| 87 | 
            +
                @store.exit
         | 
| 88 | 
            +
                src_dir = File.join(File.dirname(__FILE__), 'LegacyDBs', 'version_4.1')
         | 
| 89 | 
            +
                FileUtils.cp_r(Dir.glob(src_dir + '/*'), @db_dir)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                db = LegacyDB.new(@db_dir)
         | 
| 92 | 
            +
                capture_io { db.open }
         | 
| 93 | 
            +
                capture_io { expect(db.repair).to eql(0) }
         | 
| 94 | 
            +
                expect(File.exist?(File.join(@db_dir, 'space_index.blobs'))).to be true
         | 
| 95 | 
            +
                expect(File.exist?(File.join(@db_dir, 'space_list.blobs'))).to be true
         | 
| 96 | 
            +
                expect(File.exist?(File.join(@db_dir, 'database_spaces.blobs'))).to be false
         | 
| 97 | 
            +
                capture_io { expect(db.check).to be true }
         | 
| 81 98 | 
             
              end
         | 
| 82 99 |  | 
| 83 100 | 
             
              it 'should refuse a version downgrade' do
         | 
| @@ -89,7 +106,7 @@ describe PEROBS::FlatFileDB do | |
| 89 106 | 
             
                File.write(version_file, '1000000')
         | 
| 90 107 |  | 
| 91 108 | 
             
                # Open the store again
         | 
| 92 | 
            -
                expect { PEROBS::Store.new(@db_dir | 
| 109 | 
            +
                expect { PEROBS::Store.new(@db_dir) }.to raise_error(PEROBS::FatalError)
         | 
| 93 110 | 
             
              end
         | 
| 94 111 |  | 
| 95 112 | 
             
              it 'should recover from a lost index file' do
         | 
| @@ -97,7 +114,10 @@ describe PEROBS::FlatFileDB do | |
| 97 114 | 
             
                @store.exit
         | 
| 98 115 |  | 
| 99 116 | 
             
                File.delete(File.join(@db_dir, 'index.blobs'))
         | 
| 100 | 
            -
                store =  | 
| 117 | 
            +
                store = nil
         | 
| 118 | 
            +
                capture_io do
         | 
| 119 | 
            +
                  store = PEROBS::Store.new(@db_dir)
         | 
| 120 | 
            +
                end
         | 
| 101 121 | 
             
                expect(store['o'].b).to eql(42)
         | 
| 102 122 | 
             
              end
         | 
| 103 123 |  | 
| @@ -106,10 +126,174 @@ describe PEROBS::FlatFileDB do | |
| 106 126 | 
             
                @store.exit
         | 
| 107 127 |  | 
| 108 128 | 
             
                File.write(File.join(@db_dir, 'index.blobs'), '*' * 500)
         | 
| 109 | 
            -
                store =  | 
| 110 | 
            -
                 | 
| 129 | 
            +
                store = nil
         | 
| 130 | 
            +
                capture_io do
         | 
| 131 | 
            +
                  store = PEROBS::Store.new(@db_dir)
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
                capture_io { store.check(true) }
         | 
| 111 134 | 
             
                expect(store['o'].b).to eql(42)
         | 
| 112 135 | 
             
              end
         | 
| 113 136 |  | 
| 137 | 
            +
              it 'should discard a corrupted blob inside the database.blobs file' do
         | 
| 138 | 
            +
                @store.exit
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir)
         | 
| 141 | 
            +
                db_file = File.join(@db_dir, 'database.blobs')
         | 
| 142 | 
            +
                db.open
         | 
| 143 | 
            +
                0.upto(5) do |i|
         | 
| 144 | 
            +
                  db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}", i + 1)
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
                db.close
         | 
| 147 | 
            +
                db.open
         | 
| 148 | 
            +
                0.upto(5) do |i|
         | 
| 149 | 
            +
                  db.put_object("#{i + 10}:#{'Y' * (i + 1) * 25}", i + 10)
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
                pos = db.instance_variable_get('@flat_file').find_obj_addr_by_id(10)
         | 
| 152 | 
            +
                db.close
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                f = File.open(db_file, 'rb+')
         | 
| 155 | 
            +
                f.seek(pos)
         | 
| 156 | 
            +
                f.write('ZZZZZ')
         | 
| 157 | 
            +
                f.close
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                db.open
         | 
| 160 | 
            +
                expect(db.check_db).to eql(1)
         | 
| 161 | 
            +
                expect(db.check_db(true)).to eql(1)
         | 
| 162 | 
            +
                db.close
         | 
| 163 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
         | 
| 164 | 
            +
                                                       :log_level => Logger::ERROR })
         | 
| 165 | 
            +
                db.open
         | 
| 166 | 
            +
                expect(db.check_db).to eql(0)
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                0.upto(5) do |i|
         | 
| 169 | 
            +
                  expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}")
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
                expect(db.get_object(10)).to be_nil
         | 
| 172 | 
            +
                1.upto(5) do |i|
         | 
| 173 | 
            +
                  expect(db.get_object(i + 10)).to eql("#{i + 10}:#{'Y' * (i + 1) * 25}")
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
                db.close
         | 
| 176 | 
            +
              end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
              it 'should discard a corrupted blob at the end of the database.blobs file' do
         | 
| 179 | 
            +
                @store.exit
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir)
         | 
| 182 | 
            +
                db_file = File.join(@db_dir, 'database.blobs')
         | 
| 183 | 
            +
                db.open
         | 
| 184 | 
            +
                0.upto(5) do |i|
         | 
| 185 | 
            +
                  db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
                db.close
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                f = File.truncate(db_file, File.size(db_file) - 20)
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                db.open
         | 
| 192 | 
            +
                expect(db.check_db).to eql(2)
         | 
| 193 | 
            +
                expect(db.check_db(true)).to eql(2)
         | 
| 194 | 
            +
                db.close
         | 
| 195 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
         | 
| 196 | 
            +
                                                       :log_level => Logger::ERROR })
         | 
| 197 | 
            +
                db.open
         | 
| 198 | 
            +
                expect(db.check_db).to eql(0)
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                0.upto(4) do |i|
         | 
| 201 | 
            +
                  expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
         | 
| 202 | 
            +
                end
         | 
| 203 | 
            +
                expect(db.get_object(6)).to be_nil
         | 
| 204 | 
            +
                db.close
         | 
| 205 | 
            +
              end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
              it 'should discard a corrupted header at the end of the database.blobs file' do
         | 
| 208 | 
            +
                @store.exit
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir)
         | 
| 211 | 
            +
                db_file = File.join(@db_dir, 'database.blobs')
         | 
| 212 | 
            +
                db.open
         | 
| 213 | 
            +
                0.upto(5) do |i|
         | 
| 214 | 
            +
                  db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
         | 
| 215 | 
            +
                end
         | 
| 216 | 
            +
                db.close
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                f = File.truncate(db_file, File.size(db_file) - 200)
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                db.open
         | 
| 221 | 
            +
                expect(db.check_db).to eql(1)
         | 
| 222 | 
            +
                expect(db.check_db(true)).to eql(1)
         | 
| 223 | 
            +
                db.close
         | 
| 224 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
         | 
| 225 | 
            +
                                                       :log_level => Logger::ERROR })
         | 
| 226 | 
            +
                db.open
         | 
| 227 | 
            +
                expect(db.check_db).to eql(0)
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                0.upto(4) do |i|
         | 
| 230 | 
            +
                  expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
         | 
| 231 | 
            +
                end
         | 
| 232 | 
            +
                expect(db.get_object(6)).to be_nil
         | 
| 233 | 
            +
                db.close
         | 
| 234 | 
            +
              end
         | 
| 235 | 
            +
             | 
| 236 | 
            +
              it 'should handle a lost blob at the end of the database.blobs file' do
         | 
| 237 | 
            +
                @store.exit
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir)
         | 
| 240 | 
            +
                db_file = File.join(@db_dir, 'database.blobs')
         | 
| 241 | 
            +
                db.open
         | 
| 242 | 
            +
                0.upto(5) do |i|
         | 
| 243 | 
            +
                  db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
         | 
| 244 | 
            +
                end
         | 
| 245 | 
            +
                db.close
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                # This exactly removes the last blob (#6)
         | 
| 248 | 
            +
                f = File.truncate(db_file, File.size(db_file) - 210)
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                db.open
         | 
| 251 | 
            +
                expect(db.check_db).to eql(1)
         | 
| 252 | 
            +
                # The repair won't find the missing blob since the blob file is without
         | 
| 253 | 
            +
                # errors.
         | 
| 254 | 
            +
                expect(db.check_db(true)).to eql(0)
         | 
| 255 | 
            +
                db.close
         | 
| 256 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
         | 
| 257 | 
            +
                                                       :log_level => Logger::ERROR })
         | 
| 258 | 
            +
                db.open
         | 
| 259 | 
            +
                expect(db.check_db).to eql(0)
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                0.upto(4) do |i|
         | 
| 262 | 
            +
                  expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
         | 
| 263 | 
            +
                end
         | 
| 264 | 
            +
                expect(db.get_object(6)).to be_nil
         | 
| 265 | 
            +
                db.close
         | 
| 266 | 
            +
              end
         | 
| 267 | 
            +
             | 
| 268 | 
            +
              it 'should handle duplicate entries for the same ID in database.blobs file' do
         | 
| 269 | 
            +
                @store.exit
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir)
         | 
| 272 | 
            +
                db_file = File.join(@db_dir, 'database.blobs')
         | 
| 273 | 
            +
                db.open
         | 
| 274 | 
            +
                0.upto(5) do |i|
         | 
| 275 | 
            +
                  db.put_object("#{i + 1}:#{'X' * (i + 1) * 30}$", i + 1)
         | 
| 276 | 
            +
                end
         | 
| 277 | 
            +
                db.close
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                # This appends the entry 2 again
         | 
| 280 | 
            +
                blob2 = File.read(db_file, 319 - 199, 199)
         | 
| 281 | 
            +
                File.write(db_file, blob2, File.size(db_file))
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                db.open
         | 
| 284 | 
            +
                expect(db.check_db).to eql(2)
         | 
| 285 | 
            +
                expect(db.check_db(true)).to eql(1)
         | 
| 286 | 
            +
                db.close
         | 
| 287 | 
            +
                db = PEROBS::FlatFileDB.new(@db_dir, { :log => $stderr,
         | 
| 288 | 
            +
                                                       :log_level => Logger::WARN })
         | 
| 289 | 
            +
                db.open
         | 
| 290 | 
            +
                expect(db.check_db).to eql(0)
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                0.upto(5) do |i|
         | 
| 293 | 
            +
                  expect(db.get_object(i + 1)).to eql("#{i + 1}:#{'X' * (i + 1) * 30}$")
         | 
| 294 | 
            +
                end
         | 
| 295 | 
            +
                db.close
         | 
| 296 | 
            +
              end
         | 
| 297 | 
            +
             | 
| 114 298 | 
             
            end
         | 
| 115 299 |  |