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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a61fc945e0ef9f5ed6558080931d2acae42cc0401f375275684e4ee32fefe4f7
|
4
|
+
data.tar.gz: 4d864fdc0791aa78d8c180b4686ee825cd25e209284fca1966f144813c063280
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3834a9caae693d82837fb9f75141cb35e85f1a2c1439d1bb898f8578d9ae082f46deb0233d2b8a02d6ae6b0bf66862098b47ff571ff3d9a6b874fadaef6d23a
|
7
|
+
data.tar.gz: 883f1b5e553fae2be0039aa090d89bf6eb44ec1d0dc31488aeaa727ec8bc2844c9b722568cd7dde0b33c507121afd11e720154d4ff27e66aa3ac3812d5603954
|
data/README.md
CHANGED
@@ -30,7 +30,7 @@ classes:
|
|
30
30
|
|
31
31
|
When you derive your own class from PEROBS::Object you need to
|
32
32
|
specify which instance variables should be persistent. By using
|
33
|
-
|
33
|
+
'attr_persist' you can provide a list of symbols that declare the instance
|
34
34
|
variables to persist. This will also create getter and setter methods
|
35
35
|
for these instance varables. You can set default values in the
|
36
36
|
constructor . The constructor of PEROBS::ObjectBase derived objects
|
@@ -61,13 +61,14 @@ almost every Ruby data type. YAML is much slower than JSON and Marshal
|
|
61
61
|
is not guaranteed to be compatible between Ruby versions.
|
62
62
|
|
63
63
|
Once you have created a store you can assign objects to it. All
|
64
|
-
persistent objects must be created with Store.new().
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
64
|
+
persistent objects must be created with Store.new(). The Store object
|
65
|
+
is available via the @store instance variable provided by the parent
|
66
|
+
class. This is necessary as you will only deal with proxy objects in
|
67
|
+
your code. Except for the member methods, you will never deal with
|
68
|
+
the objects directly. Instead Store.new() returns a POXReference
|
69
|
+
object that acts as a transparent proxy. This proxy is needed as your
|
70
|
+
code never knows if the actual object is really loaded into the memory
|
71
|
+
or not. PEROBS will handle this transparently for you.
|
71
72
|
|
72
73
|
A build-in cache keeps access latencies to recently used objects low
|
73
74
|
and lazily flushes modified objects into the persistend back-end when
|
@@ -92,22 +93,22 @@ require 'perobs'
|
|
92
93
|
|
93
94
|
class Person < PEROBS::Object
|
94
95
|
|
95
|
-
|
96
|
+
attr_persist :name, :mother, :father, :kids, :spouse, :status
|
96
97
|
|
97
98
|
def initialize(p, name)
|
98
99
|
super(p)
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
self.name = name
|
101
|
+
self.kids = @store.new(PEROBS::Array)
|
102
|
+
self.status = :single
|
102
103
|
end
|
103
104
|
|
104
105
|
def restore
|
105
106
|
# Use block version of attr_init() to avoid creating unneded
|
106
107
|
# objects. The block is only called when @father doesn't exist yet.
|
107
|
-
attr_init(:father) do { store.new(Person, 'Dad') }
|
108
|
+
attr_init(:father) do { @store.new(Person, 'Dad') }
|
108
109
|
end
|
109
110
|
|
110
|
-
def
|
111
|
+
def marry(spouse)
|
111
112
|
self.spouse = spouse
|
112
113
|
self.status = :married
|
113
114
|
end
|
@@ -136,7 +137,7 @@ contains the 3 Person objects.
|
|
136
137
|
### Accessing persistent instance variables
|
137
138
|
|
138
139
|
All instance variables that should be persisted must be declared with
|
139
|
-
'
|
140
|
+
'attr_persist'. This will create the instance variable, a getter and setter
|
140
141
|
method for it. These getter and setter methods are the recommended way
|
141
142
|
to access instance variables both from ouside of the instances as well
|
142
143
|
as from within. To access the setter or getter method from within an
|
@@ -166,8 +167,8 @@ object to another object.
|
|
166
167
|
### Caveats and known issues
|
167
168
|
|
168
169
|
PEROBS is currently not thread-safe. You cannot simultaneously access
|
169
|
-
the database from multiple application.
|
170
|
-
|
170
|
+
the database from multiple application. The library uses locks to
|
171
|
+
ensure that only one Store object is accessing the database at a time.
|
171
172
|
|
172
173
|
## Installation
|
173
174
|
|
@@ -187,7 +188,7 @@ Or install it yourself as:
|
|
187
188
|
|
188
189
|
## Copyright and License
|
189
190
|
|
190
|
-
Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
|
191
|
+
Copyright (c) 2015, 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
|
191
192
|
|
192
193
|
PEROBS and all accompanying files are licensed under this MIT License
|
193
194
|
|
data/lib/perobs.rb
CHANGED
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,20 +100,48 @@ 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
|
-
# @return [Array of
|
144
|
+
# @return [Array of Integer] IDs of referenced objects
|
98
145
|
def _referenced_object_ids
|
99
146
|
@data.each.select do |v|
|
100
147
|
v && v.respond_to?(:is_poxreference?)
|
@@ -103,7 +150,7 @@ module PEROBS
|
|
103
150
|
|
104
151
|
# This method should only be used during store repair operations. It will
|
105
152
|
# delete all references to the given object ID.
|
106
|
-
# @param id [
|
153
|
+
# @param id [Integer] targeted object ID
|
107
154
|
def _delete_reference_to_id(id)
|
108
155
|
@data.delete_if do |v|
|
109
156
|
v && v.respond_to?(:is_poxreference?) && v.id == id
|
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
|
#
|
@@ -27,12 +27,12 @@
|
|
27
27
|
|
28
28
|
require 'perobs/LockFile'
|
29
29
|
require 'perobs/EquiBlobsFile'
|
30
|
-
require 'perobs/
|
30
|
+
require 'perobs/PersistentObjectCache'
|
31
31
|
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
|
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,22 +97,45 @@ module PEROBS
|
|
89
97
|
|
90
98
|
@node_cache.clear
|
91
99
|
@nodes.open
|
92
|
-
|
93
|
-
|
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
|
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')
|
94
116
|
end
|
95
117
|
|
96
118
|
# Close the tree file.
|
97
119
|
def close
|
98
120
|
sync
|
121
|
+
PEROBS.log.info "BTree file #{@name} has currently " +
|
122
|
+
"#{@nodes.total_entries} used entries and #{@nodes.total_spaces} " +
|
123
|
+
"unused entries"
|
99
124
|
@nodes.close
|
100
125
|
@root = nil
|
101
126
|
end
|
102
127
|
|
128
|
+
# @return true if file is currently open
|
129
|
+
def is_open?
|
130
|
+
!@root.nil?
|
131
|
+
end
|
132
|
+
|
103
133
|
# Clear all pools and forget any registered spaces.
|
104
134
|
def clear
|
105
135
|
@node_cache.clear
|
106
136
|
@nodes.clear
|
107
|
-
|
137
|
+
@size = 0
|
138
|
+
set_root(BTreeNode::create(self))
|
108
139
|
end
|
109
140
|
|
110
141
|
# Erase the backing store of the BTree. This method should only be called
|
@@ -112,12 +143,15 @@ module PEROBS
|
|
112
143
|
# all stored data from the BTree.
|
113
144
|
def erase
|
114
145
|
@nodes.erase
|
146
|
+
@size = 0
|
147
|
+
@root = nil
|
115
148
|
@dirty_flag.forced_unlock
|
116
149
|
end
|
117
150
|
|
118
151
|
# Flush all pending modifications into the tree file.
|
119
152
|
def sync
|
120
153
|
@node_cache.flush(true)
|
154
|
+
@nodes.set_custom_data('btree_size', @size)
|
121
155
|
@nodes.sync
|
122
156
|
@dirty_flag.unlock if @dirty_flag.is_locked?
|
123
157
|
end
|
@@ -125,14 +159,59 @@ module PEROBS
|
|
125
159
|
# Check if the tree file contains any errors.
|
126
160
|
# @return [Boolean] true if no erros were found, false otherwise
|
127
161
|
def check(&block)
|
128
|
-
|
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
|
129
194
|
end
|
130
195
|
|
131
196
|
# Register a new node as root node of the tree.
|
197
|
+
# @param node [BTreeNode]
|
132
198
|
def set_root(node)
|
133
199
|
@root = node
|
134
200
|
@nodes.first_entry = node.node_address
|
135
|
-
|
201
|
+
end
|
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)
|
136
215
|
end
|
137
216
|
|
138
217
|
# Insert a new value into the tree using the key as a unique index. If the
|
@@ -140,8 +219,10 @@ module PEROBS
|
|
140
219
|
# @param key [Integer] Unique key
|
141
220
|
# @param value [Integer] value
|
142
221
|
def insert(key, value)
|
143
|
-
@root.insert(key, value)
|
144
|
-
|
222
|
+
if @root.insert(key, value)
|
223
|
+
@size += 1
|
224
|
+
@node_cache.flush
|
225
|
+
end
|
145
226
|
end
|
146
227
|
|
147
228
|
# Retrieve the value associated with the given key. If no entry was found,
|
@@ -152,10 +233,17 @@ module PEROBS
|
|
152
233
|
@root.get(key)
|
153
234
|
end
|
154
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
|
+
|
155
243
|
# Find and remove the value associated with the given key. If no entry was
|
156
244
|
# found, return nil, otherwise the found value.
|
157
245
|
def remove(key)
|
158
|
-
removed_value = @root.remove(key)
|
246
|
+
@size -= 1 unless (removed_value = @root.remove(key)).nil?
|
159
247
|
|
160
248
|
# Check if the root node only contains one child link after the delete
|
161
249
|
# operation. Then we can delete that node and pull the tree one level
|
@@ -178,17 +266,6 @@ module PEROBS
|
|
178
266
|
@root.each(&block)
|
179
267
|
end
|
180
268
|
|
181
|
-
# Mark the given node as being modified. This will cause the dirty_flag
|
182
|
-
# lock to be taken and the @is_dirty flag to be set.
|
183
|
-
# @param node [BTreeNode] node to mark
|
184
|
-
def mark_node_as_modified(node)
|
185
|
-
unless @is_dirty
|
186
|
-
@dirty_flag.lock
|
187
|
-
@is_dirty = true
|
188
|
-
end
|
189
|
-
@node_cache.mark_as_modified(node)
|
190
|
-
end
|
191
|
-
|
192
269
|
# Delete the node at the given address in the BTree file.
|
193
270
|
# @param address [Integer] address in file
|
194
271
|
def delete_node(address)
|
@@ -196,37 +273,16 @@ module PEROBS
|
|
196
273
|
@nodes.delete_blob(address)
|
197
274
|
end
|
198
275
|
|
276
|
+
# @return [Integer] The number of entries stored in the tree.
|
277
|
+
def entries_count
|
278
|
+
@size
|
279
|
+
end
|
280
|
+
|
199
281
|
# @return [String] Human reable form of the tree.
|
200
282
|
def to_s
|
201
283
|
@root.to_s
|
202
284
|
end
|
203
285
|
|
204
|
-
# Create a new BTreeNode. If the node_address is not nil, the node data is
|
205
|
-
# read from the backing store. The parent and is_leaf arguments are
|
206
|
-
# ignored in this case.
|
207
|
-
# @param parent [BTreeNode] parent node
|
208
|
-
# @param node_address [Integer or nil] address of the node to create
|
209
|
-
# @param is_leaf[Boolean] True if node is a leaf node, false otherweise
|
210
|
-
def new_node(parent, node_address = nil, is_leaf = true)
|
211
|
-
node = BTreeNode.new(self, parent, node_address, is_leaf)
|
212
|
-
@node_cache.insert(node)
|
213
|
-
|
214
|
-
node
|
215
|
-
end
|
216
|
-
|
217
|
-
# Return the BTreeNode that matches the given node address. If a blob
|
218
|
-
# address and size are given, a new node is created instead of being read
|
219
|
-
# from the file.
|
220
|
-
# @param node_address [Integer] Address of the node in the BTree file
|
221
|
-
# @return [BTreeNode]
|
222
|
-
def get_node(node_address)
|
223
|
-
if (node = @node_cache[node_address])
|
224
|
-
return node
|
225
|
-
end
|
226
|
-
|
227
|
-
new_node(nil, node_address)
|
228
|
-
end
|
229
|
-
|
230
286
|
end
|
231
287
|
|
232
288
|
end
|