bud 0.0.8 → 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -10
- data/bin/budplot +1 -2
- data/docs/cheat.md +2 -15
- data/examples/basics/paths.rb +7 -7
- data/lib/bud/aggs.rb +15 -19
- data/lib/bud/bud_meta.rb +165 -77
- data/lib/bud/bust/bust.rb +11 -4
- data/lib/bud/collections.rb +643 -280
- data/lib/bud/depanalysis.rb +50 -25
- data/lib/bud/executor/elements.rb +592 -0
- data/lib/bud/executor/group.rb +104 -0
- data/lib/bud/executor/join.rb +638 -0
- data/lib/bud/graphs.rb +12 -11
- data/lib/bud/joins.rb +2 -1
- data/lib/bud/meta_algebra.rb +5 -4
- data/lib/bud/metrics.rb +9 -3
- data/lib/bud/monkeypatch.rb +131 -23
- data/lib/bud/rebl.rb +41 -28
- data/lib/bud/rewrite.rb +112 -440
- data/lib/bud/server.rb +3 -2
- data/lib/bud/source.rb +109 -0
- data/lib/bud/state.rb +16 -9
- data/lib/bud/storage/dbm.rb +62 -16
- data/lib/bud/storage/zookeeper.rb +2 -2
- data/lib/bud/viz.rb +8 -4
- data/lib/bud/viz_util.rb +10 -9
- data/lib/bud.rb +413 -199
- metadata +40 -55
- data/examples/deploy/tokenring-ec2.rb +0 -26
- data/examples/deploy/tokenring-fork.rb +0 -15
- data/examples/deploy/tokenring-thread.rb +0 -15
- data/examples/deploy/tokenring.rb +0 -47
- data/lib/bud/deploy/deployer.rb +0 -67
- data/lib/bud/deploy/ec2deploy.rb +0 -199
- data/lib/bud/deploy/forkdeploy.rb +0 -90
- data/lib/bud/deploy/threaddeploy.rb +0 -38
- data/lib/bud/storage/tokyocabinet.rb +0 -190
- data/lib/bud/stratify.rb +0 -85
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'bud/deploy/deployer'
|
2
|
-
|
3
|
-
module ForkDeployProtocol
|
4
|
-
state do
|
5
|
-
channel :child_ack, [:@loc, :node_id] => [:node_addr]
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
module ForkDeployChild
|
10
|
-
include ForkDeployProtocol
|
11
|
-
|
12
|
-
bootstrap do
|
13
|
-
child_ack <~ [[@deployer_addr, @node_id, ip_port]]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# An implementation of the Deployer that runs instances using forked local
|
18
|
-
# processes (listening on an ephemeral port).
|
19
|
-
#
|
20
|
-
# Note that this module is included in both the deployer process and in the
|
21
|
-
# deployed instances. To write code that only runs in one type of process,
|
22
|
-
# consult the ":deploy" Bud option (which is false in deployed children).
|
23
|
-
module ForkDeploy
|
24
|
-
include Deployer
|
25
|
-
include ForkDeployProtocol
|
26
|
-
|
27
|
-
def initialize(opts={})
|
28
|
-
super
|
29
|
-
@child_modules = [ForkDeployChild]
|
30
|
-
@child_pids = []
|
31
|
-
@dead_pids = []
|
32
|
-
end
|
33
|
-
|
34
|
-
bloom :child_info do
|
35
|
-
node <= child_ack {|a| [a.node_id, a.node_addr]}
|
36
|
-
node_ready <= child_ack {|a| [a.node_id]}
|
37
|
-
end
|
38
|
-
|
39
|
-
bootstrap do
|
40
|
-
return unless @options[:deploy]
|
41
|
-
|
42
|
-
Signal.trap("CHLD") do
|
43
|
-
# We receive SIGCHLD when a child process changes state; unfortunately,
|
44
|
-
# there's no easy way to tell whether the child process we're getting the
|
45
|
-
# signal for is one of ForkDeploy's children. Hence, check if any of the
|
46
|
-
# forked children have exited. We also ignore Errno::ECHILD, because
|
47
|
-
# someone else's waitpid() could easily race with us.
|
48
|
-
@child_pids.each do |c|
|
49
|
-
begin
|
50
|
-
pid = Process.waitpid(c, Process::WNOHANG)
|
51
|
-
unless pid.nil?
|
52
|
-
@dead_pids << pid
|
53
|
-
end
|
54
|
-
rescue Errno::ECHILD
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
on_shutdown do
|
60
|
-
# NB: Setting the SIGCHLD handler to "IGNORE" results in waitpid() being
|
61
|
-
# called automatically (to cleanup zombies), at least on OSX. This is not
|
62
|
-
# what we want, since it would cause a subsequent waitpid() to fail.
|
63
|
-
Signal.trap("CHLD", "DEFAULT")
|
64
|
-
pids = @child_pids - @dead_pids
|
65
|
-
pids.each do |p|
|
66
|
-
begin
|
67
|
-
Process.kill("TERM", p)
|
68
|
-
Process.waitpid(p)
|
69
|
-
rescue Errno::ESRCH
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
child_opts = @options[:deploy_child_opts]
|
75
|
-
child_opts ||= {}
|
76
|
-
deployer_addr = self.ip_port
|
77
|
-
node_count[[]].num.times do |i|
|
78
|
-
@child_pids << Bud.do_fork do
|
79
|
-
@child_modules.each do |m|
|
80
|
-
# XXX: Can this be done without "instance_eval"?
|
81
|
-
self.class.instance_eval "include #{m}"
|
82
|
-
end
|
83
|
-
child = self.class.new(child_opts)
|
84
|
-
child.instance_variable_set('@deployer_addr', deployer_addr)
|
85
|
-
child.instance_variable_set('@node_id', i)
|
86
|
-
child.run_fg
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'bud/deploy/deployer'
|
2
|
-
|
3
|
-
# An implementation of the Deployer that runs instances using the current Ruby
|
4
|
-
# process (listening on an ephemeral port). ThreadDeploy is probably not the
|
5
|
-
# best name: all the spawned instances are run by a single thread, they are just
|
6
|
-
# multiplexed via EventMachine.
|
7
|
-
#
|
8
|
-
# Note that this module is included in both the deployer process and in the
|
9
|
-
# deployed instances. To write code that only runs in one type of process,
|
10
|
-
# consult the ":deploy" Bud option (which is false in deployed children).
|
11
|
-
module ThreadDeploy
|
12
|
-
include Deployer
|
13
|
-
|
14
|
-
bootstrap do
|
15
|
-
return unless @options[:deploy]
|
16
|
-
|
17
|
-
@instances = []
|
18
|
-
on_shutdown do
|
19
|
-
@instances.each {|b| b.stop}
|
20
|
-
end
|
21
|
-
|
22
|
-
print "Spawning threads"
|
23
|
-
child_opts = @options[:deploy_child_opts]
|
24
|
-
child_opts ||= {}
|
25
|
-
deployer_addr = self.ip_port
|
26
|
-
node_count[[]].num.times do |i|
|
27
|
-
b = self.class.new(child_opts)
|
28
|
-
b.instance_variable_set('@deployer_addr', deployer_addr)
|
29
|
-
b.instance_variable_set('@node_id', i)
|
30
|
-
b.run_bg
|
31
|
-
@instances << b
|
32
|
-
node << [i, b.ip_port]
|
33
|
-
node_ready << [i]
|
34
|
-
print "."
|
35
|
-
end
|
36
|
-
puts "done"
|
37
|
-
end
|
38
|
-
end
|
@@ -1,190 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'tokyocabinet'
|
3
|
-
Bud::HAVE_TOKYOCABINET = true
|
4
|
-
rescue LoadError
|
5
|
-
end
|
6
|
-
|
7
|
-
module Bud
|
8
|
-
# Persistent table implementation based on TokyoCabinet.
|
9
|
-
class BudTcTable < BudCollection # :nodoc: all
|
10
|
-
def initialize(name, bud_instance, given_schema)
|
11
|
-
tc_dir = bud_instance.options[:tc_dir]
|
12
|
-
raise Bud::Error, "TC support must be enabled via 'tc_dir'" unless tc_dir
|
13
|
-
if bud_instance.port.nil?
|
14
|
-
raise Bud::Error, "use of dbm storage requires an explicit port to be specified in Bud initialization options"
|
15
|
-
end
|
16
|
-
|
17
|
-
unless File.exists?(tc_dir)
|
18
|
-
Dir.mkdir(tc_dir)
|
19
|
-
puts "Created directory: #{tc_dir}" unless bud_instance.options[:quiet]
|
20
|
-
end
|
21
|
-
|
22
|
-
dirname = "#{tc_dir}/bud_#{bud_instance.port}"
|
23
|
-
unless File.exists?(dirname)
|
24
|
-
Dir.mkdir(dirname)
|
25
|
-
puts "Created directory: #{dirname}" unless bud_instance.options[:quiet]
|
26
|
-
end
|
27
|
-
|
28
|
-
super(name, bud_instance, given_schema)
|
29
|
-
@to_delete = []
|
30
|
-
|
31
|
-
@hdb = TokyoCabinet::HDB.new
|
32
|
-
db_fname = "#{dirname}/#{name}.tch"
|
33
|
-
flags = TokyoCabinet::HDB::OWRITER | TokyoCabinet::HDB::OCREAT
|
34
|
-
if bud_instance.options[:tc_truncate] == true
|
35
|
-
flags |= TokyoCabinet::HDB::OTRUNC
|
36
|
-
end
|
37
|
-
if !@hdb.open(db_fname, flags)
|
38
|
-
raise Bud::Error, "failed to open TokyoCabinet DB '#{db_fname}': #{@hdb.errmsg}"
|
39
|
-
end
|
40
|
-
@hdb.tranbegin
|
41
|
-
end
|
42
|
-
|
43
|
-
def init_storage
|
44
|
-
# XXX: we can't easily use the @storage infrastructure provided by
|
45
|
-
# BudCollection; issue #33
|
46
|
-
@storage = nil
|
47
|
-
end
|
48
|
-
|
49
|
-
def [](key)
|
50
|
-
check_enumerable(key)
|
51
|
-
key_s = MessagePack.pack(key)
|
52
|
-
val_s = @hdb[key_s]
|
53
|
-
if val_s
|
54
|
-
return make_tuple(key, MessagePack.unpack(val_s))
|
55
|
-
else
|
56
|
-
return @delta[key]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def has_key?(k)
|
61
|
-
check_enumerable(k)
|
62
|
-
key_s = MessagePack.pack(k)
|
63
|
-
return true if @hdb.has_key? key_s
|
64
|
-
return @delta.has_key? k
|
65
|
-
end
|
66
|
-
|
67
|
-
def include?(tuple)
|
68
|
-
key = get_key_vals(tuple)
|
69
|
-
value = self[key]
|
70
|
-
return (value == tuple)
|
71
|
-
end
|
72
|
-
|
73
|
-
def make_tuple(k_ary, v_ary)
|
74
|
-
t = Array.new(k_ary.length + v_ary.length)
|
75
|
-
@key_colnums.each_with_index do |k,i|
|
76
|
-
t[k] = k_ary[i]
|
77
|
-
end
|
78
|
-
val_cols.each_with_index do |c,i|
|
79
|
-
t[cols.index(c)] = v_ary[i]
|
80
|
-
end
|
81
|
-
tuple_accessors(t)
|
82
|
-
end
|
83
|
-
|
84
|
-
def each(&block)
|
85
|
-
each_from([@delta], &block)
|
86
|
-
each_storage(&block)
|
87
|
-
end
|
88
|
-
|
89
|
-
def each_from(bufs, &block)
|
90
|
-
bufs.each do |b|
|
91
|
-
if b == @storage then
|
92
|
-
each_storage(&block)
|
93
|
-
else
|
94
|
-
b.each_value do |v|
|
95
|
-
tick_metrics if bud_instance.options[:metrics]
|
96
|
-
yield v
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def each_storage(&block)
|
103
|
-
@hdb.each do |k,v|
|
104
|
-
k_ary = MessagePack.unpack(k)
|
105
|
-
v_ary = MessagePack.unpack(v)
|
106
|
-
tick_metrics if bud_instance.options[:metrics]
|
107
|
-
yield make_tuple(k_ary, v_ary)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def flush
|
112
|
-
@hdb.trancommit
|
113
|
-
end
|
114
|
-
|
115
|
-
def close
|
116
|
-
@hdb.close
|
117
|
-
end
|
118
|
-
|
119
|
-
def merge_to_hdb(buf)
|
120
|
-
buf.each do |key,tuple|
|
121
|
-
merge_tuple_to_hdb(key, tuple)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def merge_tuple_to_hdb(key, tuple)
|
126
|
-
val = val_cols.map{|c| tuple[cols.index(c)]}
|
127
|
-
key_s = MessagePack.pack(key)
|
128
|
-
val_s = MessagePack.pack(val)
|
129
|
-
if @hdb.putkeep(key_s, val_s) == false
|
130
|
-
old_tuple = self[key]
|
131
|
-
raise_pk_error(tuple, old_tuple) if tuple != old_tuple
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
# move deltas to TC, and new_deltas to deltas
|
136
|
-
def tick_deltas
|
137
|
-
merge_to_hdb(@delta)
|
138
|
-
@delta = @new_delta
|
139
|
-
@new_delta = {}
|
140
|
-
end
|
141
|
-
|
142
|
-
superator "<-" do |o|
|
143
|
-
o.each do |tuple|
|
144
|
-
@to_delete << tuple unless tuple.nil?
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def insert(tuple)
|
149
|
-
tuple = prep_tuple(tuple)
|
150
|
-
key = get_key_vals(tuple)
|
151
|
-
merge_tuple_to_hdb(key, tuple)
|
152
|
-
end
|
153
|
-
|
154
|
-
alias << insert
|
155
|
-
|
156
|
-
# Remove to_delete and then add pending to HDB
|
157
|
-
def tick
|
158
|
-
raise Bud::Error, "orphaned tuples in @delta for #{@tabname}" unless @delta.empty?
|
159
|
-
raise Bud::Error, "orphaned tuples in @new_delta for #{@tabname}" unless @new_delta.empty?
|
160
|
-
|
161
|
-
@to_delete.each do |tuple|
|
162
|
-
k = get_key_vals(tuple)
|
163
|
-
k_str = MessagePack.pack(k)
|
164
|
-
cols_str = @hdb[k_str]
|
165
|
-
unless cols_str.nil?
|
166
|
-
hdb_cols = MessagePack.unpack(cols_str)
|
167
|
-
delete_cols = val_cols.map{|c| tuple[cols.index(c)]}
|
168
|
-
if hdb_cols == delete_cols
|
169
|
-
@hdb.delete k_str
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
@to_delete = []
|
174
|
-
|
175
|
-
merge_to_hdb(@pending)
|
176
|
-
@pending = {}
|
177
|
-
|
178
|
-
@hdb.trancommit
|
179
|
-
@hdb.tranbegin
|
180
|
-
end
|
181
|
-
|
182
|
-
def length
|
183
|
-
@hdb.length
|
184
|
-
end
|
185
|
-
|
186
|
-
def empty?
|
187
|
-
@hdb.empty?
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
data/lib/bud/stratify.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bud'
|
3
|
-
|
4
|
-
class Stratification # :nodoc: all
|
5
|
-
include Bud
|
6
|
-
|
7
|
-
state do
|
8
|
-
# Data inserted by client (Bud rewrite code)
|
9
|
-
table :depends, [:rule, :head, :op, :body, :neg]
|
10
|
-
|
11
|
-
# Intermediate state
|
12
|
-
scratch :depends_clean, [:head, :body, :neg, :temporal]
|
13
|
-
scratch :depends_tc, [:head, :body, :via, :neg, :temporal]
|
14
|
-
scratch :stratum_base, [:predicate, :stratum]
|
15
|
-
|
16
|
-
# Output state
|
17
|
-
scratch :cycle, [:predicate, :via, :neg, :temporal]
|
18
|
-
scratch :stratum, [:predicate, :stratum]
|
19
|
-
scratch :top_strat, [:stratum]
|
20
|
-
end
|
21
|
-
|
22
|
-
def declaration
|
23
|
-
strata[0] = lambda {
|
24
|
-
depends_clean <= depends do |d|
|
25
|
-
is_temporal = (d.op.to_s =~ /<[\+\-\~]/)
|
26
|
-
[d.head, d.body, d.neg, is_temporal]
|
27
|
-
end
|
28
|
-
|
29
|
-
# Compute the transitive closure of "depends_clean" to detect cycles in
|
30
|
-
# the deductive fragment of the program.
|
31
|
-
depends_tc <= depends_clean do |d|
|
32
|
-
[d.head, d.body, d.body, d.neg, d.temporal]
|
33
|
-
end
|
34
|
-
depends_tc <= (depends_clean * depends_tc).pairs(:body => :head) do |b, r|
|
35
|
-
[b.head, r.body, b.body, (b.neg or r.neg), (b.temporal or r.temporal)]
|
36
|
-
end
|
37
|
-
|
38
|
-
cycle <= depends_tc do |d|
|
39
|
-
if d.head == d.body
|
40
|
-
if d.neg and !d.temporal
|
41
|
-
raise Bud::CompileError, "unstratifiable program: #{d.inspect}"
|
42
|
-
else
|
43
|
-
[d.head, d.via, d.neg, d.temporal]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# we initially assign all predicates to stratum 0
|
49
|
-
stratum_base <= depends {|d| [d.body, 0]}
|
50
|
-
}
|
51
|
-
|
52
|
-
strata[1] = lambda {
|
53
|
-
# classic stratification:
|
54
|
-
# if A depends on B, A is >= B.
|
55
|
-
# if A depends nonmonotonically on B, A > B.
|
56
|
-
# if A are B are nonmonotonically co-dependent, give up.
|
57
|
-
# (don't need to do this, b/c we've ruled out deductive cycles)
|
58
|
-
#
|
59
|
-
# Stratum choice controls local evaluation order, so we need only consider
|
60
|
-
# deductive rules (<=). Temporal rules are placed in an extra "top"
|
61
|
-
# stratum afterward.
|
62
|
-
stratum_base <= (depends * stratum_base).pairs(:body => :predicate) do |d, s|
|
63
|
-
if d.op.to_s == '<='
|
64
|
-
if d.neg
|
65
|
-
# BUMP
|
66
|
-
[d.head, s.stratum + 1]
|
67
|
-
else
|
68
|
-
# HOIST
|
69
|
-
[d.head, s.stratum]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
}
|
74
|
-
|
75
|
-
strata[2] = lambda {
|
76
|
-
stratum <= stratum_base.group([stratum_base.predicate], max(stratum_base.stratum))
|
77
|
-
}
|
78
|
-
|
79
|
-
strata[3] = lambda {
|
80
|
-
# there is no good reason that top_strat can't be computed in strata[2] over stratum_base.
|
81
|
-
# however, when it is deduced that way, it is empty after a tick
|
82
|
-
top_strat <= stratum.group([], max(stratum.stratum))
|
83
|
-
}
|
84
|
-
end
|
85
|
-
end
|