perobs 4.0.0 → 4.4.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.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +27 -16
  3. data/lib/perobs/Array.rb +66 -19
  4. data/lib/perobs/BTree.rb +106 -15
  5. data/lib/perobs/BTreeBlob.rb +4 -3
  6. data/lib/perobs/BTreeDB.rb +5 -4
  7. data/lib/perobs/BTreeNode.rb +482 -156
  8. data/lib/perobs/BTreeNodeLink.rb +10 -0
  9. data/lib/perobs/BigArray.rb +285 -0
  10. data/lib/perobs/BigArrayNode.rb +1002 -0
  11. data/lib/perobs/BigHash.rb +246 -0
  12. data/lib/perobs/BigTree.rb +197 -0
  13. data/lib/perobs/BigTreeNode.rb +873 -0
  14. data/lib/perobs/Cache.rb +48 -10
  15. data/lib/perobs/ConsoleProgressMeter.rb +61 -0
  16. data/lib/perobs/DataBase.rb +4 -3
  17. data/lib/perobs/DynamoDB.rb +57 -15
  18. data/lib/perobs/EquiBlobsFile.rb +155 -50
  19. data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
  20. data/lib/perobs/FlatFile.rb +519 -227
  21. data/lib/perobs/FlatFileBlobHeader.rb +113 -54
  22. data/lib/perobs/FlatFileDB.rb +49 -23
  23. data/lib/perobs/FuzzyStringMatcher.rb +175 -0
  24. data/lib/perobs/Hash.rb +127 -33
  25. data/lib/perobs/IDList.rb +144 -0
  26. data/lib/perobs/IDListPage.rb +107 -0
  27. data/lib/perobs/IDListPageFile.rb +180 -0
  28. data/lib/perobs/IDListPageRecord.rb +142 -0
  29. data/lib/perobs/Object.rb +18 -15
  30. data/lib/perobs/ObjectBase.rb +46 -5
  31. data/lib/perobs/PersistentObjectCache.rb +57 -68
  32. data/lib/perobs/PersistentObjectCacheLine.rb +24 -12
  33. data/lib/perobs/ProgressMeter.rb +97 -0
  34. data/lib/perobs/SpaceManager.rb +273 -0
  35. data/lib/perobs/SpaceTree.rb +21 -12
  36. data/lib/perobs/SpaceTreeNode.rb +53 -61
  37. data/lib/perobs/Store.rb +264 -145
  38. data/lib/perobs/version.rb +1 -1
  39. data/lib/perobs.rb +2 -0
  40. data/perobs.gemspec +4 -4
  41. data/test/Array_spec.rb +15 -6
  42. data/test/BTree_spec.rb +6 -2
  43. data/test/BigArray_spec.rb +261 -0
  44. data/test/BigHash_spec.rb +152 -0
  45. data/test/BigTreeNode_spec.rb +153 -0
  46. data/test/BigTree_spec.rb +259 -0
  47. data/test/EquiBlobsFile_spec.rb +105 -1
  48. data/test/FNV_Hash_1a_64_spec.rb +59 -0
  49. data/test/FlatFileDB_spec.rb +198 -14
  50. data/test/FuzzyStringMatcher_spec.rb +261 -0
  51. data/test/Hash_spec.rb +13 -3
  52. data/test/IDList_spec.rb +77 -0
  53. data/test/LegacyDBs/LegacyDB.rb +155 -0
  54. data/test/LegacyDBs/version_3/class_map.json +1 -0
  55. data/test/LegacyDBs/version_3/config.json +1 -0
  56. data/test/LegacyDBs/version_3/database.blobs +0 -0
  57. data/test/LegacyDBs/version_3/database_spaces.blobs +0 -0
  58. data/test/LegacyDBs/version_3/index.blobs +0 -0
  59. data/test/LegacyDBs/version_3/version +1 -0
  60. data/test/LockFile_spec.rb +9 -6
  61. data/test/SpaceManager_spec.rb +176 -0
  62. data/test/SpaceTree_spec.rb +4 -1
  63. data/test/Store_spec.rb +305 -203
  64. data/test/spec_helper.rb +9 -4
  65. metadata +57 -16
  66. data/lib/perobs/BTreeNodeCache.rb +0 -109
  67. data/lib/perobs/TreeDB.rb +0 -277
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a988f009b1b97bc8da2b2bcfeb65399284a07032
4
- data.tar.gz: 4346d5cdbec5b4154741f0a41d3f526985803912
2
+ SHA256:
3
+ metadata.gz: c2c526c97aab15c09f8e8acbcb432203e7d60fef8c504df6ddb0b8f35459cca0
4
+ data.tar.gz: ad077bb879c041289c7a474e87645064ff84203c674640aff59b5f23923ac1b7
5
5
  SHA512:
