perobs 2.5.0 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9df83ee37d61319185f94bcaf64a1f48083280fd
4
- data.tar.gz: 130fbd021bfe32cad6b45e6b4063f0093552d8f3
3
+ metadata.gz: b157aa5361418e9985f54fd0b08d6158efb83137
4
+ data.tar.gz: 0cf5c58f8ceaaa4c463d122875b969479fd04501
5
5
  SHA512:
6
- metadata.gz: a1e61da3e76e0bb4e965ca00c0d4b97105fe5a37f1272cc9a4b0d997d0f8a521547c22c3318ad0bc17eabf9059c54d7d616ad287f1d48743c9f126e17c66ec97
7
- data.tar.gz: 0d86ab15a8ffdb18a1b8df4f27a5f2e0fc4f9619f040934ec7904b4e17bc453b383be424190951f2c2d6f1fcb309398913aba6111a360ba908a428f1629c7b67
6
+ metadata.gz: 82cb0f25912d1063dcba7a6012ca0cc80f1dfff72ac7500cf19d771745faf35f09759592176c3b0a25a9cd46bbdb88874013f58d9f2b669405624cf33f582867
7
+ data.tar.gz: 2d9690e7863f4a2b049090c1094b9e838f7412ca3b0e708f111a186daf56c1f0826aa66d1aadb8b56566b485b241df74f381e27d0c3fd0b043097528a594f469
data/lib/perobs/Array.rb CHANGED
@@ -102,7 +102,7 @@ module PEROBS
102
102
  end
103
103
 
104
104
  # This method should only be used during store repair operations. It will
105
- # delete all referenced to the given object ID.
105
+ # delete all references to the given object ID.
106
106
  # @param id [Fixnum/Bignum] targeted object ID
107
107
  def _delete_reference_to_id(id)
108
108
  @data.delete_if do |v|
