perobs 3.0.1 → 4.3.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 +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
|