bud 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|