@@ -0,0 +1,233 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = BTreeNode.rb -- Persistent Ruby Object Store
4
+ #
5
+ # Copyright (c) 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
6
+ #
7
+ # MIT License
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+
28
+ require 'perobs/LockFile'
29
+ require 'perobs/EquiBlobsFile'
30
+ require 'perobs/BTreeNodeCache'
31
+ require 'perobs/BTreeNode'
32
+
33
+ module PEROBS
34
+
35
+ # This BTree class is very similar to a classic BTree implementation. It
36
+ # manages a tree that is always balanced. The BTree is stored in the
37
+ # specified directory and partially kept in memory to speed up operations.
38
+ # The order of the tree specifies how many keys each node will be able to
39
+ # hold. Leaf nodes will have a value associated with each key. Branch nodes
40
+ # have N + 1 references to child nodes instead.
41
+ class BTree
42
+
43
+ attr_reader :order, :nodes
44
+
45
+ # Create a new BTree object.
46
+ # @param dir [String] Directory to store the tree file
47
+ # @param name [String] Base name of the BTree related files in 'dir'
48
+ # @param order [Integer] The maximum number of keys per node. This number
49
+ # must be odd and larger than 2 and smaller than 2**16 - 1.
50
+ def initialize(dir, name, order)
51
+ @dir = dir
52
+ @name = name
53
+ unless order > 2
54
+ PEROBS.log.fatal "BTree order must be larger than 2, not #{order}"
55
+ end
56
+ unless order % 2 == 1
57
+ PEROBS.log.fatal "BTree order must be an uneven number, not #{order}"
58
+ end
59
+ unless order < 2 ** 16 - 1
60
+ PEROBS.log.fatal "BTree order must be smaller than #{2**16 - 1}"
61
+ end
62
+ @order = order
63
+
64
+ # This EquiBlobsFile contains the nodes of the BTree.
65
+ @nodes = EquiBlobsFile.new(@dir, @name,
66
+ BTreeNode::node_bytes(@order))
67
+ @node_cache = BTreeNodeCache.new
68
+
69
+ # This BTree implementation uses a write cache to improve write
70
+ # performance of multiple successive read/write operations. This also
71
+ # means that data may not be written on the backing store until the
72
+ # sync() or close() methods have been called. A bug in the program or a
73
+ # premature program termination can lead to data loss. To detect such
74
+ # situations, we use a lock file whenever there are pending writes.
75
+ @is_dirty = false
76
+ @dirty_flag = LockFile.new(File.join(@dir, name + '.dirty'),
77
+ { :timeout_secs => 0 })
78
+ end
79
+
80
+ # Open the tree file.
81
+ def open(file_must_exist = false)
82
+ if @dirty_flag.is_locked?
83
+ PEROBS.log.fatal "Index file #{@nodes.file_name} is already " +
84
+ "locked"
85
+ end
86
+ if file_must_exist && !@nodes.file_exist?
87
+ PEROBS.log.fatal "Index file #{@nodes.file_name} does not exist"
88
+ end
89
+
90
+ @node_cache.clear
91
+ @nodes.open
92
+ set_root(new_node(nil, @nodes.total_entries == 0 ?
93
+ nil : @nodes.first_entry))
94
+ end
95
+
96
+ # Close the tree file.
97
+ def close
98
+ sync
99
+ @nodes.close
100
+ @root = nil
101
+ end
102
+
103
+ # Clear all pools and forget any registered spaces.
104
+ def clear
105
+ @node_cache.clear
106
+ @nodes.clear
107
+ set_root(new_node(nil))
108
+ end
109
+
110
+ # Erase the backing store of the BTree. This method should only be called
111
+ # when not having the BTree open. And it obviously and permanently erases
112
+ # all stored data from the BTree.
113
+ def erase
114
+ @nodes.erase
115
+ @dirty_flag.forced_unlock
116
+ end
117
+
118
+ # Flush all pending modifications into the tree file.
119
+ def sync
120
+ @node_cache.flush(true)
121
+ @nodes.sync
122
+ @dirty_flag.unlock if @dirty_flag.is_locked?
123
+ end
124
+
125
+ # Check if the tree file contains any errors.
126
+ # @return [Boolean] true if no erros were found, false otherwise
127
+ def check(&block)
128
+ @root.check(&block)
129
+ end
130
+
131
+ # Register a new node as root node of the tree.
132
+ def set_root(node)
133
+ @root = node
134
+ @nodes.first_entry = node.node_address
135
+ @node_cache.set_root(node)
136
+ end
137
+
138
+ # Insert a new value into the tree using the key as a unique index. If the
139
+ # key already exists the old value will be overwritten.
140
+ # @param key [Integer] Unique key
141
+ # @param value [Integer] value
142
+ def insert(key, value)
143
+ @root.insert(key, value)
144
+ @node_cache.flush
145
+ end
146
+
147
+ # Retrieve the value associated with the given key. If no entry was found,
148
+ # return nil.
149
+ # @param key [Integer] Unique key
150
+ # @return [Integer or nil] found value or nil
151
+ def get(key)
152
+ @root.get(key)
153
+ end
154
+
155
+ # Find and remove the value associated with the given key. If no entry was
156
+ # found, return nil, otherwise the found value.
157
+ def remove(key)
158
+ removed_value = @root.remove(key)
159
+
160
+ # Check if the root node only contains one child link after the delete
161
+ # operation. Then we can delete that node and pull the tree one level
162
+ # up. This could happen for a sequence of nodes that all got merged to
163
+ # single child nodes.
164
+ while !@root.is_leaf && @root.children.size == 1
165
+ old_root = @root
166
+ set_root(@root.children.first)
167
+ @root.parent = nil
168
+ delete_node(old_root.node_address)
169
+ end
170
+
171
+ @node_cache.flush
172
+ removed_value
173
+ end
174
+
175
+ # Iterate over all key/value pairs that are stored in the tree.
176
+ # @yield [key, value]
177
+ def each(&block)
178
+ @root.each(&block)
179
+ end
180
+
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
+ # Delete the node at the given address in the BTree file.
193
+ # @param address [Integer] address in file
194
+ def delete_node(address)
195
+ @node_cache.delete(address)
196
+ @nodes.delete_blob(address)
197
+ end
198
+
199
+ # @return [String] Human reable form of the tree.
200
+ def to_s
201
+ @root.to_s
202
+ end
203
+
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
+ end
231
+
232
+ end
233
+
@@ -182,8 +182,10 @@ module PEROBS
182
182
  # Basic consistency check.
183
183
  # @param repair [TrueClass/FalseClass] True if found errors should be
184
184
  # repaired.
185
+ # @return number of errors found
185
186
  def check_db(repair = false)
186
187
  each_blob { |blob| blob.check(repair) }
188
+ 0
187
189
  end
188
190
 
189
191
  # Check if the stored object is syntactically correct.