bud 0.0.4 → 0.0.5
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.
- data/README +2 -3
- data/bin/budvis +0 -66
- data/docs/README.md +27 -15
- data/docs/bust.md +1 -1
- data/docs/cheat.md +79 -30
- data/docs/operational.md +8 -4
- data/examples/basics/paths.rb +5 -3
- data/lib/bud/aggs.rb +1 -1
- data/lib/bud/bud_meta.rb +11 -2
- data/lib/bud/bust/bust.rb +1 -1
- data/lib/bud/collections.rb +78 -20
- data/lib/bud/deploy/threaddeploy.rb +1 -1
- data/lib/bud/errors.rb +3 -0
- data/lib/bud/graphs.rb +25 -26
- data/lib/bud/joins.rb +78 -33
- data/lib/bud/metrics.rb +43 -0
- data/lib/bud/monkeypatch.rb +1 -1
- data/lib/bud/rebl.rb +20 -13
- data/lib/bud/rewrite.rb +217 -39
- data/lib/bud/server.rb +16 -13
- data/lib/bud/state.rb +39 -25
- data/lib/bud/storage/dbm.rb +6 -1
- data/lib/bud/storage/tokyocabinet.rb +6 -0
- data/lib/bud/storage/zookeeper.rb +6 -6
- data/lib/bud/viz.rb +5 -1
- data/lib/bud/viz_util.rb +70 -0
- data/lib/bud.rb +227 -99
- metadata +33 -24
- data/docs/c.html +0 -251
- data/examples/deploy/deploy_ip_port +0 -1
- data/examples/deploy/keys.rb +0 -5
- data/lib/bud.rb.orig +0 -806
data/lib/bud/viz_util.rb
CHANGED
@@ -14,6 +14,50 @@ module TraceCardinality
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
class VizHelper
|
18
|
+
include Bud
|
19
|
+
include TraceCardinality
|
20
|
+
|
21
|
+
def initialize(tabinf, cycle, depends, rules, dir)
|
22
|
+
@t_tabinf = tabinf
|
23
|
+
@t_cycle = cycle
|
24
|
+
@t_depends = depends
|
25
|
+
@t_rules = rules
|
26
|
+
@dir = dir
|
27
|
+
super()
|
28
|
+
end
|
29
|
+
|
30
|
+
def summarize(dir, schema)
|
31
|
+
table_io = {}
|
32
|
+
cardinalities.sort{|a, b| a[0] <=> b[0]}.each do |card|
|
33
|
+
table_io["#{card.table}_#{card.bud_time}"] = start_table(dir, card.table, card.bud_time, schema[card.table])
|
34
|
+
end
|
35
|
+
|
36
|
+
full_info.each do |info|
|
37
|
+
write_table_content(table_io["#{info.table}_#{info.bud_time}"], info.row)
|
38
|
+
end
|
39
|
+
|
40
|
+
table_io.each_value do |tab|
|
41
|
+
end_table(tab)
|
42
|
+
end
|
43
|
+
|
44
|
+
# fix: nested loops
|
45
|
+
times.sort.each do |time|
|
46
|
+
card_info = {}
|
47
|
+
cardinalities.each do |card|
|
48
|
+
if card.bud_time == time.bud_time
|
49
|
+
card_info[card.table] = card.cnt
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
d = "#{@dir}/tm_#{time.bud_time}"
|
54
|
+
write_graphs(@t_tabinf, @t_cycle, @t_depends, @t_rules, d, @dir, nil, false, nil, time.bud_time, card_info)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
17
61
|
module VizUtil #:nodoc: all
|
18
62
|
def graph_from_instance(bud_instance, viz_name, output_base, collapse=true, fmt=nil)
|
19
63
|
tabinf = {}
|
@@ -201,4 +245,30 @@ END_JS
|
|
201
245
|
end
|
202
246
|
return meta, data
|
203
247
|
end
|
248
|
+
|
249
|
+
def start_table(dir, tab, time, schema)
|
250
|
+
str = "#{dir}/#{tab}_#{time}.html"
|
251
|
+
fout = File.new(str, "w")
|
252
|
+
|
253
|
+
fout.puts "<html><title>#{tab} @ #{time}</title>"
|
254
|
+
fout.puts "<table border=1>"
|
255
|
+
fout.puts "<tr>" + schema.map{|s| "<th> #{s} </th>"}.join(" ") + "<tr>" unless schema.nil?
|
256
|
+
fout.close
|
257
|
+
return str
|
258
|
+
end
|
259
|
+
|
260
|
+
def end_table(stream)
|
261
|
+
fp = File.open(stream, "a")
|
262
|
+
fp.puts "</table>"
|
263
|
+
fp.close
|
264
|
+
end
|
265
|
+
|
266
|
+
def write_table_content(fn, row)
|
267
|
+
stream = File.open(fn, "a")
|
268
|
+
stream.puts "<tr>"
|
269
|
+
stream.puts row.map{|c| "<td>#{c.to_s}</td>"}.join(" ")
|
270
|
+
stream.puts "</tr>"
|
271
|
+
stream.close
|
272
|
+
end
|
273
|
+
|
204
274
|
end
|
data/lib/bud.rb
CHANGED
@@ -15,6 +15,7 @@ require 'bud/deploy/forkdeploy'
|
|
15
15
|
require 'bud/deploy/threaddeploy'
|
16
16
|
require 'bud/errors'
|
17
17
|
require 'bud/joins'
|
18
|
+
require 'bud/metrics'
|
18
19
|
require 'bud/rtrace'
|
19
20
|
require 'bud/server'
|
20
21
|
require 'bud/state'
|
@@ -37,10 +38,11 @@ $bud_instances = {} # Map from instance id => Bud instance
|
|
37
38
|
# three main options:
|
38
39
|
#
|
39
40
|
# 1. Synchronously. To do this, instantiate your program and then call tick()
|
40
|
-
# one or more times; each call evaluates a single Bud timestep.
|
41
|
-
#
|
42
|
-
# is mostly intended for "one-shot" programs that
|
43
|
-
# then terminate
|
41
|
+
# one or more times; each call evaluates a single Bud timestep. In this mode,
|
42
|
+
# any network messages or timer events that occur will be buffered until the
|
43
|
+
# next call to tick(). This is mostly intended for "one-shot" programs that
|
44
|
+
# compute a single result and then terminate, or for interactively
|
45
|
+
# "single-stepping" through the execution of an event-driven system.
|
44
46
|
# 2. In a separate thread in the foreground. To do this, instantiate your
|
45
47
|
# program and then call run_fg(). The Bud interpreter will then run, handling
|
46
48
|
# network events and evaluating new timesteps as appropriate. The run_fg()
|
@@ -49,18 +51,22 @@ $bud_instances = {} # Map from instance id => Bud instance
|
|
49
51
|
# program and then call run_bg(). The Bud interpreter will run
|
50
52
|
# asynchronously. To interact with Bud (e.g., insert additional data or
|
51
53
|
# inspect the state of a Bud collection), use the sync_do and async_do
|
52
|
-
# methods.
|
54
|
+
# methods.
|
53
55
|
#
|
54
|
-
# Most programs should use method #3.
|
56
|
+
# Most programs should use method #3. Note that in all three cases, the stop()
|
57
|
+
# method should be used to shutdown a Bud instance and release any resources it
|
58
|
+
# is using.
|
55
59
|
#
|
56
60
|
# :main: Bud
|
57
61
|
module Bud
|
58
62
|
attr_reader :strata, :budtime, :inbound, :options, :meta_parser, :viz, :rtracer
|
59
|
-
attr_reader :dsock
|
60
|
-
attr_reader :tables, :channels, :tc_tables, :zk_tables, :dbm_tables
|
63
|
+
attr_reader :dsock
|
64
|
+
attr_reader :tables, :channels, :tc_tables, :zk_tables, :dbm_tables, :sources, :sinks
|
61
65
|
attr_reader :stratum_first_iter, :joinstate
|
62
|
-
|
63
|
-
|
66
|
+
attr_reader :this_stratum, :this_rule, :rule_orig_src
|
67
|
+
attr_reader :running_async
|
68
|
+
attr_accessor :stratum_collection_map, :rewritten_strata, :no_attr_rewrite_strata
|
69
|
+
attr_accessor :metrics
|
64
70
|
|
65
71
|
# options to the Bud runtime are passed in a hash, with the following keys
|
66
72
|
# * network configuration
|
@@ -72,14 +78,14 @@ module Bud
|
|
72
78
|
# * operating system interaction
|
73
79
|
# * <tt>:stdin</tt> if non-nil, reading from the +stdio+ collection results in reading from this +IO+ handle
|
74
80
|
# * <tt>:stdout</tt> writing to the +stdio+ collection results in writing to this +IO+ handle; defaults to <tt>$stdout</tt>
|
75
|
-
# * <tt>:
|
81
|
+
# * <tt>:signal_handling</tt> how to handle +SIGINT+ and +SIGTERM+. If :none, these signals are ignored. If :bloom, they're passed into the built-in scratch called +signals+. Else shutdown all bud instances.
|
76
82
|
# * tracing and output
|
77
83
|
# * <tt>:quiet</tt> if true, suppress certain messages
|
78
84
|
# * <tt>:trace</tt> if true, generate +budvis+ outputs
|
79
85
|
# * <tt>:rtrace</tt> if true, generate +budplot+ outputs
|
80
86
|
# * <tt>:dump_rewrite</tt> if true, dump results of internal rewriting of Bloom code to a file
|
87
|
+
# * <tt>:metrics</tt> if true, dumps a hash of internal performance metrics
|
81
88
|
# * controlling execution
|
82
|
-
# * <tt>:lazy</tt> if true, prevents runtime from ticking except on external calls to +tick+
|
83
89
|
# * <tt>:tag</tt> a name for this instance, suitable for display during tracing and visualization
|
84
90
|
# * storage configuration
|
85
91
|
# * <tt>:dbm_dir</tt> filesystem directory to hold DBM-backed collections
|
@@ -99,7 +105,8 @@ module Bud
|
|
99
105
|
@zk_tables = {}
|
100
106
|
@callbacks = {}
|
101
107
|
@callback_id = 0
|
102
|
-
@shutdown_callbacks =
|
108
|
+
@shutdown_callbacks = {}
|
109
|
+
@shutdown_callback_id = 0
|
103
110
|
@post_shutdown_callbacks = []
|
104
111
|
@timers = []
|
105
112
|
@inside_tick = false
|
@@ -109,10 +116,15 @@ module Bud
|
|
109
116
|
@done_bootstrap = false
|
110
117
|
@joinstate = {} # joins are stateful, their state needs to be kept inside the Bud instance
|
111
118
|
@instance_id = ILLEGAL_INSTANCE_ID # Assigned when we start running
|
119
|
+
@sources = {}
|
120
|
+
@sinks = {}
|
121
|
+
@metrics = {}
|
122
|
+
@endtime = nil
|
123
|
+
@running_async = false
|
124
|
+
@bud_started = false
|
112
125
|
|
113
126
|
# Setup options (named arguments), along with default values
|
114
127
|
@options = options.clone
|
115
|
-
@lazy = @options[:lazy] ||= false
|
116
128
|
@options[:ip] ||= "127.0.0.1"
|
117
129
|
@ip = @options[:ip]
|
118
130
|
@options[:port] ||= 0
|
@@ -177,6 +189,7 @@ module Bud
|
|
177
189
|
u = Unifier.new
|
178
190
|
ref_expander = NestedRefRewriter.new(klass.bud_import_table)
|
179
191
|
tmp_expander = TempExpander.new
|
192
|
+
with_expander = WithExpander.new
|
180
193
|
r2r = Ruby2Ruby.new
|
181
194
|
|
182
195
|
klass.instance_methods(false).each do |m|
|
@@ -184,24 +197,28 @@ module Bud
|
|
184
197
|
ast = u.process(ast)
|
185
198
|
ast = ref_expander.process(ast)
|
186
199
|
ast = tmp_expander.process(ast)
|
200
|
+
ast = with_expander.process(ast)
|
187
201
|
|
188
|
-
if
|
202
|
+
if ref_expander.did_work or tmp_expander.did_work or with_expander.did_work
|
189
203
|
new_source = r2r.process(ast)
|
190
204
|
klass.module_eval new_source # Replace previous method def
|
191
205
|
end
|
192
206
|
|
193
207
|
ref_expander.did_work = false
|
194
208
|
tmp_expander.did_work = false
|
209
|
+
with_expander.did_work = false
|
195
210
|
end
|
196
211
|
|
197
212
|
# If we found any temp statements in the klass's rule blocks, add a state
|
198
213
|
# block with declarations for the corresponding temp collections.
|
199
|
-
|
200
|
-
if
|
201
|
-
state_src = r2r.process(
|
214
|
+
st = tmp_expander.get_state_meth(klass)
|
215
|
+
if st
|
216
|
+
state_src = r2r.process(st)
|
202
217
|
klass.module_eval(state_src)
|
203
218
|
end
|
204
219
|
|
220
|
+
ModuleRewriter.ast_mangle_with(with_expander, klass)
|
221
|
+
|
205
222
|
# Always rewrite anonymous classes
|
206
223
|
@done_rewrite[klass.name] = true unless klass.name == ""
|
207
224
|
end
|
@@ -275,18 +292,78 @@ module Bud
|
|
275
292
|
# when interacting with it. For example, it is not safe to directly examine
|
276
293
|
# Bud collections from the caller's thread (see async_do and sync_do).
|
277
294
|
#
|
278
|
-
# This instance of Bud will
|
295
|
+
# This instance of Bud will run until stop() is called.
|
279
296
|
def run_bg
|
297
|
+
start
|
298
|
+
|
299
|
+
schedule_and_wait do
|
300
|
+
if @running_async
|
301
|
+
raise BudError, "run_bg called on already-running Bud instance"
|
302
|
+
end
|
303
|
+
@running_async = true
|
304
|
+
|
305
|
+
# Consume any events received while we weren't running async
|
306
|
+
tick_internal
|
307
|
+
end
|
308
|
+
|
309
|
+
@rtracer.sleep if options[:rtrace]
|
310
|
+
end
|
311
|
+
|
312
|
+
# Startup a Bud instance. This starts EventMachine (if needed) and binds to a
|
313
|
+
# UDP server socket. If do_tick is true, we also execute a single Bloom
|
314
|
+
# timestep. Regardless, calling this method does NOT cause Bud to begin
|
315
|
+
# executing timesteps asynchronously (see run_bg).
|
316
|
+
def start(do_tick=false)
|
280
317
|
start_reactor
|
281
|
-
# Wait for Bud to start up before returning
|
282
318
|
schedule_and_wait do
|
283
|
-
|
319
|
+
do_startup unless @bud_started
|
320
|
+
tick_internal if do_tick
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
def do_startup
|
326
|
+
raise BudError, "EventMachine not started" unless EventMachine::reactor_running?
|
327
|
+
raise BudError unless EventMachine::reactor_thread?
|
328
|
+
|
329
|
+
@instance_id = Bud.init_signal_handlers(self)
|
330
|
+
do_start_server
|
331
|
+
@bud_started = true
|
332
|
+
|
333
|
+
# Initialize periodics
|
334
|
+
@periodics.each do |p|
|
335
|
+
@timers << make_periodic_timer(p.pername, p.period)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Arrange for Bud to read from stdin if enabled. Note that we can't do this
|
339
|
+
# earlier because we need to wait for EventMachine startup.
|
340
|
+
@stdio.start_stdin_reader if @options[:stdin]
|
341
|
+
@zk_tables.each_value {|t| t.start_watchers}
|
342
|
+
|
343
|
+
@halt_cb = register_callback(:halt) do |t|
|
344
|
+
stop
|
345
|
+
if t.first.key == :kill
|
346
|
+
Bud.shutdown_all_instances
|
347
|
+
Bud.stop_em_loop
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Pause an instance of Bud that is running asynchronously. That is, this
|
353
|
+
# method allows a Bud instance operating in run_bg or run_fg mode to be
|
354
|
+
# switched to "single-stepped" mode; timesteps can be manually invoked via
|
355
|
+
# tick(). To switch back to running Bud asynchronously, call run_bg().
|
356
|
+
public
|
357
|
+
def pause
|
358
|
+
schedule_and_wait do
|
359
|
+
@running_async = false
|
284
360
|
end
|
285
361
|
end
|
286
362
|
|
287
363
|
# Run Bud in the "foreground" -- the caller's thread will be used to run the
|
288
|
-
# Bud interpreter. This means this method won't return unless an error
|
289
|
-
#
|
364
|
+
# Bud interpreter. This means this method won't return unless an error occurs
|
365
|
+
# or Bud is halted. It is often more useful to run Bud in a different thread:
|
366
|
+
# see run_bg.
|
290
367
|
def run_fg
|
291
368
|
# If we're called from the EventMachine thread (and EM is running), blocking
|
292
369
|
# the current thread would imply deadlocking ourselves.
|
@@ -306,14 +383,15 @@ module Bud
|
|
306
383
|
run_bg
|
307
384
|
# Block caller's thread until Bud has shutdown
|
308
385
|
q.pop
|
386
|
+
report_metrics if options[:metrics]
|
309
387
|
end
|
310
388
|
|
311
|
-
# Shutdown a Bud instance that
|
312
|
-
# until Bud has been shutdown. If +stop_em+ is true, the
|
313
|
-
# loop is also shutdown; this will interfere with the
|
314
|
-
# Bud instances in the same process (as well as
|
315
|
-
# use EventMachine).
|
316
|
-
def
|
389
|
+
# Shutdown a Bud instance and release any resources that it was using. This
|
390
|
+
# method blocks until Bud has been shutdown. If +stop_em+ is true, the
|
391
|
+
# EventMachine event loop is also shutdown; this will interfere with the
|
392
|
+
# execution of any other Bud instances in the same process (as well as
|
393
|
+
# anything else that happens to use EventMachine).
|
394
|
+
def stop(stop_em=false, do_shutdown_cb=true)
|
317
395
|
schedule_and_wait do
|
318
396
|
do_shutdown(do_shutdown_cb)
|
319
397
|
end
|
@@ -322,15 +400,30 @@ module Bud
|
|
322
400
|
Bud.stop_em_loop
|
323
401
|
EventMachine::reactor_thread.join
|
324
402
|
end
|
403
|
+
report_metrics if options[:metrics]
|
325
404
|
end
|
405
|
+
alias :stop_bg :stop
|
326
406
|
|
327
407
|
# Register a callback that will be invoked when this instance of Bud is
|
328
408
|
# shutting down.
|
409
|
+
# XXX: The naming of this method (and cancel_shutdown_cb) is inconsistent
|
410
|
+
# with the naming of register_callback and friends.
|
329
411
|
def on_shutdown(&blk)
|
330
412
|
# Start EM if not yet started
|
331
413
|
start_reactor
|
414
|
+
rv = nil
|
415
|
+
schedule_and_wait do
|
416
|
+
rv = @shutdown_callback_id
|
417
|
+
@shutdown_callbacks[@shutdown_callback_id] = blk
|
418
|
+
@shutdown_callback_id += 1
|
419
|
+
end
|
420
|
+
return rv
|
421
|
+
end
|
422
|
+
|
423
|
+
def cancel_shutdown_cb(id)
|
332
424
|
schedule_and_wait do
|
333
|
-
@shutdown_callbacks
|
425
|
+
raise Bud::BudError unless @shutdown_callbacks.has_key? id
|
426
|
+
@shutdown_callbacks.delete(id)
|
334
427
|
end
|
335
428
|
end
|
336
429
|
|
@@ -345,11 +438,10 @@ module Bud
|
|
345
438
|
end
|
346
439
|
|
347
440
|
# Given a block, evaluate that block inside the background Ruby thread at some
|
348
|
-
# time in the future
|
349
|
-
# thread, the block can safely examine Bud
|
350
|
-
#
|
351
|
-
#
|
352
|
-
# non-blocking version, see async_do.
|
441
|
+
# time in the future, and then perform a Bloom tick. Because the block is
|
442
|
+
# evaluate inside the background Ruby thread, the block can safely examine Bud
|
443
|
+
# state. Note that calling sync_do blocks the caller until the block has been
|
444
|
+
# evaluated; for a non-blocking version, see async_do.
|
353
445
|
#
|
354
446
|
# Note that the block is invoked after one Bud timestep has ended but before
|
355
447
|
# the next timestep begins. Hence, synchronous accumulation (<=) into a Bud
|
@@ -361,7 +453,7 @@ module Bud
|
|
361
453
|
schedule_and_wait do
|
362
454
|
yield if block_given?
|
363
455
|
# Do another tick, in case the user-supplied block inserted any data
|
364
|
-
|
456
|
+
tick_internal
|
365
457
|
end
|
366
458
|
end
|
367
459
|
|
@@ -372,17 +464,7 @@ module Bud
|
|
372
464
|
EventMachine::schedule do
|
373
465
|
yield if block_given?
|
374
466
|
# Do another tick, in case the user-supplied block inserted any data
|
375
|
-
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
# Shutdown any persistent tables used by the current Bud instance. If you are
|
380
|
-
# running Bud via tick() and using +tctable+ collections, you should call this
|
381
|
-
# after you're finished using Bud. Programs that use Bud via run_fg() or
|
382
|
-
# run_bg() don't need to call this manually.
|
383
|
-
def close_tables
|
384
|
-
@tables.each_value do |t|
|
385
|
-
t.close
|
467
|
+
tick_internal
|
386
468
|
end
|
387
469
|
end
|
388
470
|
|
@@ -394,10 +476,9 @@ module Bud
|
|
394
476
|
# runtime is blocked while the callback is invoked, it can also examine any
|
395
477
|
# other Bud state freely.)
|
396
478
|
#
|
397
|
-
# Note that registering callbacks on persistent collections (e.g., tables
|
398
|
-
#
|
399
|
-
#
|
400
|
-
# tick.
|
479
|
+
# Note that registering callbacks on persistent collections (e.g., tables,
|
480
|
+
# syncs and stores) is probably not wise: as long as any tuples are stored in
|
481
|
+
# the collection, the callback will be invoked at the end of every tick.
|
401
482
|
def register_callback(tbl_name, &block)
|
402
483
|
# We allow callbacks to be added before or after EM has been started. To
|
403
484
|
# simplify matters, we start EM if it hasn't been started yet.
|
@@ -419,7 +500,7 @@ module Bud
|
|
419
500
|
# Unregister the callback that has the given ID.
|
420
501
|
def unregister_callback(id)
|
421
502
|
schedule_and_wait do
|
422
|
-
raise Bud::BudError unless @callbacks.has_key? id
|
503
|
+
raise Bud::BudError, "Missing callback: #{id.inspect}" unless @callbacks.has_key? id
|
423
504
|
@callbacks.delete(id)
|
424
505
|
end
|
425
506
|
end
|
@@ -433,6 +514,14 @@ module Bud
|
|
433
514
|
cb = register_callback(out_tbl) do |c|
|
434
515
|
q.push c.to_a
|
435
516
|
end
|
517
|
+
|
518
|
+
# If the runtime shuts down before we see anything in the output collection,
|
519
|
+
# make sure we hear about it so we can raise an error
|
520
|
+
# XXX: Using two separate callbacks here is ugly.
|
521
|
+
shutdown_cb = on_shutdown do
|
522
|
+
q.push :callback
|
523
|
+
end
|
524
|
+
|
436
525
|
unless in_tbl.nil?
|
437
526
|
sync_do {
|
438
527
|
t = @tables[in_tbl]
|
@@ -444,8 +533,13 @@ module Bud
|
|
444
533
|
}
|
445
534
|
end
|
446
535
|
result = q.pop
|
536
|
+
if result == :callback
|
537
|
+
# Don't try to unregister the callbacks first: runtime is already shutdown
|
538
|
+
raise BudShutdownWithCallbacksError, "Bud instance shutdown before sync_callback completed"
|
539
|
+
end
|
447
540
|
unregister_callback(cb)
|
448
|
-
|
541
|
+
cancel_shutdown_cb(shutdown_cb)
|
542
|
+
return (result == :callback) ? nil : result
|
449
543
|
end
|
450
544
|
|
451
545
|
# A common special case for sync_callback: block on a delta to a table.
|
@@ -469,8 +563,14 @@ module Bud
|
|
469
563
|
return if EventMachine::reactor_running?
|
470
564
|
|
471
565
|
EventMachine::error_handler do |e|
|
472
|
-
|
473
|
-
|
566
|
+
# Only print a backtrace if a non-BudError is raised (this presumably
|
567
|
+
# indicates an unexpected failure).
|
568
|
+
if e.class <= BudError
|
569
|
+
puts "#{e.class}: #{e}"
|
570
|
+
else
|
571
|
+
puts "Unexpected Bud error: #{e.inspect}"
|
572
|
+
puts e.backtrace.join("\n")
|
573
|
+
end
|
474
574
|
Bud.shutdown_all_instances
|
475
575
|
raise e
|
476
576
|
end
|
@@ -524,40 +624,22 @@ module Bud
|
|
524
624
|
@instance_id = ILLEGAL_INSTANCE_ID
|
525
625
|
}
|
526
626
|
|
627
|
+
unregister_callback(@halt_cb)
|
527
628
|
if do_shutdown_cb
|
528
|
-
@shutdown_callbacks.
|
629
|
+
@shutdown_callbacks.each_value {|cb| cb.call}
|
529
630
|
end
|
530
631
|
@timers.each {|t| t.cancel}
|
531
|
-
|
532
|
-
|
632
|
+
@tables.each_value {|t| t.close}
|
633
|
+
if EventMachine::reactor_running? and @bud_started
|
634
|
+
@dsock.close_connection
|
635
|
+
end
|
636
|
+
@bud_started = false
|
637
|
+
@running_async = false
|
533
638
|
if do_shutdown_cb
|
534
639
|
@post_shutdown_callbacks.each {|cb| cb.call}
|
535
640
|
end
|
536
641
|
end
|
537
642
|
|
538
|
-
private
|
539
|
-
def start_bud
|
540
|
-
raise BudError unless EventMachine::reactor_thread?
|
541
|
-
|
542
|
-
@instance_id = Bud.init_signal_handlers(self)
|
543
|
-
do_start_server
|
544
|
-
|
545
|
-
# Initialize periodics
|
546
|
-
@periodics.each do |p|
|
547
|
-
@timers << set_periodic_timer(p.pername, p.ident, p.period)
|
548
|
-
end
|
549
|
-
|
550
|
-
# Arrange for Bud to read from stdin if enabled. Note that we can't do this
|
551
|
-
# earlier because we need to wait for EventMachine startup.
|
552
|
-
@stdio.start_stdin_reader if @options[:stdin]
|
553
|
-
@zk_tables.each_value {|t| t.start_watchers}
|
554
|
-
|
555
|
-
# Compute a fixpoint; this will also invoke any bootstrap blocks.
|
556
|
-
tick unless @lazy
|
557
|
-
|
558
|
-
@rtracer.sleep if options[:rtrace]
|
559
|
-
end
|
560
|
-
|
561
643
|
def do_start_server
|
562
644
|
@dsock = EventMachine::open_datagram_socket(@ip, @options[:port],
|
563
645
|
BudServer, self)
|
@@ -574,12 +656,18 @@ module Bud
|
|
574
656
|
# forwarding, and external_ip:local_port would be if you're in a DMZ, for
|
575
657
|
# example.
|
576
658
|
def ip_port
|
577
|
-
raise BudError, "ip_port called before port defined" if
|
659
|
+
raise BudError, "ip_port called before port defined" if port.nil?
|
660
|
+
ip.to_s + ":" + port.to_s
|
661
|
+
end
|
578
662
|
|
663
|
+
def ip
|
579
664
|
ip = options[:ext_ip] ? "#{@options[:ext_ip]}" : "#{@ip}"
|
580
|
-
|
665
|
+
end
|
666
|
+
|
667
|
+
def port
|
668
|
+
return nil if @port.nil? and @options[:port] == 0 and not @options[:ext_port]
|
669
|
+
return options[:ext_port] ? "#{@options[:ext_port]}" :
|
581
670
|
(@port.nil? ? "#{@options[:port]}" : "#{@port}")
|
582
|
-
ip + ":" + port
|
583
671
|
end
|
584
672
|
|
585
673
|
# Returns the internal IP and port. See ip_port.
|
@@ -588,9 +676,20 @@ module Bud
|
|
588
676
|
@port.nil? ? "#{@ip}:#{@options[:port]}" : "#{@ip}:#{@port}"
|
589
677
|
end
|
590
678
|
|
591
|
-
#
|
679
|
+
# From client code, manually trigger a timestep of Bloom execution.
|
592
680
|
def tick
|
681
|
+
start(true)
|
682
|
+
end
|
683
|
+
|
684
|
+
# One timestep of Bloom execution. This MUST be invoked from the EventMachine
|
685
|
+
# thread; it is not intended to be called directly by client code.
|
686
|
+
def tick_internal
|
593
687
|
begin
|
688
|
+
starttime = Time.now if options[:metrics]
|
689
|
+
if options[:metrics] and not @endtime.nil?
|
690
|
+
@metrics[:betweentickstats] ||= initialize_stats
|
691
|
+
@metrics[:betweentickstats] = running_stats(@metrics[:betweentickstats], starttime - @endtime)
|
692
|
+
end
|
594
693
|
@inside_tick = true
|
595
694
|
@tables.each_value do |t|
|
596
695
|
t.tick
|
@@ -606,10 +705,17 @@ module Bud
|
|
606
705
|
do_flush
|
607
706
|
invoke_callbacks
|
608
707
|
@budtime += 1
|
708
|
+
@inbound.clear
|
609
709
|
ensure
|
610
710
|
@inside_tick = false
|
611
711
|
@tick_clock_time = nil
|
612
712
|
end
|
713
|
+
|
714
|
+
if options[:metrics]
|
715
|
+
@endtime = Time.now
|
716
|
+
@metrics[:tickstats] ||= initialize_stats
|
717
|
+
@metrics[:tickstats] = running_stats(@metrics[:tickstats], @endtime - starttime)
|
718
|
+
end
|
613
719
|
end
|
614
720
|
|
615
721
|
# Returns the wallclock time associated with the current Bud tick. That is,
|
@@ -629,9 +735,11 @@ module Bud
|
|
629
735
|
def builtin_state
|
630
736
|
loopback :localtick, [:col1]
|
631
737
|
@stdio = terminal :stdio
|
632
|
-
|
738
|
+
readonly :signals, [:key]
|
739
|
+
scratch :halt, [:key]
|
740
|
+
@periodics = table :periodics_tbl, [:pername] => [:period]
|
633
741
|
|
634
|
-
# for
|
742
|
+
# for Bud reflection
|
635
743
|
table :t_rules, [:rule_id] => [:lhs, :op, :src, :orig_src]
|
636
744
|
table :t_depends, [:rule_id, :lhs, :op, :body] => [:nm]
|
637
745
|
table :t_depends_tc, [:head, :body, :via, :neg, :temporal]
|
@@ -643,14 +751,13 @@ module Bud
|
|
643
751
|
table :t_table_schema, [:tab_name, :col_name, :ord, :loc]
|
644
752
|
end
|
645
753
|
|
646
|
-
# Handle any inbound tuples off the wire
|
647
|
-
#
|
754
|
+
# Handle any inbound tuples off the wire. Received messages are placed
|
755
|
+
# directly into the storage of the appropriate local channel. The inbound
|
756
|
+
# queue is cleared at the end of the tick.
|
648
757
|
def receive_inbound
|
649
758
|
@inbound.each do |msg|
|
650
|
-
# puts "dequeueing tuple #{msg[1].inspect} into #{msg[0]} @ #{ip_port}"
|
651
759
|
tables[msg[0].to_sym] << msg[1]
|
652
760
|
end
|
653
|
-
@inbound = []
|
654
761
|
end
|
655
762
|
|
656
763
|
# "Flush" any tuples that need to be flushed. This does two things:
|
@@ -696,13 +803,20 @@ module Bud
|
|
696
803
|
# stratum_first_iter field here distinguishes these cases.
|
697
804
|
@stratum_first_iter = true
|
698
805
|
begin
|
806
|
+
@this_stratum = strat_num
|
699
807
|
strat.each_with_index do |r,i|
|
808
|
+
@this_rule = i
|
700
809
|
fixpoint = false
|
810
|
+
rule_src = @rule_orig_src[strat_num][i] unless @rule_orig_src[strat_num].nil?
|
701
811
|
begin
|
812
|
+
if options[:metrics]
|
813
|
+
metrics[:rules] ||= {}
|
814
|
+
metrics[:rules][{:strat_num => strat_num, :rule_num => i, :rule_src => rule_src}] ||= 0
|
815
|
+
metrics[:rules][{:strat_num => strat_num, :rule_num => i, :rule_src => rule_src}] += 1
|
816
|
+
end
|
702
817
|
r.call
|
703
818
|
rescue Exception => e
|
704
819
|
# Don't report source text for certain rules (old-style rule blocks)
|
705
|
-
rule_src = @rule_orig_src[strat_num][i] unless @rule_orig_src[strat_num].nil?
|
706
820
|
src_msg = ""
|
707
821
|
unless rule_src == ""
|
708
822
|
src_msg = "\nRule: #{rule_src}"
|
@@ -741,10 +855,10 @@ module Bud
|
|
741
855
|
Time.new.to_i.to_s << rand.to_s
|
742
856
|
end
|
743
857
|
|
744
|
-
def
|
858
|
+
def make_periodic_timer(name, period)
|
745
859
|
EventMachine::PeriodicTimer.new(period) do
|
746
|
-
@
|
747
|
-
|
860
|
+
@inbound << [name, [gen_id, Time.now]]
|
861
|
+
tick_internal if @running_async
|
748
862
|
end
|
749
863
|
end
|
750
864
|
|
@@ -791,11 +905,15 @@ module Bud
|
|
791
905
|
$signal_lock.synchronize {
|
792
906
|
# If we setup signal handlers and then fork a new process, we want to
|
793
907
|
# reinitialize the signal handler in the child process.
|
794
|
-
unless b.options[:
|
908
|
+
unless b.options[:signal_handling] == :none or $signal_handler_setup
|
795
909
|
EventMachine::PeriodicTimer.new(SIGNAL_CHECK_PERIOD) do
|
796
910
|
if $got_shutdown_signal
|
797
|
-
|
798
|
-
|
911
|
+
if b.options[:signal_handling] == :bloom
|
912
|
+
Bud.tick_all_instances
|
913
|
+
else
|
914
|
+
Bud.shutdown_all_instances
|
915
|
+
Bud.stop_em_loop
|
916
|
+
end
|
799
917
|
$got_shutdown_signal = false
|
800
918
|
end
|
801
919
|
end
|
@@ -803,6 +921,7 @@ module Bud
|
|
803
921
|
["INT", "TERM"].each do |signal|
|
804
922
|
Signal.trap(signal) {
|
805
923
|
$got_shutdown_signal = true
|
924
|
+
b.sync_do{b.signals.pending_merge([[signal]])}
|
806
925
|
}
|
807
926
|
end
|
808
927
|
$setup_signal_handler_pid = true
|
@@ -814,12 +933,21 @@ module Bud
|
|
814
933
|
}
|
815
934
|
end
|
816
935
|
|
936
|
+
def self.tick_all_instances
|
937
|
+
instances = nil
|
938
|
+
$signal_lock.synchronize {
|
939
|
+
instances = $bud_instances.clone
|
940
|
+
}
|
941
|
+
|
942
|
+
instances.each_value {|b| b.sync_do }
|
943
|
+
end
|
944
|
+
|
817
945
|
def self.shutdown_all_instances(do_shutdown_cb=true)
|
818
946
|
instances = nil
|
819
947
|
$signal_lock.synchronize {
|
820
948
|
instances = $bud_instances.clone
|
821
949
|
}
|
822
950
|
|
823
|
-
instances.each_value {|b| b.
|
951
|
+
instances.each_value {|b| b.stop(false, do_shutdown_cb) }
|
824
952
|
end
|
825
953
|
end
|