perobs 3.0.1 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +19 -18
  3. data/lib/perobs.rb +2 -0
  4. data/lib/perobs/Array.rb +68 -21
  5. data/lib/perobs/BTree.rb +110 -54
  6. data/lib/perobs/BTreeBlob.rb +14 -13
  7. data/lib/perobs/BTreeDB.rb +11 -10
  8. data/lib/perobs/BTreeNode.rb +551 -197
  9. data/lib/perobs/BTreeNodeCache.rb +10 -8
  10. data/lib/perobs/BTreeNodeLink.rb +11 -1
  11. data/lib/perobs/BigArray.rb +285 -0
  12. data/lib/perobs/BigArrayNode.rb +1002 -0
  13. data/lib/perobs/BigHash.rb +246 -0
  14. data/lib/perobs/BigTree.rb +197 -0
  15. data/lib/perobs/BigTreeNode.rb +873 -0
  16. data/lib/perobs/Cache.rb +47 -22
  17. data/lib/perobs/ClassMap.rb +2 -2
  18. data/lib/perobs/ConsoleProgressMeter.rb +61 -0
  19. data/lib/perobs/DataBase.rb +4 -3
  20. data/lib/perobs/DynamoDB.rb +62 -20
  21. data/lib/perobs/EquiBlobsFile.rb +174 -59
  22. data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
  23. data/lib/perobs/FlatFile.rb +536 -242
  24. data/lib/perobs/FlatFileBlobHeader.rb +120 -84
  25. data/lib/perobs/FlatFileDB.rb +58 -27
  26. data/lib/perobs/FuzzyStringMatcher.rb +175 -0
  27. data/lib/perobs/Hash.rb +129 -35
  28. data/lib/perobs/IDList.rb +144 -0
  29. data/lib/perobs/IDListPage.rb +107 -0
  30. data/lib/perobs/IDListPageFile.rb +180 -0
  31. data/lib/perobs/IDListPageRecord.rb +142 -0
  32. data/lib/perobs/LockFile.rb +3 -0
  33. data/lib/perobs/Object.rb +28 -20
  34. data/lib/perobs/ObjectBase.rb +53 -10
  35. data/lib/perobs/PersistentObjectCache.rb +142 -0
  36. data/lib/perobs/PersistentObjectCacheLine.rb +99 -0
  37. data/lib/perobs/ProgressMeter.rb +97 -0
  38. data/lib/perobs/SpaceManager.rb +273 -0
  39. data/lib/perobs/SpaceTree.rb +63 -47
  40. data/lib/perobs/SpaceTreeNode.rb +134 -115
  41. data/lib/perobs/SpaceTreeNodeLink.rb +1 -1
  42. data/lib/perobs/StackFile.rb +1 -1
  43. data/lib/perobs/Store.rb +180 -70
  44. data/lib/perobs/version.rb +1 -1
  45. data/perobs.gemspec +4 -4
  46. data/test/Array_spec.rb +48 -39
  47. data/test/BTreeDB_spec.rb +2 -2
  48. data/test/BTree_spec.rb +50 -1
  49. data/test/BigArray_spec.rb +261 -0
  50. data/test/BigHash_spec.rb +152 -0
  51. data/test/BigTreeNode_spec.rb +153 -0
  52. data/test/BigTree_spec.rb +259 -0
  53. data/test/EquiBlobsFile_spec.rb +105 -5
  54. data/test/FNV_Hash_1a_64_spec.rb +59 -0
  55. data/test/FlatFileDB_spec.rb +199 -15
  56. data/test/FuzzyStringMatcher_spec.rb +261 -0
  57. data/test/Hash_spec.rb +27 -16
  58. data/test/IDList_spec.rb +77 -0
  59. data/test/LegacyDBs/LegacyDB.rb +155 -0
  60. data/test/LegacyDBs/version_3/class_map.json +1 -0
  61. data/test/LegacyDBs/version_3/config.json +1 -0
  62. data/test/LegacyDBs/version_3/database.blobs +0 -0
  63. data/test/LegacyDBs/version_3/database_spaces.blobs +0 -0
  64. data/test/LegacyDBs/version_3/index.blobs +0 -0
  65. data/test/LegacyDBs/version_3/version +1 -0
  66. data/test/LockFile_spec.rb +9 -6
  67. data/test/Object_spec.rb +5 -5
  68. data/test/SpaceManager_spec.rb +176 -0
  69. data/test/SpaceTree_spec.rb +27 -9
  70. data/test/Store_spec.rb +353 -206
  71. data/test/perobs_spec.rb +7 -3
  72. data/test/spec_helper.rb +9 -4
  73. metadata +59 -16
  74. data/lib/perobs/SpaceTreeNodeCache.rb +0 -76
  75. data/lib/perobs/TreeDB.rb +0 -277
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 20f164ff526b984530355e99ab38623fd2b87cea
4
- data.tar.gz: befc7531c92e0dc86c23b058cbc572657f66661f
2
+ SHA256:
3
+ metadata.gz: a61fc945e0ef9f5ed6558080931d2acae42cc0401f375275684e4ee32fefe4f7
4
+ data.tar.gz: 4d864fdc0791aa78d8c180b4686ee825cd25e209284fca1966f144813c063280
5
5
  SHA512:
