perobs 2.5.0 → 3.0.0

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