bud 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/budplot +37 -12
- data/bin/budtimelines +20 -22
- data/bin/budvis +1 -1
- data/docs/cheat.md +54 -17
- data/docs/operational.md +1 -1
- data/lib/bud.rb +31 -18
- data/lib/bud/bud_meta.rb +7 -7
- data/lib/bud/bust/bust.rb +2 -2
- data/lib/bud/collections.rb +80 -68
- data/lib/bud/deploy/ec2deploy.rb +1 -1
- data/lib/bud/graphs.rb +9 -11
- data/lib/bud/joins.rb +29 -20
- data/lib/bud/monkeypatch.rb +8 -2
- data/lib/bud/rebl.rb +29 -13
- data/lib/bud/rewrite.rb +40 -39
- data/lib/bud/server.rb +1 -1
- data/lib/bud/state.rb +3 -3
- data/lib/bud/storage/dbm.rb +4 -4
- data/lib/bud/storage/tokyocabinet.rb +4 -4
- data/lib/bud/storage/zookeeper.rb +3 -3
- data/lib/bud/stratify.rb +6 -3
- data/lib/bud/viz.rb +1 -1
- data/lib/bud/viz_util.rb +11 -7
- metadata +10 -8
data/bin/budplot
CHANGED
@@ -8,7 +8,7 @@ require 'bud/viz_util'
|
|
8
8
|
|
9
9
|
include VizUtil
|
10
10
|
|
11
|
-
def
|
11
|
+
def is_constant?(m)
|
12
12
|
begin
|
13
13
|
return (eval("defined?(#{m})") == "constant")
|
14
14
|
rescue SyntaxError
|
@@ -16,16 +16,42 @@ def is_module?(m)
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def make_instance(mods)
|
20
|
+
# If we're given a single identifier that names a class, just return an
|
21
|
+
# instance of that class. Otherwise, define a bogus class that includes all
|
22
|
+
# the module names specified by the user and return an instance.
|
20
23
|
mods.each do |m|
|
21
|
-
unless
|
22
|
-
puts "Error: unable to find definition for module \"#{m}\""
|
24
|
+
unless is_constant? m
|
25
|
+
puts "Error: unable to find definition for module or class \"#{m}\""
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
mod_klass = eval m
|
30
|
+
if mod_klass.class == Class
|
31
|
+
if mods.length == 1
|
32
|
+
return mod_klass.new
|
33
|
+
else
|
34
|
+
puts "Error: cannot intermix classes and modules"
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
elsif mod_klass.class != Module
|
38
|
+
puts "Error: \"#{m}\" is not a module or class name"
|
23
39
|
exit
|
24
40
|
end
|
25
41
|
end
|
26
42
|
|
27
|
-
|
28
|
-
|
43
|
+
def_lines = ["class FooBar",
|
44
|
+
"include Bud",
|
45
|
+
mods.map {|m| "include #{m}"},
|
46
|
+
"end"
|
47
|
+
]
|
48
|
+
class_def = def_lines.flatten.join("\n")
|
49
|
+
eval(class_def)
|
50
|
+
FooBar.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def process(mods)
|
54
|
+
d = make_instance(mods)
|
29
55
|
|
30
56
|
interfaces = {}
|
31
57
|
d.t_provides.each do |name, is_input|
|
@@ -39,10 +65,10 @@ def process(mods)
|
|
39
65
|
d.tables.each do |t|
|
40
66
|
tab = t[0].to_s
|
41
67
|
tabinf[tab] = t[1].class.to_s
|
68
|
+
next if d.builtin_tables.has_key? t[0]
|
69
|
+
|
42
70
|
if interfaces[tab].nil?
|
43
|
-
|
44
|
-
priv << t
|
45
|
-
end
|
71
|
+
priv << t
|
46
72
|
else
|
47
73
|
if interfaces[tab]
|
48
74
|
inp << t
|
@@ -84,7 +110,6 @@ def do_table(f, info)
|
|
84
110
|
info.sort{|a, b| a[0].to_s <=> b[0].to_s}.each do |tbl_name, tbl_impl|
|
85
111
|
next if tbl_impl.schema.nil?
|
86
112
|
key_s = tbl_impl.key_cols.join(", ")
|
87
|
-
key_s = "[]" if key_s == ""
|
88
113
|
val_s = tbl_impl.val_cols.join(", ")
|
89
114
|
f.puts "<tr><td><b>#{tbl_name}</b></td>"
|
90
115
|
f.puts "<td>#{key_s}</td><td>#{val_s}</td></tr>"
|
@@ -93,7 +118,7 @@ def do_table(f, info)
|
|
93
118
|
end
|
94
119
|
|
95
120
|
if ARGV.length < 2
|
96
|
-
puts "Usage: budplot LIST_OF_FILES
|
121
|
+
puts "Usage: budplot LIST_OF_FILES LIST_OF_MODULES_OR_CLASSES"
|
97
122
|
exit
|
98
123
|
end
|
99
124
|
|
@@ -102,7 +127,7 @@ end
|
|
102
127
|
modules = []
|
103
128
|
ARGV.each do |arg|
|
104
129
|
if File.exists? arg
|
105
|
-
|
130
|
+
require arg
|
106
131
|
else
|
107
132
|
modules << arg
|
108
133
|
end
|
data/bin/budtimelines
CHANGED
@@ -36,7 +36,7 @@ end
|
|
36
36
|
module TPSchema
|
37
37
|
state do
|
38
38
|
table :deltas, [:bud_time, :tab, :nm]
|
39
|
-
table :zerod_cards, [:bud_time, :table, :cnt]
|
39
|
+
table :zerod_cards, [:bud_time, :table, :cnt, :pred]
|
40
40
|
table :nm_tab, [:table]
|
41
41
|
table :collapsible_base, [:start, :fin]
|
42
42
|
table :collapsible, [:start, :fin]
|
@@ -49,19 +49,18 @@ end
|
|
49
49
|
module DeltaLogic
|
50
50
|
include TPSchema
|
51
51
|
bloom do
|
52
|
-
zerod_cards <= cardinalities
|
52
|
+
zerod_cards <= cardinalities{|c| c + [c.bud_time-1]}
|
53
53
|
zerod_cards <= (times * depends).pairs do |t, d|
|
54
54
|
unless cardinalities{|c| c[1] if c[0] == t.bud_time}.include? d[1]
|
55
|
-
[t.bud_time, d[1], 0]
|
55
|
+
[t.bud_time, d[1], 0, t.bud_time - 1]
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
59
|
nm_tab <= depends do |d|
|
61
60
|
[d[1]] if d[4]
|
62
61
|
end
|
63
62
|
|
64
|
-
deltas <= (zerod_cards * zerod_cards).pairs(:table => :table) do |c1, c2|
|
63
|
+
deltas <= (zerod_cards * zerod_cards).pairs(:table => :table, :bud_time => :pred) do |c1, c2|
|
65
64
|
if c1.bud_time == c2.bud_time - 1 and c1.table == c2.table and c1.cnt != c2.cnt
|
66
65
|
if nm_tab.include? [c1.table]
|
67
66
|
[c2.bud_time, c1.table, true]
|
@@ -77,26 +76,22 @@ module VanillaTraceProcessing
|
|
77
76
|
include TPSchema
|
78
77
|
include DeltaLogic
|
79
78
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
79
|
+
state do
|
80
|
+
scratch :tp, times.schema
|
81
|
+
scratch :bi1, best_interval.schema
|
82
|
+
end
|
86
83
|
|
84
|
+
bloom do
|
85
|
+
tp <= times.notin(deltas, :bud_time => :bud_time) {|t, d| true if d.nm}
|
86
|
+
collapsible_base <= tp {|t| [t.bud_time-1, t.bud_time]}
|
87
87
|
collapsible <= collapsible_base
|
88
88
|
|
89
89
|
collapsible <= (collapsible_base * collapsible).pairs(:fin => :start) do |b, c|
|
90
|
-
puts "another collapsible row; now #{b.inspect} - #{c.inspect}"
|
91
90
|
[b.start, c.fin]
|
92
91
|
end
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
or collapsible{|c2| c2.fin == c.fin and c2.start < c.start}.any?
|
97
|
-
c
|
98
|
-
end
|
99
|
-
end
|
93
|
+
bi1 <= collapsible.notin(collapsible, :start => :start) {|c1, c2| true if c2.fin > c1.fin}
|
94
|
+
best_interval <= bi1.notin(collapsible, :fin => :fin) {|c1, c2| true if c2.start < c1.start}
|
100
95
|
end
|
101
96
|
end
|
102
97
|
|
@@ -139,12 +134,12 @@ da = GlobalDepAnalyzer.new
|
|
139
134
|
|
140
135
|
ARGV.each do |arg_raw|
|
141
136
|
elems = arg_raw.split("_")
|
142
|
-
arg = elems[1..
|
137
|
+
arg = elems[1..4].join("_")
|
143
138
|
clean_arg << arg
|
144
139
|
snd_info[arg] = []
|
145
140
|
rcv_info[arg] = []
|
146
141
|
|
147
|
-
meta, data = get_meta2("#{arg_raw}
|
142
|
+
meta, data = get_meta2("#{arg_raw}")
|
148
143
|
tp = SimpleTraceProcessor.new
|
149
144
|
|
150
145
|
meta[:depends].each do |m|
|
@@ -166,7 +161,6 @@ ARGV.each do |arg_raw|
|
|
166
161
|
|
167
162
|
tp.tick
|
168
163
|
|
169
|
-
|
170
164
|
puts "entries in collapsible: #{tp.collapsible.length}"
|
171
165
|
puts "entries in base: #{tp.collapsible_base.length}"
|
172
166
|
puts "entries in deltas: #{tp.deltas.length}"
|
@@ -182,7 +176,11 @@ da.tick
|
|
182
176
|
nmreach = {}
|
183
177
|
da.depends_tc.each do |d|
|
184
178
|
nmreach[d[0]] = {} unless nmreach[d[0]]
|
185
|
-
nmreach[d[0]][d[1]]
|
179
|
+
if nmreach[d[0]][d[1]]
|
180
|
+
nmreach[d[0]][d[1]] = d[3] or nmreach[d[0]][d[1]]
|
181
|
+
else
|
182
|
+
nmreach[d[0]][d[1]] = d[3]
|
183
|
+
end
|
186
184
|
end
|
187
185
|
|
188
186
|
# our local intervals relations are too optimistic. to say that intervals[foo] = [2, 5]
|
data/bin/budvis
CHANGED
data/docs/cheat.md
CHANGED
@@ -82,6 +82,13 @@ State declaration includes interval (in seconds).
|
|
82
82
|
|
83
83
|
periodic :timer, 0.1
|
84
84
|
|
85
|
+
Note that because periodics are just a simple wrapper over the system clock, Bud
|
86
|
+
provides few semantic guarantees about the behavior of periodics. In particular,
|
87
|
+
periodics execute in a best-effort manner (there is no guarantee of timely
|
88
|
+
delivery of a periodic tuple), and the system clock value stored in the `val`
|
89
|
+
field may not be monotonically increasing (e.g., if the system clock is changed
|
90
|
+
in the midst of Bud execution).
|
91
|
+
|
85
92
|
### stdio ###
|
86
93
|
Built-in scratch collection for performing terminal I/O.<br>
|
87
94
|
System-provided attributes: `[:line] => []`
|
@@ -151,12 +158,10 @@ update/upsert:
|
|
151
158
|
|
152
159
|
* `left <+- right` (*deferred*)<br>
|
153
160
|
deferred insert of items on rhs and deferred deletion of items with matching
|
154
|
-
keys on lhs.
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
the corresponding rhs fact. Note that both the removal and insertion operators
|
159
|
-
happen atomically in the next timestep.
|
161
|
+
keys on lhs. That is, for each fact produced by the rhs, the upsert operator
|
162
|
+
removes any existing tuples that match on the lhs collection's key columns
|
163
|
+
before inserting the corresponding rhs fact. Note that both the removal and
|
164
|
+
insertion operations happen atomically in the next timestep.
|
160
165
|
|
161
166
|
### Collection Methods ###
|
162
167
|
Standard Ruby methods used on a BudCollection `bc`:
|
@@ -184,26 +189,36 @@ implicit map:
|
|
184
189
|
|
185
190
|
`bc.include?`:
|
186
191
|
|
187
|
-
|
192
|
+
# This is similar to SQL's NOT IN; note that Bud provides a "notin"
|
193
|
+
# collection method that should probably be preferred to this approach.
|
194
|
+
t5 <= bc do |t|
|
188
195
|
t unless t2.include?([t.col1, t.col2])
|
189
196
|
end
|
190
197
|
|
191
198
|
## BudCollection-Specific Methods ##
|
192
|
-
`bc.
|
199
|
+
`bc.schema`: returns the schema of `bc` (Hash of key column names => non-key column names)<br>
|
193
200
|
|
194
|
-
`bc.
|
201
|
+
`bc.cols`: returns the column names in `bc` as an Array<br>
|
195
202
|
|
196
|
-
`bc.
|
203
|
+
`bc.key_cols`: returns the key column names in `bc` as an Array<br>
|
197
204
|
|
198
|
-
|
205
|
+
`bc.val_cols`: returns the non-key column names in `bc` as an Array<br>
|
206
|
+
|
207
|
+
`bc.keys`: projects `bc` to key columns<br>
|
208
|
+
|
209
|
+
`bc.values`: projects `bc` to non-key columns<br>
|
199
210
|
|
200
211
|
`chan.payloads`: projects `chan` to non-address columns. Only defined for channels.
|
201
212
|
|
202
213
|
# at sender
|
203
|
-
msgs <~ requests {|r| "127.0.0.1:12345", r}
|
214
|
+
msgs <~ requests {|r| ["127.0.0.1:12345", r]}
|
204
215
|
# at receiver
|
205
216
|
requests <= msgs.payloads
|
206
217
|
|
218
|
+
`bc.inspected`: returns a human-readable version of the contents of `bc`
|
219
|
+
|
220
|
+
stdio <~ bc.inspected
|
221
|
+
|
207
222
|
`bc.exists?`: test for non-empty collection. Can optionally pass in a block.
|
208
223
|
|
209
224
|
stdio <~ [["Wake Up!"] if timer.exists?]
|
@@ -211,10 +226,18 @@ implicit map:
|
|
211
226
|
[r.inspect] if msgs.exists?{|m| r.ident == m.ident}
|
212
227
|
end
|
213
228
|
|
214
|
-
`bc.notin(bc2, `*optional hash pairs
|
215
|
-
Output
|
216
|
-
|
217
|
-
|
229
|
+
`bc.notin(bc2, `*optional hash pairs*`, `*optional ruby block*`)`:<br>
|
230
|
+
Output the facts in `bc` that do not appear in `bc2`, as follows. First, we form a temporary collection `t` as follows:
|
231
|
+
|
232
|
+
1. Join `bc` and `bc2` according to the specified hash pairs. Hash pairs can
|
233
|
+
be fully qualified (`bc.attr1 => bc2.attr2`) or shorthand (`:attr1 =>
|
234
|
+
:attr2`).
|
235
|
+
|
236
|
+
2. If a code block is specified, invoke the block on every pair of matching
|
237
|
+
tuples in the join result. Any matches for which the block returns `nil`
|
238
|
+
are removed from `t`.
|
239
|
+
|
240
|
+
Finally, we output every tuple of `bc` that does *not* appear in `t`.
|
218
241
|
|
219
242
|
# output items from foo if (a) there is no matching key in bar, or
|
220
243
|
# (b) all matching keys in bar have a smaller value
|
@@ -330,13 +353,23 @@ There are two ways to use a module *B* in another Bloom module *A*:
|
|
330
353
|
(facts inserted into a collection defined in `b1` won't also be inserted
|
331
354
|
into `b2`'s copy of the collection).
|
332
355
|
|
356
|
+
In practice, a Bloom program is often composed of a collection of modules (which
|
357
|
+
may themselves include or import sub-modules) and one "top-level class" that
|
358
|
+
includes/imports those modules as well as the `Bud` module. An instance of this
|
359
|
+
top-level class represents an instance of the Bud interpreter; it is on this
|
360
|
+
top-level class that the `run_fg` method should be invoked, for example.
|
361
|
+
|
362
|
+
Note that to enable the Bloom DSL for a collection of Ruby code, it is
|
363
|
+
sufficient to include the `Bud` module *once* in the top-level class. That is,
|
364
|
+
you should *not* include `Bud` in every Bloom module that you write.
|
365
|
+
|
333
366
|
## Skeleton of a Bud Module ##
|
334
367
|
|
335
368
|
require 'rubygems'
|
336
369
|
require 'bud'
|
337
370
|
|
338
371
|
module YourModule
|
339
|
-
|
372
|
+
import SubModule => :sub_m
|
340
373
|
|
341
374
|
state do
|
342
375
|
...
|
@@ -355,3 +388,7 @@ There are two ways to use a module *B* in another Bloom module *A*:
|
|
355
388
|
end
|
356
389
|
end
|
357
390
|
|
391
|
+
class TopLevelClass
|
392
|
+
include Bud
|
393
|
+
include YourModule
|
394
|
+
end
|
data/docs/operational.md
CHANGED
@@ -31,7 +31,7 @@ It is important to understand how the Bloom collection operators fit into these
|
|
31
31
|
|
32
32
|
## Atomicity: Timesteps and Deferred Operators ##
|
33
33
|
|
34
|
-
The only instantaneous Bloom operator is a merge (`<=`), which can only introduce additional items into a collection--it
|
34
|
+
The only instantaneous Bloom operator is a merge (`<=`), which can only introduce additional items into a collection--it cannot delete or change existing items. As a result, all state within a Bloom timestep is *immutable*: once an item is in a collection at timestep *T*, it stays in that collection throughout timestep *T*. (And forever after, the fact that the item was in that collection at timestep *T* remains true.)
|
35
35
|
|
36
36
|
To get atomic state change in Bloom, you exploit the combination of two language features:
|
37
37
|
|
data/lib/bud.rb
CHANGED
@@ -5,6 +5,10 @@ require 'socket'
|
|
5
5
|
require 'superators'
|
6
6
|
require 'thread'
|
7
7
|
|
8
|
+
# Ruby2Ruby 1.3.1 is buggy (see issue #250)
|
9
|
+
gem 'ruby2ruby', '< 1.3.1'
|
10
|
+
require 'ruby2ruby'
|
11
|
+
|
8
12
|
require 'bud/monkeypatch'
|
9
13
|
|
10
14
|
require 'bud/aggs'
|
@@ -61,7 +65,8 @@ $bud_instances = {} # Map from instance id => Bud instance
|
|
61
65
|
module Bud
|
62
66
|
attr_reader :strata, :budtime, :inbound, :options, :meta_parser, :viz, :rtracer
|
63
67
|
attr_reader :dsock
|
64
|
-
attr_reader :
|
68
|
+
attr_reader :builtin_tables, :tables
|
69
|
+
attr_reader :channels, :tc_tables, :zk_tables, :dbm_tables, :sources, :sinks
|
65
70
|
attr_reader :stratum_first_iter, :joinstate
|
66
71
|
attr_reader :this_stratum, :this_rule, :rule_orig_src
|
67
72
|
attr_reader :running_async
|
@@ -96,8 +101,8 @@ module Bud
|
|
96
101
|
# * <tt>:deploy</tt> enable deployment
|
97
102
|
# * <tt>:deploy_child_opts</tt> option hash to pass to deployed instances
|
98
103
|
def initialize(options={})
|
104
|
+
@builtin_tables = {}
|
99
105
|
@tables = {}
|
100
|
-
@table_meta = []
|
101
106
|
@rewritten_strata = []
|
102
107
|
@channels = {}
|
103
108
|
@tc_tables = {}
|
@@ -180,11 +185,14 @@ module Bud
|
|
180
185
|
|
181
186
|
# Rewrite methods defined in the given klass to expand module references and
|
182
187
|
# temp collections. Imported modules are rewritten during the import process;
|
183
|
-
# we rewrite the main
|
184
|
-
# only rewrite each distinct Class once
|
188
|
+
# we rewrite the main class associated with this Bud instance and any included
|
189
|
+
# modules here. Note that we only rewrite each distinct Class once, and we
|
190
|
+
# skip methods defined by the Bud (Ruby) module directly (since we can be sure
|
191
|
+
# those won't reference Bloom modules).
|
185
192
|
def self.rewrite_local_methods(klass)
|
186
193
|
@done_rewrite ||= {}
|
187
194
|
return if @done_rewrite.has_key? klass.name
|
195
|
+
return if klass.name == self.name # Skip methods defined in the Bud module
|
188
196
|
|
189
197
|
u = Unifier.new
|
190
198
|
ref_expander = NestedRefRewriter.new(klass.bud_import_table)
|
@@ -368,7 +376,7 @@ module Bud
|
|
368
376
|
# If we're called from the EventMachine thread (and EM is running), blocking
|
369
377
|
# the current thread would imply deadlocking ourselves.
|
370
378
|
if Thread.current == EventMachine::reactor_thread and EventMachine::reactor_running?
|
371
|
-
raise BudError, "
|
379
|
+
raise BudError, "cannot invoke run_fg from inside EventMachine"
|
372
380
|
end
|
373
381
|
|
374
382
|
q = Queue.new
|
@@ -486,7 +494,7 @@ module Bud
|
|
486
494
|
cb_id = nil
|
487
495
|
schedule_and_wait do
|
488
496
|
unless @tables.has_key? tbl_name
|
489
|
-
raise Bud::BudError, "
|
497
|
+
raise Bud::BudError, "no such table: #{tbl_name}"
|
490
498
|
end
|
491
499
|
|
492
500
|
raise Bud::BudError if @callbacks.has_key? @callback_id
|
@@ -500,7 +508,7 @@ module Bud
|
|
500
508
|
# Unregister the callback that has the given ID.
|
501
509
|
def unregister_callback(id)
|
502
510
|
schedule_and_wait do
|
503
|
-
raise Bud::BudError, "
|
511
|
+
raise Bud::BudError, "missing callback: #{id.inspect}" unless @callbacks.has_key? id
|
504
512
|
@callbacks.delete(id)
|
505
513
|
end
|
506
514
|
end
|
@@ -730,16 +738,19 @@ module Bud
|
|
730
738
|
private
|
731
739
|
|
732
740
|
# Builtin BUD state (predefined collections). We could define this using the
|
733
|
-
# standard
|
741
|
+
# standard state block syntax, but we want to ensure that builtin state is
|
734
742
|
# initialized before user-defined state.
|
735
743
|
def builtin_state
|
744
|
+
# We expect there to be no previously-defined tables
|
745
|
+
raise BudError unless @tables.empty?
|
746
|
+
|
736
747
|
loopback :localtick, [:col1]
|
737
748
|
@stdio = terminal :stdio
|
738
749
|
readonly :signals, [:key]
|
739
750
|
scratch :halt, [:key]
|
740
751
|
@periodics = table :periodics_tbl, [:pername] => [:period]
|
741
752
|
|
742
|
-
#
|
753
|
+
# For Bud reflection
|
743
754
|
table :t_rules, [:rule_id] => [:lhs, :op, :src, :orig_src]
|
744
755
|
table :t_depends, [:rule_id, :lhs, :op, :body] => [:nm]
|
745
756
|
table :t_depends_tc, [:head, :body, :via, :neg, :temporal]
|
@@ -749,6 +760,9 @@ module Bud
|
|
749
760
|
table :t_cycle, [:predicate, :via, :neg, :temporal]
|
750
761
|
table :t_table_info, [:tab_name, :tab_type]
|
751
762
|
table :t_table_schema, [:tab_name, :col_name, :ord, :loc]
|
763
|
+
|
764
|
+
# Identify builtin tables as such
|
765
|
+
@builtin_tables = @tables.clone
|
752
766
|
end
|
753
767
|
|
754
768
|
# Handle any inbound tuples off the wire. Received messages are placed
|
@@ -826,7 +840,7 @@ module Bud
|
|
826
840
|
unless new_e.class <= BudError
|
827
841
|
new_e = BudError
|
828
842
|
end
|
829
|
-
raise new_e, "
|
843
|
+
raise new_e, "exception during Bud evaluation.\nException: #{e.inspect}.#{src_msg}"
|
830
844
|
end
|
831
845
|
end
|
832
846
|
@stratum_first_iter = false
|
@@ -835,14 +849,13 @@ module Bud
|
|
835
849
|
colls = @stratum_collection_map[strat_num] if @stratum_collection_map
|
836
850
|
colls ||= @tables.keys
|
837
851
|
colls.each do |name|
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
# ignore missing tables; rebl for example deletes them mid-stream
|
852
|
+
coll = @tables[name]
|
853
|
+
# ignore missing tables; rebl for example deletes them mid-stream
|
854
|
+
next if coll.nil?
|
855
|
+
|
856
|
+
unless coll.delta.empty? and coll.new_delta.empty?
|
857
|
+
fixpoint = false unless coll.new_delta.empty?
|
858
|
+
coll.tick_deltas
|
846
859
|
end
|
847
860
|
end
|
848
861
|
end while not fixpoint
|