perobs 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/perobs/Array.rb +3 -3
- data/lib/perobs/BTreeBlob.rb +10 -10
- data/lib/perobs/BTreeDB.rb +6 -6
- data/lib/perobs/Cache.rb +2 -2
- data/lib/perobs/ClassMap.rb +2 -2
- data/lib/perobs/DynamoDB.rb +5 -5
- data/lib/perobs/EquiBlobsFile.rb +14 -7
- data/lib/perobs/FlatFile.rb +29 -7
- data/lib/perobs/FlatFileBlobHeader.rb +7 -12
- data/lib/perobs/FlatFileDB.rb +6 -6
- data/lib/perobs/Hash.rb +2 -2
- data/lib/perobs/Object.rb +2 -2
- data/lib/perobs/ObjectBase.rb +1 -1
- data/lib/perobs/SpaceTree.rb +47 -42
- data/lib/perobs/SpaceTreeNode.rb +89 -69
- data/lib/perobs/SpaceTreeNodeCache.rb +84 -11
- data/lib/perobs/SpaceTreeNodeLink.rb +1 -1
- data/lib/perobs/StackFile.rb +1 -1
- data/lib/perobs/Store.rb +14 -14
- data/lib/perobs/TreeDB.rb +7 -7
- data/lib/perobs/version.rb +1 -1
- data/test/Array_spec.rb +4 -4
- data/test/BTreeDB_spec.rb +2 -2
- data/test/SpaceTree_spec.rb +23 -8
- data/test/Store_spec.rb +3 -1
- metadata +2 -2
data/lib/perobs/Hash.rb
CHANGED
@@ -92,7 +92,7 @@ module PEROBS
|
|
92
92
|
|
93
93
|
# Return a list of all object IDs of all persistend objects that this Hash
|
94
94
|
# is referencing.
|
95
|
-
# @return [Array of
|
95
|
+
# @return [Array of Integer] IDs of referenced objects
|
96
96
|
def _referenced_object_ids
|
97
97
|
@data.each_value.select { |v| v && v.respond_to?(:is_poxreference?) }.
|
98
98
|
map { |o| o.id }
|
@@ -100,7 +100,7 @@ module PEROBS
|
|
100
100
|
|
101
101
|
# This method should only be used during store repair operations. It will
|
102
102
|
# delete all referenced to the given object ID.
|
103
|
-
# @param id [
|
103
|
+
# @param id [Integer] targeted object ID
|
104
104
|
def _delete_reference_to_id(id)
|
105
105
|
@data.delete_if do |k, v|
|
106
106
|
v && v.respond_to?(:is_poxreference?) && v.id == id
|
data/lib/perobs/Object.rb
CHANGED
@@ -140,7 +140,7 @@ module PEROBS
|
|
140
140
|
|
141
141
|
# Return a list of all object IDs that the attributes of this instance are
|
142
142
|
# referencing.
|
143
|
-
# @return [Array of
|
143
|
+
# @return [Array of Integer] IDs of referenced objects
|
144
144
|
def _referenced_object_ids
|
145
145
|
ids = []
|
146
146
|
_all_attributes.each do |attr|
|
@@ -152,7 +152,7 @@ module PEROBS
|
|
152
152
|
|
153
153
|
# This method should only be used during store repair operations. It will
|
154
154
|
# delete all references to the given object ID.
|
155
|
-
# @param id [
|
155
|
+
# @param id [Integer] targeted object ID
|
156
156
|
def _delete_reference_to_id(id)
|
157
157
|
_all_attributes.each do |attr|
|
158
158
|
ivar = ('@' + attr.to_s).to_sym
|
data/lib/perobs/ObjectBase.rb
CHANGED
@@ -207,7 +207,7 @@ module PEROBS
|
|
207
207
|
end
|
208
208
|
|
209
209
|
# Restore the object state from the storage back-end.
|
210
|
-
# @param level [
|
210
|
+
# @param level [Integer] the transaction nesting level
|
211
211
|
def _restore(level)
|
212
212
|
# Find the most recently stored state of this object. This could be on
|
213
213
|
# any previous stash level or in the regular object DB. If the object
|
data/lib/perobs/SpaceTree.rb
CHANGED
@@ -40,7 +40,7 @@ module PEROBS
|
|
40
40
|
# size which drastically simplifies the backing store operation.
|
41
41
|
class SpaceTree
|
42
42
|
|
43
|
-
attr_reader :nodes
|
43
|
+
attr_reader :nodes, :cache
|
44
44
|
|
45
45
|
# Manage the free spaces tree in the specified directory
|
46
46
|
# @param dir [String] directory path of an existing directory
|
@@ -51,29 +51,44 @@ module PEROBS
|
|
51
51
|
@nodes = EquiBlobsFile.new(@dir, 'database_spaces',
|
52
52
|
SpaceTreeNode::NODE_BYTES, 1)
|
53
53
|
|
54
|
-
@
|
54
|
+
@cache = SpaceTreeNodeCache.new(self, 128)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Open the SpaceTree file.
|
58
58
|
def open
|
59
59
|
@nodes.open
|
60
|
-
@
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
@cache.clear
|
61
|
+
node = @nodes.total_entries == 0 ?
|
62
|
+
SpaceTreeNode::create(self) :
|
63
|
+
SpaceTreeNode::load(self, @nodes.first_entry)
|
64
|
+
@root_address = node.node_address
|
64
65
|
end
|
65
66
|
|
66
67
|
# Close the SpaceTree file.
|
67
68
|
def close
|
69
|
+
@cache.flush
|
68
70
|
@nodes.close
|
69
|
-
@
|
70
|
-
@
|
71
|
+
@root_address = nil
|
72
|
+
@cache.clear
|
71
73
|
end
|
72
74
|
|
75
|
+
# Flush all pending writes to the file system.
|
76
|
+
def sync
|
77
|
+
@cache.flush
|
78
|
+
@nodes.sync
|
79
|
+
end
|
80
|
+
|
81
|
+
# Set a new root node for the SpaceTree
|
82
|
+
# @param node [SpaceTreeNode]
|
73
83
|
def set_root(node)
|
74
|
-
@
|
84
|
+
@root_address = node.node_address
|
85
|
+
@nodes.first_entry = node.node_address
|
75
86
|
end
|
76
87
|
|
88
|
+
# Return the SpaceTreeNode that is the root of the SpaceTree.
|
89
|
+
def root
|
90
|
+
@root_address ? @cache.get(@root_address) : nil
|
91
|
+
end
|
77
92
|
|
78
93
|
# Erase the SpaceTree file. This method cannot be called while the file is
|
79
94
|
# open.
|
@@ -88,7 +103,11 @@ module PEROBS
|
|
88
103
|
if size <= 0
|
89
104
|
PEROBS.log.fatal "Size (#{size}) must be larger than 0."
|
90
105
|
end
|
91
|
-
|
106
|
+
if has_space?(address, size)
|
107
|
+
PEROBS.log.fatal "The space with address #{address} and size #{size} " +
|
108
|
+
"can't be added twice."
|
109
|
+
end
|
110
|
+
root.add_space(address, size)
|
92
111
|
end
|
93
112
|
|
94
113
|
# Get a space that has at least the requested size.
|
@@ -99,10 +118,10 @@ module PEROBS
|
|
99
118
|
PEROBS.log.fatal "Size (#{size}) must be larger than 0."
|
100
119
|
end
|
101
120
|
|
102
|
-
if (address_size =
|
121
|
+
if (address_size = root.find_matching_space(size))
|
103
122
|
# First we try to find an exact match.
|
104
123
|
return address_size
|
105
|
-
elsif (address_size =
|
124
|
+
elsif (address_size = root.find_equal_or_larger_space(size))
|
106
125
|
return address_size
|
107
126
|
else
|
108
127
|
return nil
|
@@ -112,38 +131,15 @@ module PEROBS
|
|
112
131
|
# Delete the node at the given address in the SpaceTree file.
|
113
132
|
# @param address [Integer] address in file
|
114
133
|
def delete_node(address)
|
115
|
-
@
|
134
|
+
@cache.delete(address)
|
116
135
|
@nodes.delete_blob(address)
|
117
136
|
end
|
118
137
|
|
119
138
|
# Clear all pools and forget any registered spaces.
|
120
139
|
def clear
|
121
140
|
@nodes.clear
|
122
|
-
@
|
123
|
-
@
|
124
|
-
@node_cache.insert(@root)
|
125
|
-
end
|
126
|
-
|
127
|
-
# Create a new SpaceTreeNode.
|
128
|
-
# @param parent [SpaceTreeNode] parent node
|
129
|
-
# @param blob_address [Integer] address of the free space
|
130
|
-
# @param size [Integer] size of the free space
|
131
|
-
def new_node(parent, blob_address, size)
|
132
|
-
node = SpaceTreeNode.new(self, parent, nil, blob_address, size)
|
133
|
-
@node_cache.insert(node)
|
134
|
-
end
|
135
|
-
|
136
|
-
# Return the SpaceTreeNode that matches the given node address. If a blob
|
137
|
-
# address and size are given, a new node is created instead of being read
|
138
|
-
# from the file.
|
139
|
-
# @param node_address [Integer] Address of the node in the SpaceTree file
|
140
|
-
# @return [SpaceTreeNode]
|
141
|
-
def get_node(node_address)
|
142
|
-
if (node = @node_cache.get(node_address))
|
143
|
-
return node
|
144
|
-
end
|
145
|
-
|
146
|
-
@node_cache.insert(SpaceTreeNode.new(self, nil, node_address))
|
141
|
+
@cache.clear
|
142
|
+
@root_address = SpaceTreeNode::create(self).node_address
|
147
143
|
end
|
148
144
|
|
149
145
|
# Check if there is a space in the free space lists that matches the
|
@@ -152,7 +148,7 @@ module PEROBS
|
|
152
148
|
# @param [Integer] size Length of the space in bytes
|
153
149
|
# @return [Boolean] True if space is found, false otherwise
|
154
150
|
def has_space?(address, size)
|
155
|
-
|
151
|
+
root.has_space?(address, size)
|
156
152
|
end
|
157
153
|
|
158
154
|
# Check if the index is OK and matches the flat_file data (if given).
|
@@ -160,19 +156,28 @@ module PEROBS
|
|
160
156
|
# @return True if space list matches, flase otherwise
|
161
157
|
def check(flat_file = nil)
|
162
158
|
@nodes.check
|
163
|
-
|
159
|
+
root.check(flat_file)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Iterate over all entries and yield address and size.
|
163
|
+
def each
|
164
|
+
root.each do |node, mode, stack|
|
165
|
+
if mode == :on_enter
|
166
|
+
yield(node.blob_address, node.size)
|
167
|
+
end
|
168
|
+
end
|
164
169
|
end
|
165
170
|
|
166
171
|
# Complete internal tree data structure as textual tree.
|
167
172
|
# @return [String]
|
168
173
|
def to_s
|
169
|
-
|
174
|
+
root.to_tree_s
|
170
175
|
end
|
171
176
|
|
172
177
|
# Convert the tree into an Array of [address, size] touples.
|
173
178
|
# @return [Array]
|
174
179
|
def to_a
|
175
|
-
|
180
|
+
root.to_a
|
176
181
|
end
|
177
182
|
|
178
183
|
end
|
data/lib/perobs/SpaceTreeNode.rb
CHANGED
@@ -51,43 +51,97 @@ module PEROBS
|
|
51
51
|
# Create a new SpaceTreeNode object. If node_address is not nil, the data
|
52
52
|
# will be read from the SpaceTree file at the given node_address.
|
53
53
|
# @param tree [SpaceTree] Tree that the object should belong to
|
54
|
-
# @param parent [SpaceTreeNode] Parent node in the tree
|
55
54
|
# @param node_address [Integer] Address of the node in the file
|
56
55
|
# @param blob_address [Integer] Address of the free space blob
|
57
56
|
# @param size [Integer] Size of the free space blob
|
58
|
-
|
59
|
-
|
57
|
+
# @param parent [SpaceTreeNode] Parent node in the tree
|
58
|
+
# @param smaller [SpaceTreeNode] smaller node in the tree
|
59
|
+
# @param equal [SpaceTreeNode] equal node in the tree
|
60
|
+
# @param larger [SpaceTreeNode] larger node in the tree
|
61
|
+
def initialize(tree, node_address, blob_address = 0, size = 0,
|
62
|
+
parent = nil, smaller = nil, equal = nil, larger = nil)
|
60
63
|
@tree = tree
|
61
|
-
if
|
64
|
+
if node_address <= 0
|
62
65
|
PEROBS.log.fatal "Node address (#{node_address}) must be larger than 0"
|
63
66
|
end
|
67
|
+
@node_address = node_address
|
68
|
+
if blob_address < 0
|
69
|
+
PEROBS.log.fatal "Blob address (#{node_address}) must be larger than 0"
|
70
|
+
end
|
64
71
|
@blob_address = blob_address
|
65
72
|
@size = size
|
66
|
-
@
|
67
|
-
@
|
73
|
+
@parent = parent
|
74
|
+
@smaller = smaller
|
75
|
+
@equal = equal
|
76
|
+
@larger = larger
|
77
|
+
|
78
|
+
ObjectSpace.define_finalizer(
|
79
|
+
self, SpaceTreeNode._finalize(@tree, @node_address))
|
80
|
+
@tree.cache.insert_unmodified(self)
|
81
|
+
end
|
68
82
|
|
69
|
-
|
70
|
-
|
71
|
-
|
83
|
+
# This method generates the destructor for the objects of this class. It
|
84
|
+
# is done this way to prevent the Proc object hanging on to a reference to
|
85
|
+
# self which would prevent the object from being collected. This internal
|
86
|
+
# method is not intended for users to call.
|
87
|
+
def SpaceTreeNode._finalize(tree, node_address)
|
88
|
+
proc { tree.cache._collect(node_address) }
|
89
|
+
end
|
72
90
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
91
|
+
# Create a new SpaceTreeNode. This method should be used for the creation
|
92
|
+
# of new nodes instead of calling the constructor directly.
|
93
|
+
# @param tree [SpaceTree] The tree the node should belong to
|
94
|
+
# @param node_address [Integer] Address of the node in the file
|
95
|
+
# @param blob_address [Integer] Address of the free space blob
|
96
|
+
# @param size [Integer] Size of the free space blob
|
97
|
+
# @param parent [SpaceTreeNode] Parent node in the tree
|
98
|
+
def SpaceTreeNode::create(tree, blob_address = 0, size = 0, parent = nil)
|
99
|
+
node_address = tree.nodes.free_address
|
100
|
+
|
101
|
+
node = SpaceTreeNode.new(tree, node_address, blob_address, size, parent)
|
102
|
+
node.save
|
103
|
+
|
104
|
+
node
|
105
|
+
end
|
106
|
+
|
107
|
+
# Restore a node from the backing store at the given address and tree.
|
108
|
+
# @param tree [SpaceTree] The tree the node belongs to
|
109
|
+
# @param node_address [Integer] The address in the file.
|
110
|
+
def SpaceTreeNode::load(tree, node_address)
|
111
|
+
unless node_address > 0
|
112
|
+
PEROBS.log.fatal "node_address (#{node_address}) must be larger than 0"
|
113
|
+
end
|
114
|
+
unless (bytes = tree.nodes.retrieve_blob(node_address))
|
115
|
+
PEROBS.log.fatal "SpaceTreeNode at address #{node_address} does " +
|
116
|
+
"not exist"
|
90
117
|
end
|
118
|
+
|
119
|
+
blob_address, size, parent_node_address,
|
120
|
+
smaller_node_address, equal_node_address,
|
121
|
+
larger_node_address = bytes.unpack(NODE_BYTES_FORMAT)
|
122
|
+
|
123
|
+
parent = parent_node_address != 0 ?
|
124
|
+
SpaceTreeNodeLink.new(tree, parent_node_address) : nil
|
125
|
+
smaller = smaller_node_address != 0 ?
|
126
|
+
SpaceTreeNodeLink.new(tree, smaller_node_address) : nil
|
127
|
+
equal = equal_node_address != 0 ?
|
128
|
+
SpaceTreeNodeLink.new(tree, equal_node_address) : nil
|
129
|
+
larger = larger_node_address != 0 ?
|
130
|
+
SpaceTreeNodeLink.new(tree, larger_node_address) : nil
|
131
|
+
|
132
|
+
node = SpaceTreeNode.new(tree, node_address, blob_address, size,
|
133
|
+
parent, smaller, equal, larger)
|
134
|
+
|
135
|
+
node
|
136
|
+
end
|
137
|
+
|
138
|
+
def save
|
139
|
+
bytes = [ @blob_address, @size,
|
140
|
+
@parent ? @parent.node_address : 0,
|
141
|
+
@smaller ? @smaller.node_address : 0,
|
142
|
+
@equal ? @equal.node_address : 0,
|
143
|
+
@larger ? @larger.node_address : 0].pack(NODE_BYTES_FORMAT)
|
144
|
+
@tree.nodes.store_blob(@node_address, bytes)
|
91
145
|
end
|
92
146
|
|
93
147
|
# Add a new node for the given address and size to the tree.
|
@@ -110,7 +164,7 @@ module PEROBS
|
|
110
164
|
# There is no smaller node yet, so we create a new one as a
|
111
165
|
# smaller child of the current node.
|
112
166
|
node.set_link('@smaller',
|
113
|
-
@tree
|
167
|
+
SpaceTreeNode::create(@tree, address, size, node))
|
114
168
|
break
|
115
169
|
end
|
116
170
|
elsif size > node.size
|
@@ -122,13 +176,13 @@ module PEROBS
|
|
122
176
|
# There is no larger node yet, so we create a new one as a larger
|
123
177
|
# child of the current node.
|
124
178
|
node.set_link('@larger',
|
125
|
-
@tree
|
179
|
+
SpaceTreeNode::create(@tree, address, size, node))
|
126
180
|
break
|
127
181
|
end
|
128
182
|
else
|
129
183
|
# Same size as current node. Insert new node as equal child at top of
|
130
184
|
# equal list.
|
131
|
-
new_node = @tree
|
185
|
+
new_node = SpaceTreeNode::create(@tree, address, size, node)
|
132
186
|
new_node.set_link('@equal', node.equal)
|
133
187
|
|
134
188
|
node.set_link('@equal', new_node)
|
@@ -147,7 +201,7 @@ module PEROBS
|
|
147
201
|
node = self
|
148
202
|
loop do
|
149
203
|
if node.blob_address == address
|
150
|
-
return
|
204
|
+
return size == node.size
|
151
205
|
elsif size < node.size && node.smaller
|
152
206
|
node = node.smaller
|
153
207
|
elsif size > node.size && node.larger
|
@@ -242,7 +296,7 @@ module PEROBS
|
|
242
296
|
PEROBS.log.fatal "Cannot unlink unknown child node with address " +
|
243
297
|
"#{child_node.node_address} from #{to_s}"
|
244
298
|
end
|
245
|
-
|
299
|
+
@tree.cache.insert_modified(self)
|
246
300
|
end
|
247
301
|
|
248
302
|
# Depth-first iterator for all nodes. The iterator yields the given block
|
@@ -339,7 +393,7 @@ module PEROBS
|
|
339
393
|
@parent.set_link('@larger', node)
|
340
394
|
else
|
341
395
|
PEROBS.log.fatal "Cannot relink unknown child node with address " +
|
342
|
-
"#{node.node_address} from #{to_s}"
|
396
|
+
"#{node.node_address} from #{parent.to_s}"
|
343
397
|
end
|
344
398
|
else
|
345
399
|
if node
|
@@ -382,7 +436,7 @@ module PEROBS
|
|
382
436
|
def set_size_and_address(size, address)
|
383
437
|
@size = size
|
384
438
|
@blob_address = address
|
385
|
-
|
439
|
+
@tree.cache.insert_modified(self)
|
386
440
|
end
|
387
441
|
|
388
442
|
def set_link(name, node_or_address)
|
@@ -398,12 +452,12 @@ module PEROBS
|
|
398
452
|
# Clear the node link.
|
399
453
|
instance_variable_set(name, nil)
|
400
454
|
end
|
401
|
-
|
455
|
+
@tree.cache.insert_modified(self)
|
402
456
|
end
|
403
457
|
|
404
458
|
def parent=(p)
|
405
459
|
@parent = p ? SpaceTreeNodeLink.new(@tree, p) : nil
|
406
|
-
|
460
|
+
@tree.cache.insert_modified(self)
|
407
461
|
end
|
408
462
|
# Compare this node to another node.
|
409
463
|
# @return [Boolean] true if node address is identical, false otherwise
|
@@ -633,40 +687,6 @@ module PEROBS
|
|
633
687
|
str
|
634
688
|
end
|
635
689
|
|
636
|
-
private
|
637
|
-
|
638
|
-
def write_node
|
639
|
-
bytes = [ @blob_address, @size,
|
640
|
-
@parent ? @parent.node_address : 0,
|
641
|
-
@smaller ? @smaller.node_address : 0,
|
642
|
-
@equal ? @equal.node_address : 0,
|
643
|
-
@larger ? @larger.node_address : 0].pack(NODE_BYTES_FORMAT)
|
644
|
-
@tree.nodes.store_blob(@node_address, bytes)
|
645
|
-
end
|
646
|
-
|
647
|
-
def read_node
|
648
|
-
unless @node_address > 0
|
649
|
-
PEROBS.log.fatal "@node_address must be larger than 0"
|
650
|
-
end
|
651
|
-
return false unless (bytes = @tree.nodes.retrieve_blob(@node_address))
|
652
|
-
|
653
|
-
@blob_address, @size, parent_node_address,
|
654
|
-
smaller_node_address, equal_node_address,
|
655
|
-
larger_node_address = bytes.unpack(NODE_BYTES_FORMAT)
|
656
|
-
# The parent address can also be 0 as the parent can rightly point back
|
657
|
-
# to the root node which always has the address 0.
|
658
|
-
@parent = parent_node_address != 0 ?
|
659
|
-
SpaceTreeNodeLink.new(@tree, parent_node_address) : nil
|
660
|
-
@smaller = smaller_node_address != 0 ?
|
661
|
-
SpaceTreeNodeLink.new(@tree, smaller_node_address) : nil
|
662
|
-
@equal = equal_node_address != 0 ?
|
663
|
-
SpaceTreeNodeLink.new(@tree, equal_node_address) : nil
|
664
|
-
@larger = larger_node_address != 0 ?
|
665
|
-
SpaceTreeNodeLink.new(@tree, larger_node_address) : nil
|
666
|
-
|
667
|
-
true
|
668
|
-
end
|
669
|
-
|
670
690
|
end
|
671
691
|
|
672
692
|
end
|