6
- metadata.gz: 3b18e2e16719f896d8500852f0c1529ce2a1d9816695df04be2203cd1a10da8b70fe737afa910ec1fed71855eb6534da2b0dd16f8c6428c0ea1ae06a6509b59f
7
- data.tar.gz: d6de8c99f6cffcb245b5468c6f6b5f4b5572c44019f7b9926adeae126a81ad507b815bbefda24b07f4724bc0a11abc0ab3235b304d2ca7180cde3bdc628c0b1e
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
- po_attr you can provide a list of symbols that describe the instance
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(). This is
65
- necessary as you will only deal with proxy objects in your code.
66
- Except for the member methods, you will never deal with the objects
67
- directly. Instead Store.new() returns a POXReference object that acts
68
- as a transparent proxy. This proxy is needed as your code never knows
69
- if the actual object is really loaded into the memory or not. PEROBS
70
- will handle this transparently for you.
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
- po_attr :name, :mother, :father, :kids, :spouse, :status
96
+ attr_persist :name, :mother, :father, :kids, :spouse, :status
96
97
 
97
98
  def initialize(p, name)
98
99
  super(p)
99
- attr_init(:name, name)
100
- attr_init(:kids, store.new(PEROBS::Array))
101
- attr_init(:status, :single)
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 merry(spouse)
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
- 'po_attr'. This will create the instance variable, a getter and setter
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. You must provide your own
170
- locking mechanism to prevent this from happening.
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
@@ -27,3 +27,5 @@
27
27
 
28
28
  require "perobs/version"
29
29
  require 'perobs/Store'
30
+ require 'perobs/ConsoleProgressMeter'
31
+ require 'perobs/FuzzyStringMatcher'
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. They only perform read
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
- :&, :*, :+, :-, :==, :[], :<=>, :at, :abbrev, :assoc, :bsearch, :collect,
51
- :combination, :compact, :count, :cycle, :dclone, :drop, :drop_while,
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
- :flatten, :frozen?, :hash, :include?, :index, :join, :last,
54
- :length, :map, :pack, :permutation, :pretty_print, :pretty_print_cycle,
55
- :product, :rassoc, :reject, :repeated_combination,
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
- :<<, :[]=, :clear, :collect!, :compact!, :concat, :delete, :delete_at,
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 size [Fixnum] The requested size of the Array
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, size = 0, default = nil)
107
+ def initialize(p, arg1 = 0, default = nil, &block)
88
108
  super(p)
89
- @data = ::Array.new(size, default)
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 Fixnum or Bignum] IDs of referenced objects
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 [Fixnum/Bignum] targeted object 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/BTreeNodeCache'
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 BTree implementation. It
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
- def initialize(dir, name, order)
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
- unless order > 2
54
- PEROBS.log.fatal "BTree order must be larger than 2, not #{order}"
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
- @node_cache = BTreeNodeCache.new
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
- set_root(new_node(nil, @nodes.total_entries == 0 ?
93
- nil : @nodes.first_entry))
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
- set_root(new_node(nil))
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
- @root.check(&block)
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
- @node_cache.set_root(node)
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
- @node_cache.flush
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