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