6
- metadata.gz: 639673c10faa5082c742258ee9848f5e951c6d1ec7fc9f03ec7066260ea5042b6c8b90020df6fa506b8be54cc5fd9793a0939f80ef1da726c27832c8a2eb8c85
7
- data.tar.gz: 92e63145a1fc9b6e76dbdf6f4cdebfb5a8935bfcbd93a9634e22c12f874b945b656a17fe93aa03a7a3189a00371d2e2abd8d411862ad0ad51b0c364f7b7dc027
6
+ metadata.gz: b86fc662ef8bbf623ee7da777f023a74e3db9502df18c45dcda9ce244982225aaec7e74ce25378147c807775765c57d801810ac16be01d2145227bb0642caa3b
7
+ data.tar.gz: d0c5a79eb60a1221bc385a1fafbc77a1aaa1e47782b3ea27ca92f5f7c539804bc00a388fdddc1ab956d7805dbdd52d055f5cdd76a8bb155ec5ada4335b7b9175
data/README.md CHANGED
@@ -6,11 +6,12 @@ them from PEROBS::Object. They will be in memory when needed and
6
6
  transparently stored into a persistent storage.
7
7
 
8
8
  This library is ideal for Ruby applications that work on huge, mostly
9
- constant data sets and usually handle a small subset of the data at a
9
+ static data sets and usually process a small subset of the data at a
10
10
  time. To ensure data consistency of a larger data set, you can use
11
11
  transactions to make modifications of multiple objects atomicaly.
12
12
  Transactions can be nested and are aborted when an exception is
13
- raised.
13
+ raised. PEROBS is thread-safe, so you can use it in a multi-threaded
14
+ application.
14
15
 
15
16
  ## Usage
16
17
 
@@ -108,7 +109,7 @@ class Person < PEROBS::Object
108
109
  attr_init(:father) do { @store.new(Person, 'Dad') }
109
110
  end
110
111
 
111
- def merry(spouse)
112
+ def marry(spouse)
112
113
  self.spouse = spouse
113
114
  self.status = :married
114
115
  end
@@ -120,15 +121,18 @@ class Person < PEROBS::Object
120
121
 
121
122
  end
122
123
 
