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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c2c526c97aab15c09f8e8acbcb432203e7d60fef8c504df6ddb0b8f35459cca0
|
4
|
+
data.tar.gz: ad077bb879c041289c7a474e87645064ff84203c674640aff59b5f23923ac1b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b86fc662ef8bbf623ee7da777f023a74e3db9502df18c45dcda9ce244982225aaec7e74ce25378147c807775765c57d801810ac16be01d2145227bb0642caa3b
|
7
|
+
data.tar.gz: d0c5a79eb60a1221bc385a1fafbc77a1aaa1e47782b3ea27ca92f5f7c539804bc00a388fdddc1ab956d7805dbdd52d055f5cdd76a8bb155ec5ada4335b7b9175
|
data/README.md
CHANGED
@@ -6,11 +6,12 @@ them from PEROBS::Object. They will be in memory when needed and
|
|
6
6
|
transparently stored into a persistent storage.
|
7
7
|
|
8
8
|
This library is ideal for Ruby applications that work on huge, mostly
|
9
|
-
|
9
|
+
static data sets and usually process a small subset of the data at a
|
10
10
|
time. To ensure data consistency of a larger data set, you can use
|
11
11
|
transactions to make modifications of multiple objects atomicaly.
|
12
12
|
Transactions can be nested and are aborted when an exception is
|
13
|
-
raised.
|
13
|
+
raised. PEROBS is thread-safe, so you can use it in a multi-threaded
|
14
|
+
application.
|
14
15
|
|
15
16
|
## Usage
|
16
17
|
|
@@ -108,7 +109,7 @@ class Person < PEROBS::Object
|
|
108
109
|
attr_init(:father) do { @store.new(Person, 'Dad') }
|
109
110
|
end
|
110
111
|
|
111
|
-
def
|
112
|
+
def marry(spouse)
|
112
113
|
self.spouse = spouse
|
113
114
|
self.status = :married
|
114
115
|
end
|
@@ -120,15 +121,18 @@ class Person < PEROBS::Object
|
|
120
121
|
|
121
122
|
end
|
122
123
|
|
123
|
-
|
124
|
-
store
|
125
|
-
store['
|
126
|
-
|
127
|
-
jim
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
124
|
+
begin
|
125
|
+
store = PEROBS::Store.new('family')
|
126
|
+
store['grandpa'] = joe = store.new(Person, 'Joe')
|
127
|
+
store['grandma'] = jane = store.new(Person, 'Jane')
|
128
|
+
jim = store.new(Person, 'Jim')
|
129
|
+
jim.father = joe
|
130
|
+
joe.kids << jim
|
131
|
+
jim.mother = jane
|
132
|
+
jane.kids << jim
|
133
|
+
ensure
|
134
|
+
store.exit
|
135
|
+
end
|
132
136
|
```
|
133
137
|
|
134
138
|
When you run this script, a folder named 'family' will be created. It
|
@@ -166,9 +170,15 @@ object to another object.
|
|
166
170
|
|
167
171
|
### Caveats and known issues
|
168
172
|
|
169
|
-
|
170
|
-
|
171
|
-
|
173
|
+
You cannot simultaneously access the database from multiple
|
174
|
+
applications concurrently. The library uses locks to ensure that only
|
175
|
+
one Store object is accessing the database at a time.
|
176
|
+
|
177
|
+
In case the application terminates without calling Store::exit(), the
|
178
|
+
database or the database index could get corrupted. To check the
|
179
|
+
consistency of your database you can use Store::check(). To check and
|
180
|
+
repair the database you can call Store::repair(). Depending on the
|
181
|
+
size of your database, these operations can last minutes to hours.
|
172
182
|
|
173
183
|
## Installation
|
174
184
|
|
@@ -188,7 +198,8 @@ Or install it yourself as:
|
|
188
198
|
|
189
199
|
## Copyright and License
|
190
200
|
|
191
|
-
Copyright (c) 2015, 2016, 2017
|
201
|
+
Copyright (c) 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 by
|
202
|
+
Chris Schlaeger <chris@taskjuggler.org>
|
192
203
|
|
193
204
|
PEROBS and all accompanying files are licensed under this MIT License
|
194
205
|
|
data/lib/perobs/Array.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# = Array.rb -- Persistent Ruby Object Store
|
4
4
|
#
|
5
|
-
# Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
|
5
|
+
# Copyright (c) 2015, 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
|
6
6
|
#
|
7
7
|
# MIT License
|
8
8
|
#
|
@@ -44,19 +44,27 @@ module PEROBS
|
|
44
44
|
|
45
45
|
attr_reader :data
|
46
46
|
|
47
|
-
# These methods do not mutate the Array
|
48
|
-
# operations.
|
47
|
+
# These methods do not mutate the Array but create a new PEROBS::Array
|
48
|
+
# object. They only perform read operations.
|
49
49
|
([
|
50
|
-
:&,
|
51
|
-
:
|
50
|
+
:|, :&, :+, :-, :collect, :compact, :drop, :drop_while,
|
51
|
+
:flatten, :map, :reject, :reverse, :rotate, :select, :shuffle, :slice,
|
52
|
+
:sort, :take, :take_while, :uniq, :values_at
|
53
|
+
] + Enumerable.instance_methods).uniq.each do |method_sym|
|
54
|
+
define_method(method_sym) do |*args, &block|
|
55
|
+
@store.cache.cache_read(self)
|
56
|
+
@store.new(PEROBS::Array, @data.send(method_sym, *args, &block))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# These methods do not mutate the Array and only perform read operations.
|
61
|
+
# They do not return basic objects types.
|
62
|
+
([
|
63
|
+
:==, :[], :<=>, :at, :bsearch, :bsearch_index, :count, :cycle,
|
52
64
|
:each, :each_index, :empty?, :eql?, :fetch, :find_index, :first,
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:repeated_permutation, :reverse, :reverse_each, :rindex, :rotate,
|
57
|
-
:sample, :select, :shelljoin, :shuffle, :size, :slice, :sort, :take,
|
58
|
-
:take_while, :to_a, :to_ary, :to_s, :transpose, :uniq, :values_at, :zip,
|
59
|
-
:|
|
65
|
+
:frozen?, :include?, :index, :join, :last, :length, :pack,
|
66
|
+
:pretty_print, :pretty_print_cycle, :reverse_each, :rindex, :sample,
|
67
|
+
:size, :to_a, :to_ary, :to_s
|
60
68
|
] + Enumerable.instance_methods).uniq.each do |method_sym|
|
61
69
|
define_method(method_sym) do |*args, &block|
|
62
70
|
@store.cache.cache_read(self)
|
@@ -64,12 +72,23 @@ module PEROBS
|
|
64
72
|
end
|
65
73
|
end
|
66
74
|
|
75
|
+
# These methods mutate the Array and return self.
|
76
|
+
[
|
77
|
+
:<<, :clear, :collect!, :compact!, :concat,
|
78
|
+
:fill, :flatten!, :insert, :keep_if, :map!, :push,
|
79
|
+
:reject!, :replace, :select!, :reverse!, :rotate!, :shuffle!,
|
80
|
+
:slice!, :sort!, :sort_by!, :uniq!
|
81
|
+
].each do |method_sym|
|
82
|
+
define_method(method_sym) do |*args, &block|
|
83
|
+
@store.cache.cache_write(self)
|
84
|
+
@data.send(method_sym, *args, &block)
|
85
|
+
myself
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
67
89
|
# These methods mutate the Array.
|
68
90
|
[
|
69
|
-
|
70
|
-
:delete_if, :fill, :flatten!, :insert, :keep_if, :map!, :pop, :push,
|
71
|
-
:reject!, :replace, :select!, :reverse!, :rotate!, :shift, :shuffle!,
|
72
|
-
:slice!, :sort!, :sort_by!, :uniq!, :unshift
|
91
|
+
:delete, :delete_at, :delete_if, :shift, :pop
|
73
92
|
].each do |method_sym|
|
74
93
|
define_method(method_sym) do |*args, &block|
|
75
94
|
@store.cache.cache_write(self)
|
@@ -81,17 +100,45 @@ module PEROBS
|
|
81
100
|
# PEROBS users should never call this method or equivalents of derived
|
82
101
|
# methods directly.
|
83
102
|
# @param p [PEROBS::Handle] PEROBS handle
|
84
|
-
# @param
|
103
|
+
# @param arg1 [Integer or Array] The requested size of the Array or an
|
104
|
+
# Array to initialize
|
85
105
|
# @param default [Any] The default value that is returned when no value is
|
86
106
|
# stored for a specific key.
|
87
|
-
def initialize(p,
|
107
|
+
def initialize(p, arg1 = 0, default = nil, &block)
|
88
108
|
super(p)
|
89
|
-
|
109
|
+
if arg1.is_a?(::Array)
|
110
|
+
arg1.each { |v| _check_assignment_value(v) }
|
111
|
+
@data = arg1.dup
|
112
|
+
elsif block_given?
|
113
|
+
@data = ::Array.new(arg1) do
|
114
|
+
_check_assignment_value(yield)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
@data = ::Array.new(arg1, _check_assignment_value(default))
|
118
|
+
end
|
90
119
|
|
91
120
|
# Ensure that the newly created object will be pushed into the database.
|
92
121
|
@store.cache.cache_write(self)
|
93
122
|
end
|
94
123
|
|
124
|
+
# Proxy for the assignment method.
|
125
|
+
def []=(*args)
|
126
|
+
if (args.length == 2)
|
127
|
+
_check_assignment_value(args[1])
|
128
|
+
else
|
129
|
+
_check_assignment_value(args[2])
|
130
|
+
end
|
131
|
+
@store.cache.cache_write(self)
|
132
|
+
@data.[]=(*args)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Proxy for the unshift method.
|
136
|
+
def unshift(val)
|
137
|
+
_check_assignment_value(val)
|
138
|
+
@store.cache.cache_write(self)
|
139
|
+
@data.unshift(val)
|
140
|
+
end
|
141
|
+
|
95
142
|
# Return a list of all object IDs of all persistend objects that this Array
|
96
143
|
# is referencing.
|
97
144
|
# @return [Array of Integer] IDs of referenced objects
|
data/lib/perobs/BTree.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# = BTreeNode.rb -- Persistent Ruby Object Store
|
4
4
|
#
|
5
|
-
# Copyright (c) 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
|
5
|
+
# Copyright (c) 2016, 2017, 2018 by Chris Schlaeger <chris@taskjuggler.org>
|
6
6
|
#
|
7
7
|
# MIT License
|
8
8
|
#
|
@@ -32,7 +32,7 @@ require 'perobs/BTreeNode'
|
|
32
32
|
|
33
33
|
module PEROBS
|
34
34
|
|
35
|
-
# This BTree class is very similar to a classic
|
35
|
+
# This BTree class is very similar to a classic B+Tree implementation. It
|
36
36
|
# manages a tree that is always balanced. The BTree is stored in the
|
37
37
|
# specified directory and partially kept in memory to speed up operations.
|
38
38
|
# The order of the tree specifies how many keys each node will be able to
|
@@ -40,18 +40,21 @@ module PEROBS
|
|
40
40
|
# have N + 1 references to child nodes instead.
|
41
41
|
class BTree
|
42
42
|
|
43
|
-
attr_reader :order, :nodes, :node_cache
|
43
|
+
attr_reader :order, :nodes, :node_cache, :first_leaf, :last_leaf, :size
|
44
44
|
|
45
45
|
# Create a new BTree object.
|
46
46
|
# @param dir [String] Directory to store the tree file
|
47
47
|
# @param name [String] Base name of the BTree related files in 'dir'
|
48
48
|
# @param order [Integer] The maximum number of keys per node. This number
|
49
49
|
# must be odd and larger than 2 and smaller than 2**16 - 1.
|
50
|
-
|
50
|
+
# @param progressmeter [ProgressMeter] reference to a ProgressMeter object
|
51
|
+
def initialize(dir, name, order, progressmeter)
|
51
52
|
@dir = dir
|
52
53
|
@name = name
|
53
|
-
|
54
|
-
|
54
|
+
@progressmeter = progressmeter
|
55
|
+
|
56
|
+
unless order > 4
|
57
|
+
PEROBS.log.fatal "BTree order must be larger than 4, not #{order}"
|
55
58
|
end
|
56
59
|
unless order % 2 == 1
|
57
60
|
PEROBS.log.fatal "BTree order must be an uneven number, not #{order}"
|
@@ -62,9 +65,14 @@ module PEROBS
|
|
62
65
|
@order = order
|
63
66
|
|
64
67
|
# This EquiBlobsFile contains the nodes of the BTree.
|
65
|
-
@nodes = EquiBlobsFile.new(@dir, @name,
|
68
|
+
@nodes = EquiBlobsFile.new(@dir, @name, @progressmeter,
|
66
69
|
BTreeNode::node_bytes(@order))
|
67
|
-
@
|
70
|
+
@nodes.register_custom_data('first_leaf')
|
71
|
+
@nodes.register_custom_data('last_leaf')
|
72
|
+
@nodes.register_custom_data('btree_size')
|
73
|
+
@node_cache = PersistentObjectCache.new(2**13, 2**13, BTreeNode, self)
|
74
|
+
@root = @first_leaf = @last_leaf = nil
|
75
|
+
@size = 0
|
68
76
|
|
69
77
|
# This BTree implementation uses a write cache to improve write
|
70
78
|
# performance of multiple successive read/write operations. This also
|
@@ -89,23 +97,44 @@ module PEROBS
|
|
89
97
|
|
90
98
|
@node_cache.clear
|
91
99
|
@nodes.open
|
92
|
-
|
93
|
-
|
94
|
-
|
100
|
+
|
101
|
+
if @nodes.total_entries == 0
|
102
|
+
# We've created a new nodes file
|
103
|
+
node = BTreeNode::create(self)
|
104
|
+
else
|
105
|
+
# We are loading an existing tree.
|
106
|
+
node = BTreeNode::load_and_link(self, @nodes.first_entry)
|
107
|
+
@first_leaf = BTreeNode::load_and_link(
|
108
|
+
self, @nodes.get_custom_data('first_leaf'))
|
109
|
+
@last_leaf = BTreeNode::load_and_link(
|
110
|
+
self, @nodes.get_custom_data('last_leaf'))
|
111
|
+
end
|
95
112
|
set_root(node)
|
113
|
+
|
114
|
+
# Get the total number of entries that are stored in the tree.
|
115
|
+
@size = @nodes.get_custom_data('btree_size')
|
96
116
|
end
|
97
117
|
|
98
118
|
# Close the tree file.
|
99
119
|
def close
|
100
120
|
sync
|
121
|
+
PEROBS.log.info "BTree file #{@name} has currently " +
|
122
|
+
"#{@nodes.total_entries} used entries and #{@nodes.total_spaces} " +
|
123
|
+
"unused entries"
|
101
124
|
@nodes.close
|
102
125
|
@root = nil
|
103
126
|
end
|
104
127
|
|
128
|
+
# @return true if file is currently open
|
129
|
+
def is_open?
|
130
|
+
!@root.nil?
|
131
|
+
end
|
132
|
+
|
105
133
|
# Clear all pools and forget any registered spaces.
|
106
134
|
def clear
|
107
135
|
@node_cache.clear
|
108
136
|
@nodes.clear
|
137
|
+
@size = 0
|
109
138
|
set_root(BTreeNode::create(self))
|
110
139
|
end
|
111
140
|
|
@@ -114,6 +143,7 @@ module PEROBS
|
|
114
143
|
# all stored data from the BTree.
|
115
144
|
def erase
|
116
145
|
@nodes.erase
|
146
|
+
@size = 0
|
117
147
|
@root = nil
|
118
148
|
@dirty_flag.forced_unlock
|
119
149
|
end
|
@@ -121,6 +151,7 @@ module PEROBS
|
|
121
151
|
# Flush all pending modifications into the tree file.
|
122
152
|
def sync
|
123
153
|
@node_cache.flush(true)
|
154
|
+
@nodes.set_custom_data('btree_size', @size)
|
124
155
|
@nodes.sync
|
125
156
|
@dirty_flag.unlock if @dirty_flag.is_locked?
|
126
157
|
end
|
@@ -128,22 +159,70 @@ module PEROBS
|
|
128
159
|
# Check if the tree file contains any errors.
|
129
160
|
# @return [Boolean] true if no erros were found, false otherwise
|
130
161
|
def check(&block)
|
131
|
-
|
162
|
+
sync
|
163
|
+
return false unless @nodes.check
|
164
|
+
|
165
|
+
entries = 0
|
166
|
+
stats = nil
|
167
|
+
@progressmeter.start('Checking index structure', @size) do |pm|
|
168
|
+
stats = @root.check do |k, v|
|
169
|
+
pm.update(entries += 1)
|
170
|
+
block_given? ? yield(k, v) : true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
return false unless stats
|
175
|
+
|
176
|
+
unless entries == @size
|
177
|
+
PEROBS.log.error "The BTree size (#{@size}) and the number of " +
|
178
|
+
"found entries (#{entries}) don't match"
|
179
|
+
return false
|
180
|
+
end
|
181
|
+
unless stats.nodes_count == @nodes.total_entries
|
182
|
+
PEROBS.log.error "The BTree nodes count (#{stats.nodes_count}) and " +
|
183
|
+
"the number of entries in the nodes file (#{@nodes.total_entries}) " +
|
184
|
+
"don't match"
|
185
|
+
return false
|
186
|
+
end
|
187
|
+
PEROBS.log.info "Statistics for the BTree #{@name}: " +
|
188
|
+
"Number of nodes: #{stats.nodes_count}; " +
|
189
|
+
"Branch depth: #{stats.branch_depth}; " +
|
190
|
+
"Number of leave nodes: #{stats.leave_nodes}; " +
|
191
|
+
"Number of leaves: #{stats.leaves}"
|
192
|
+
|
193
|
+
true
|
132
194
|
end
|
133
195
|
|
134
196
|
# Register a new node as root node of the tree.
|
197
|
+
# @param node [BTreeNode]
|
135
198
|
def set_root(node)
|
136
199
|
@root = node
|
137
200
|
@nodes.first_entry = node.node_address
|
138
201
|
end
|
139
202
|
|
203
|
+
# Set the address of the first leaf node.
|
204
|
+
# @param node [BTreeNode]
|
205
|
+
def set_first_leaf(node)
|
206
|
+
@first_leaf = node
|
207
|
+
@nodes.set_custom_data('first_leaf', node.node_address)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Set the address of the last leaf node.
|
211
|
+
# @param node [BTreeNode]
|
212
|
+
def set_last_leaf(node)
|
213
|
+
@last_leaf = node
|
214
|
+
@nodes.set_custom_data('last_leaf', node.node_address)
|
215
|
+
end
|
216
|
+
|
140
217
|
# Insert a new value into the tree using the key as a unique index. If the
|
141
218
|
# key already exists the old value will be overwritten.
|
142
219
|
# @param key [Integer] Unique key
|
143
220
|
# @param value [Integer] value
|
144
221
|
def insert(key, value)
|
145
|
-
@root.insert(key, value)
|
146
|
-
|
222
|
+
if @root.insert(key, value)
|
223
|
+
@size += 1
|
224
|
+
@node_cache.flush
|
225
|
+
end
|
147
226
|
end
|
148
227
|
|
149
228
|
# Retrieve the value associated with the given key. If no entry was found,
|
@@ -154,10 +233,17 @@ module PEROBS
|
|
154
233
|
@root.get(key)
|
155
234
|
end
|
156
235
|
|
236
|
+
# Either return the key/value pair that exactly matches the key or a
|
237
|
+
# key/value pair that has a key that is at least min_miss_increment larger
|
238
|
+
# than the key.
|
239
|
+
def get_best_match(key, min_miss_increment)
|
240
|
+
@root.get_best_match(key, min_miss_increment)
|
241
|
+
end
|
242
|
+
|
157
243
|
# Find and remove the value associated with the given key. If no entry was
|
158
244
|
# found, return nil, otherwise the found value.
|
159
245
|
def remove(key)
|
160
|
-
removed_value = @root.remove(key)
|
246
|
+
@size -= 1 unless (removed_value = @root.remove(key)).nil?
|
161
247
|
|
162
248
|
# Check if the root node only contains one child link after the delete
|
163
249
|
# operation. Then we can delete that node and pull the tree one level
|
@@ -187,6 +273,11 @@ module PEROBS
|
|
187
273
|
@nodes.delete_blob(address)
|
188
274
|
end
|
189
275
|
|
276
|
+
# @return [Integer] The number of entries stored in the tree.
|
277
|
+
def entries_count
|
278
|
+
@size
|
279
|
+
end
|
280
|
+
|
190
281
|
# @return [String] Human reable form of the tree.
|
191
282
|
def to_s
|
192
283
|
@root.to_s
|
data/lib/perobs/BTreeBlob.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# = BTreeBlob.rb -- Persistent Ruby Object Store
|
4
4
|
#
|
5
|
-
# Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
|
5
|
+
# Copyright (c) 2015, 2016, 2019 by Chris Schlaeger <chris@taskjuggler.org>
|
6
6
|
#
|
7
7
|
# MIT License
|
8
8
|
#
|
@@ -33,7 +33,7 @@ require 'perobs/RobustFile'
|
|
33
33
|
module PEROBS
|
34
34
|
|
35
35
|
# This class manages the usage of the data blobs in the corresponding
|
36
|
-
#
|
36
|
+
# BTreeDB object.
|
37
37
|
class BTreeBlob
|
38
38
|
|
39
39
|
# Magic number used for index files.
|
@@ -144,11 +144,12 @@ module PEROBS
|
|
144
144
|
|
145
145
|
# Remove all entries from the index that have not been marked.
|
146
146
|
# @return [Array] List of deleted object IDs.
|
147
|
-
def delete_unmarked_entries
|
147
|
+
def delete_unmarked_entries(&block)
|
148
148
|
deleted_ids = []
|
149
149
|
# First remove the entry from the hash table.
|
150
150
|
@entries_by_id.delete_if do |id, e|
|
151
151
|
if e[MARKED] == 0
|
152
|
+
yield(id) if block_given?
|
152
153
|
deleted_ids << id
|
153
154
|
true
|
154
155
|
else
|
data/lib/perobs/BTreeDB.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
#
|
3
3
|
# = BTreeDB.rb -- Persistent Ruby Object Store
|
4
4
|
#
|
5
|
-
# Copyright (c) 2015, 2016
|
5
|
+
# Copyright (c) 2015, 2016, 2018, 2019
|
6
|
+
# by Chris Schlaeger <chris@taskjuggler.org>
|
6
7
|
#
|
7
8
|
# MIT License
|
8
9
|
#
|
@@ -58,7 +59,7 @@ module PEROBS
|
|
58
59
|
# nodes. The insert/find/delete time grows
|
59
60
|
# linearly with the size.
|
60
61
|
def initialize(db_name, options = {})
|
61
|
-
super(options
|
62
|
+
super(options)
|
62
63
|
|
63
64
|
@db_dir = db_name
|
64
65
|
# Create the database directory if it doesn't exist yet.
|
@@ -159,9 +160,9 @@ module PEROBS
|
|
159
160
|
# Permanently delete all objects that have not been marked. Those are
|
160
161
|
# orphaned and are no longer referenced by any actively used object.
|
161
162
|
# @return [Array] List of IDs that have been removed from the DB.
|
162
|
-
def delete_unmarked_objects
|
163
|
+
def delete_unmarked_objects(&block)
|
163
164
|
deleted_ids = []
|
164
|
-
each_blob { |blob| deleted_ids += blob.delete_unmarked_entries }
|
165
|
+
each_blob { |blob| deleted_ids += blob.delete_unmarked_entries(&block) }
|
165
166
|
deleted_ids
|
166
167
|
end
|
167
168
|
|