bud 0.0.8 → 0.1.0.pre1
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 +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
|