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