123
- store = PEROBS::Store.new('family')
124
- store['grandpa'] = joe = store.new(Person, 'Joe')
125
- store['grandma'] = jane = store.new(Person, 'Jane')
126
- jim = store.new(Person, 'Jim')
127
- jim.father = joe
128
- joe.kids << jim
129
- jim.mother = jane
130
- jane.kids << jim
131
- store.exit
124
+ begin
125
+ store = PEROBS::Store.new('family')
126
+ store['grandpa'] = joe = store.new(Person, 'Joe')
127
+ store['grandma'] = jane = store.new(Person, 'Jane')
128
+ jim = store.new(Person, 'Jim')
129
+ jim.father = joe
130
+ joe.kids << jim
131
+ jim.mother = jane
132
+ jane.kids << jim
133
+ ensure
134
+ store.exit
135
+ end
132
136
  ```
133
137
 
134
138
  When you run this script, a folder named 'family' will be created. It
@@ -166,9 +170,15 @@ object to another object.
166
170
 
167
171
  ### Caveats and known issues
168
172
 
169
- PEROBS is currently not thread-safe. You cannot simultaneously access
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.
173
+ You cannot simultaneously access the database from multiple
174
+ applications concurrently. The library uses locks to ensure that only
175
+ one Store object is accessing the database at a time.
176
+
177
+ In case the application terminates without calling Store::exit(), the
178
+ database or the database index could get corrupted. To check the
179
+ consistency of your database you can use Store::check(). To check and
180
+ repair the database you can call Store::repair(). Depending on the
181
+ size of your database, these operations can last minutes to hours.
172
182
 
173
183
  ## Installation
174
184
 
@@ -188,7 +198,8 @@ Or install it yourself as:
188
198
 
189
199
  ## Copyright and License
190
200
 
191
- Copyright (c) 2015, 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
201
+ Copyright (c) 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 by
202
+ Chris Schlaeger <chris@taskjuggler.org>
192
203
 
193
204
  PEROBS and all accompanying files are licensed under this MIT License
194
205
 
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,17 +100,45 @@ 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 [Integer] 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
144
  # @return [Array of Integer] IDs of referenced objects
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
  #
@@ -32,7 +32,7 @@ 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, :node_cache
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 = PersistentObjectCache.new(512, BTreeNode, self)
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,23 +97,44 @@ module PEROBS
89
97
 
90
98
  @node_cache.clear
91
99
  @nodes.open
92
- node = @nodes.total_entries == 0 ?
93
- BTreeNode::create(self) :
94
- BTreeNode::load(self, @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
95
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')
96
116
  end
97
117
 
98
118
  # Close the tree file.
99
119
  def close
100
120
  sync
121
+ PEROBS.log.info "BTree file #{@name} has currently " +
122
+ "#{@nodes.total_entries} used entries and #{@nodes.total_spaces} " +
123
+ "unused entries"
101
124
  @nodes.close
102
125
  @root = nil
103
126
  end
104
127
 
128
+ # @return true if file is currently open
129
+ def is_open?
130
+ !@root.nil?
131
+ end
132
+
105
133
  # Clear all pools and forget any registered spaces.
106
134
  def clear
107
135
  @node_cache.clear
108
136
  @nodes.clear
137
+ @size = 0
109
138
  set_root(BTreeNode::create(self))
110
139
  end
111
140
 
@@ -114,6 +143,7 @@ module PEROBS
114
143
  # all stored data from the BTree.
115
144
  def erase
116
145
  @nodes.erase
146
+ @size = 0
117
147
  @root = nil
118
148
  @dirty_flag.forced_unlock
119
149
  end
@@ -121,6 +151,7 @@ module PEROBS
121
151
  # Flush all pending modifications into the tree file.
122
152
  def sync
123
153
  @node_cache.flush(true)
154
+ @nodes.set_custom_data('btree_size', @size)
124
155
  @nodes.sync
125
156
  @dirty_flag.unlock if @dirty_flag.is_locked?
126
157
  end
@@ -128,22 +159,70 @@ module PEROBS
128
159
  # Check if the tree file contains any errors.
129
160
  # @return [Boolean] true if no erros were found, false otherwise
130
161
  def check(&block)
131
- @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
132
194
  end
133
195
 
134
196
  # Register a new node as root node of the tree.
197
+ # @param node [BTreeNode]
135
198
  def set_root(node)
136
199
  @root = node
137
200
  @nodes.first_entry = node.node_address
138
201
  end
139
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)
215
+ end
216
+
140
217
  # Insert a new value into the tree using the key as a unique index. If the
141
218
  # key already exists the old value will be overwritten.
142
219
  # @param key [Integer] Unique key
143
220
  # @param value [Integer] value
144
221
  def insert(key, value)
145
- @root.insert(key, value)
146
- @node_cache.flush
222
+ if @root.insert(key, value)
223
+ @size += 1
224
+ @node_cache.flush
225
+ end
147
226
  end
148
227
 
149
228
  # Retrieve the value associated with the given key. If no entry was found,
@@ -154,10 +233,17 @@ module PEROBS
154
233
  @root.get(key)
155
234
  end
156
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
+
157
243
  # Find and remove the value associated with the given key. If no entry was
158
244
  # found, return nil, otherwise the found value.
159
245
  def remove(key)
160
- removed_value = @root.remove(key)
246
+ @size -= 1 unless (removed_value = @root.remove(key)).nil?
161
247
 
162
248
  # Check if the root node only contains one child link after the delete
163
249
  # operation. Then we can delete that node and pull the tree one level
@@ -187,6 +273,11 @@ module PEROBS
187
273
  @nodes.delete_blob(address)
188
274
  end
189
275
 
276
+ # @return [Integer] The number of entries stored in the tree.
277
+ def entries_count
278
+ @size
279
+ end
280
+
190
281
  # @return [String] Human reable form of the tree.
191
282
  def to_s
192
283
  @root.to_s
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # = BTreeBlob.rb -- Persistent Ruby Object Store
4
4
  #
5
- # Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
5
+ # Copyright (c) 2015, 2016, 2019 by Chris Schlaeger <chris@taskjuggler.org>
6
6
  #
7
7
  # MIT License
8
8
  #
@@ -33,7 +33,7 @@ require 'perobs/RobustFile'
33
33
  module PEROBS
34
34
 
35
35
  # This class manages the usage of the data blobs in the corresponding
36
- # HashedBlobsDB object.
36
+ # BTreeDB object.
37
37
  class BTreeBlob
38
38
 
39
39
  # Magic number used for index files.
@@ -144,11 +144,12 @@ module PEROBS
144
144
 
145
145
  # Remove all entries from the index that have not been marked.
146
146
  # @return [Array] List of deleted object IDs.
147
- def delete_unmarked_entries
147
+ def delete_unmarked_entries(&block)
148
148
  deleted_ids = []
149
149
  # First remove the entry from the hash table.
150
150
  @entries_by_id.delete_if do |id, e|
151
151
  if e[MARKED] == 0
152
+ yield(id) if block_given?
152
153
  deleted_ids << id
153
154
  true
154
155
  else
@@ -2,7 +2,8 @@
2
2
  #
3
3
  # = BTreeDB.rb -- Persistent Ruby Object Store
4
4
  #
5
- # Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
5
+ # Copyright (c) 2015, 2016, 2018, 2019
6
+ # by Chris Schlaeger <chris@taskjuggler.org>
6
7
  #
7
8
  # MIT License
8
9
  #
@@ -58,7 +59,7 @@ module PEROBS
58
59
  # nodes. The insert/find/delete time grows
59
60
  # linearly with the size.
60
61
  def initialize(db_name, options = {})
61
- super(options[:serializer] || :json)
62
+ super(options)
62
63
 
63
64
  @db_dir = db_name
64
65
  # Create the database directory if it doesn't exist yet.
@@ -159,9 +160,9 @@ module PEROBS
159
160
  # Permanently delete all objects that have not been marked. Those are
160
161
  # orphaned and are no longer referenced by any actively used object.
161
162
  # @return [Array] List of IDs that have been removed from the DB.
162
- def delete_unmarked_objects
163
+ def delete_unmarked_objects(&block)
163
164
  deleted_ids = []
164
- each_blob { |blob| deleted_ids += blob.delete_unmarked_entries }
165
+ each_blob { |blob| deleted_ids += blob.delete_unmarked_entries(&block) }
165
166
  deleted_ids
166
167
  end
167
168