bud 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +9 -0
- data/README +30 -0
- data/bin/budplot +134 -0
- data/bin/budvis +201 -0
- data/bin/rebl +4 -0
- data/docs/README.md +13 -0
- data/docs/bfs.md +379 -0
- data/docs/bfs.raw +251 -0
- data/docs/bfs_arch.png +0 -0
- data/docs/bloom-loop.png +0 -0
- data/docs/bust.md +83 -0
- data/docs/cheat.md +291 -0
- data/docs/deploy.md +96 -0
- data/docs/diffs +181 -0
- data/docs/getstarted.md +296 -0
- data/docs/intro.md +36 -0
- data/docs/modules.md +112 -0
- data/docs/operational.md +96 -0
- data/docs/rebl.md +99 -0
- data/docs/ruby_hooks.md +19 -0
- data/docs/visualizations.md +75 -0
- data/examples/README +1 -0
- data/examples/basics/hello.rb +12 -0
- data/examples/basics/out +1103 -0
- data/examples/basics/out.new +856 -0
- data/examples/basics/paths.rb +51 -0
- data/examples/bust/README.md +9 -0
- data/examples/bust/bustclient-example.rb +23 -0
- data/examples/bust/bustinspector.html +135 -0
- data/examples/bust/bustserver-example.rb +18 -0
- data/examples/chat/README.md +9 -0
- data/examples/chat/chat.rb +45 -0
- data/examples/chat/chat_protocol.rb +8 -0
- data/examples/chat/chat_server.rb +29 -0
- data/examples/deploy/tokenring-ec2.rb +26 -0
- data/examples/deploy/tokenring-local.rb +17 -0
- data/examples/deploy/tokenring.rb +39 -0
- data/lib/bud/aggs.rb +126 -0
- data/lib/bud/bud_meta.rb +185 -0
- data/lib/bud/bust/bust.rb +126 -0
- data/lib/bud/bust/client/idempotence.rb +10 -0
- data/lib/bud/bust/client/restclient.rb +49 -0
- data/lib/bud/collections.rb +937 -0
- data/lib/bud/depanalysis.rb +44 -0
- data/lib/bud/deploy/countatomicdelivery.rb +50 -0
- data/lib/bud/deploy/deployer.rb +67 -0
- data/lib/bud/deploy/ec2deploy.rb +200 -0
- data/lib/bud/deploy/localdeploy.rb +41 -0
- data/lib/bud/errors.rb +15 -0
- data/lib/bud/graphs.rb +405 -0
- data/lib/bud/joins.rb +300 -0
- data/lib/bud/rebl.rb +314 -0
- data/lib/bud/rewrite.rb +523 -0
- data/lib/bud/rtrace.rb +27 -0
- data/lib/bud/server.rb +43 -0
- data/lib/bud/state.rb +108 -0
- data/lib/bud/storage/tokyocabinet.rb +170 -0
- data/lib/bud/storage/zookeeper.rb +178 -0
- data/lib/bud/stratify.rb +83 -0
- data/lib/bud/viz.rb +65 -0
- data/lib/bud.rb +797 -0
- metadata +330 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
|
4
|
+
class DepAnalysis #:nodoc: all
|
5
|
+
include Bud
|
6
|
+
|
7
|
+
state do
|
8
|
+
table :providing, [:pred, :input]
|
9
|
+
table :depends_tc, [:head, :body, :via, :neg, :temporal]
|
10
|
+
table :underspecified, [:pred, :input]
|
11
|
+
|
12
|
+
table :source, [:pred]
|
13
|
+
table :sink, [:pred]
|
14
|
+
end
|
15
|
+
|
16
|
+
def declaration
|
17
|
+
strata[0] = lambda {
|
18
|
+
source <= providing do |p|
|
19
|
+
if p.input and !depends_tc.map{|d| d.head}.include? p.pred
|
20
|
+
[p.pred]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sink <= providing do |p|
|
25
|
+
if !p.input and !depends_tc.map{|d| d.body}.include? p.pred
|
26
|
+
[p.pred]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
underspecified <= providing do |p|
|
31
|
+
if p.input
|
32
|
+
unless depends_tc.map{|d| d.body if d.head != d.body}.include? p.pred
|
33
|
+
[p.pred, true]
|
34
|
+
end
|
35
|
+
else
|
36
|
+
unless depends_tc.map{|d| d.head if d.head != d.body}.include? p.pred
|
37
|
+
[p.pred, false]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
|
4
|
+
# XXX: delivery modules are a massive hack. we need to think about
|
5
|
+
# aspect-oriented programming here, or allow users to extend the definitions of
|
6
|
+
# existing table types
|
7
|
+
module CountAtomicDelivery # :nodoc: all
|
8
|
+
state do
|
9
|
+
scratch :atomic_data_in, [:loc, :tuple]
|
10
|
+
channel :atomic_data_chan, [:@loc, :tuple]
|
11
|
+
table :atomic_data_recv, [:loc, :tuple]
|
12
|
+
|
13
|
+
scratch :atomic_count, [:loc, :cnt]
|
14
|
+
channel :atomic_count_chan,[:@loc, :cnt]
|
15
|
+
table :atomic_count_recv, [] => [:cnt]
|
16
|
+
|
17
|
+
scratch :atomic_recv_count, [:loc] => [:cnt]
|
18
|
+
scratch :atomic_data_atomic, [:loc, :tuple]
|
19
|
+
scratch :atomic_data_out, [:tuple]
|
20
|
+
end
|
21
|
+
|
22
|
+
bloom :countatomicdelivery do
|
23
|
+
atomic_count <= atomic_data_in.group([:loc], count)
|
24
|
+
|
25
|
+
atomic_data_chan <~ atomic_data_in
|
26
|
+
atomic_count_chan <~ atomic_count
|
27
|
+
|
28
|
+
atomic_count_recv <= atomic_count_chan.map {|c| [c.cnt]}
|
29
|
+
atomic_data_recv <= atomic_data_chan
|
30
|
+
|
31
|
+
atomic_recv_count <= atomic_data_recv.group([:loc], count)
|
32
|
+
|
33
|
+
# atomic_data_atomic <= join([atomic_recv_count, atomic_count_recv,
|
34
|
+
# atomic_data_recv],
|
35
|
+
# [atomic_recv_count.cnt,
|
36
|
+
# atomic_count_recv.cnt]).map{ |rc, cr, d| d }
|
37
|
+
|
38
|
+
atomic_data_atomic <= ((if atomic_recv_count.first and
|
39
|
+
atomic_count_recv.first and
|
40
|
+
atomic_recv_count.first.cnt ==
|
41
|
+
atomic_count_recv.first.cnt
|
42
|
+
atomic_data_recv
|
43
|
+
end) or [])
|
44
|
+
|
45
|
+
# Commented out due to bug #85.
|
46
|
+
# atomic_data_recv <- atomic_data_atomic
|
47
|
+
# Idempotence hack inserted due to bug #85.
|
48
|
+
atomic_data_out <= atomic_data_atomic.map {|a| [a.tuple] if depl_idempotent [[:atomic_data_out, a]]}
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'bud/deploy/countatomicdelivery'
|
5
|
+
|
6
|
+
class Module
|
7
|
+
def deploystrap(&block)
|
8
|
+
meth_name = "__deploystrap__#{Module.get_class_name(self)}".to_sym
|
9
|
+
define_method(meth_name, &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Deployer # :nodoc: all
|
14
|
+
include CountAtomicDelivery
|
15
|
+
|
16
|
+
state do
|
17
|
+
table :node, [:uid] => [:node]
|
18
|
+
table :node_count, [] => [:num]
|
19
|
+
table :initial_data, [:uid, :pred, :data]
|
20
|
+
channel :dont_care, [:@loc]
|
21
|
+
table :dead, [:dead]
|
22
|
+
end
|
23
|
+
|
24
|
+
def depl_idempotent(r) (dead.include? r) ? false : dead.insert(r) end
|
25
|
+
|
26
|
+
def do_deploystrap
|
27
|
+
self.class.ancestors.each do |anc|
|
28
|
+
anc.instance_methods(false).each do |m|
|
29
|
+
if /^__deploystrap__/.match m
|
30
|
+
self.method(m.to_sym).call
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(opt = {})
|
37
|
+
super
|
38
|
+
if opt[:deploy]
|
39
|
+
do_deploystrap
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Distribute the EDB to each node.
|
44
|
+
#
|
45
|
+
# XXX: this may break coordination protocols that assume the EDB is present
|
46
|
+
# before any messages are received. In order to fix this, we would probably
|
47
|
+
# need to globally synchronize to ensure that "timestamp 0" gets "fully
|
48
|
+
# evaluated" before any messages can be sent.
|
49
|
+
|
50
|
+
bloom :distribute_data do
|
51
|
+
atomic_data_in <= join([node, initial_data],
|
52
|
+
[node.uid, initial_data.uid]).map do |n, i|
|
53
|
+
[n.node, [i.pred, i.data]] if depl_idempotent [[n.node, i.pred, i.data]]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add all tuples at once.
|
57
|
+
dont_care <~ atomic_data_out.map do |a|
|
58
|
+
if depl_idempotent a
|
59
|
+
a.tuple[1].map do |d|
|
60
|
+
eval a.tuple[0].to_s + " <+ [" + d.inspect + "]"
|
61
|
+
end
|
62
|
+
[ip_port]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'AWS'
|
3
|
+
require 'pp'
|
4
|
+
require 'net/ssh'
|
5
|
+
require 'net/scp'
|
6
|
+
require 'bud'
|
7
|
+
require 'bud/deploy/deployer'
|
8
|
+
|
9
|
+
# logic to deploy Bloom programs on EC2
|
10
|
+
module EC2Deploy
|
11
|
+
include Deployer
|
12
|
+
|
13
|
+
state do
|
14
|
+
table :access_key_id, [] => [:key]
|
15
|
+
table :secret_access_key, [] => [:key]
|
16
|
+
table :image_id, [] => [:img]
|
17
|
+
table :key_name, [] => [:name]
|
18
|
+
table :ec2_key_location, [] => [:loc]
|
19
|
+
table :ec2_conn, [] => [:conn]
|
20
|
+
table :ec2_insts, [] => [:insts]
|
21
|
+
table :reservation_id, [] => [:rid]
|
22
|
+
periodic :spinup_timer, 6
|
23
|
+
scratch :the_reservation, [] => [:reservation]
|
24
|
+
scratch :the_reservation_next, [] => [:reservation]
|
25
|
+
scratch :node_up, [:node] => [:bool]
|
26
|
+
table :init_dir, [] => [:dir]
|
27
|
+
table :temp_node, [:uid, :node, :localip]
|
28
|
+
table :all_up, [:bool]
|
29
|
+
table :ruby_command, [] => [:cmd]
|
30
|
+
table :deploy_node, [:uid] => [:node]
|
31
|
+
channel :ready, [:@loc, :sender]
|
32
|
+
table :ready_tab, [:sender]
|
33
|
+
scratch :ready_count, [:num]
|
34
|
+
end
|
35
|
+
|
36
|
+
deploystrap do
|
37
|
+
# Write the IP & port to a file; we'll send this to each EC2 node.
|
38
|
+
File.open("deploy_ip_port", "w") do |f|
|
39
|
+
f.puts ip_port
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
bootstrap do
|
44
|
+
# The official BUD AMI.
|
45
|
+
image_id <= [["ami-f434c99d"]]
|
46
|
+
unless @options[:deploy]
|
47
|
+
# Send message to the deployer telling 'em we's up.
|
48
|
+
File.open("deploy_ip_port", "r") do |f|
|
49
|
+
ready <~ [[f.readline.chop, ip_port]]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
bloom :spinup do
|
55
|
+
ec2_conn <= join([access_key_id, secret_access_key]).map do
|
56
|
+
if depl_idempotent [:ec2_comm]
|
57
|
+
[AWS::EC2::Base.new(:access_key_id => access_key_id[[]].key,
|
58
|
+
:secret_access_key => secret_access_key[[]].key)]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
ec2_insts <= join([image_id, node_count, key_name, ec2_conn]).map do
|
63
|
+
if depl_idempotent [:ec2_insts]
|
64
|
+
print "Starting up EC2 instances"
|
65
|
+
STDOUT.flush
|
66
|
+
# First, we create the security group.
|
67
|
+
begin
|
68
|
+
ec2_conn[[]].conn.create_security_group(:group_name => "bud", :group_description => "bud")
|
69
|
+
rescue AWS::InvalidGroupDuplicate
|
70
|
+
# Group already exists; ok, maybe we created it previously.
|
71
|
+
else
|
72
|
+
# Add SSH permission.
|
73
|
+
ec2_conn[[]].conn.authorize_security_group_ingress( :group_name => "bud",
|
74
|
+
:ip_protocol => "tcp",
|
75
|
+
:from_port => 22,
|
76
|
+
:to_port => 22,
|
77
|
+
:cidr_ip => "0.0.0.0/0" )
|
78
|
+
# Add unlimited UDP permission from any node not in the security group.
|
79
|
+
# XXX: make this more restrictive?
|
80
|
+
ec2_conn[[]].conn.authorize_security_group_ingress( :group_name => "bud",
|
81
|
+
:ip_protocol => "udp",
|
82
|
+
:from_port => 0,
|
83
|
+
:to_port => 65535,
|
84
|
+
:cidr_ip => "0.0.0.0/0" )
|
85
|
+
end
|
86
|
+
|
87
|
+
# Finally, start up the instances.
|
88
|
+
[ec2_conn[[]].conn.run_instances(:image_id => image_id[[]].img,
|
89
|
+
:min_count => node_count[[]].num,
|
90
|
+
:max_count => node_count[[]].num,
|
91
|
+
:key_name => key_name[[]].name,
|
92
|
+
:security_group => "bud")]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
the_reservation <= join([spinup_timer, ec2_conn, ec2_insts]).map do |t,c,i|
|
97
|
+
if depl_idempotent [[:the_reservation, t.val]] and not all_up.include? [true]
|
98
|
+
to_ret = nil
|
99
|
+
begin
|
100
|
+
to_ret = [ec2_conn[[]].conn.describe_instances()["reservationSet"]["item"].find do |i|
|
101
|
+
i["reservationId"] == ec2_insts[[]].insts["reservationId"]
|
102
|
+
end]
|
103
|
+
rescue SocketError
|
104
|
+
print "E"
|
105
|
+
else
|
106
|
+
print "."
|
107
|
+
end
|
108
|
+
STDOUT.flush
|
109
|
+
to_ret
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# XXX: No upsert operator, so we have to do this.
|
114
|
+
the_reservation_next <+ the_reservation
|
115
|
+
|
116
|
+
node_up <= ((join([ec2_insts, the_reservation]).map do
|
117
|
+
if not all_up.include? [true]
|
118
|
+
the_reservation[[]].reservation["instancesSet"]["item"].map do |i|
|
119
|
+
[i, i["instanceState"]["code"] == "16"]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end)[0] or [])
|
123
|
+
|
124
|
+
|
125
|
+
all_up <+ node_up.map do
|
126
|
+
if node_up.find {|n| n.bool == false} == nil and node_up.find {|n| n.bool == true} != nil
|
127
|
+
if depl_idempotent [:nodes_all_up]
|
128
|
+
puts "done"
|
129
|
+
STDOUT.flush
|
130
|
+
[true]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# XXX: Fixed port 54321
|
136
|
+
temp_node <= join([all_up, the_reservation_next]).map do
|
137
|
+
break(((0..(the_reservation_next[[]].reservation["instancesSet"]["item"].size-1)).to_a.zip(the_reservation_next[[]].reservation["instancesSet"]["item"].map {|i| [i["ipAddress"], i["privateIpAddress"]]})).map {|n,ips| [n, ips[0] + ":54321", ips[1] + ":54321"]})
|
138
|
+
end
|
139
|
+
|
140
|
+
deploy_node <= join([temp_node, init_dir, ruby_command]).map do |t, i, r|
|
141
|
+
if depl_idempotent [[:node_startup, t.node]]
|
142
|
+
ip = t.node.split(":")[0]
|
143
|
+
port = t.node.split(":")[1]
|
144
|
+
print "Deploying to #{ip} (#{t.uid}/#{node_count[[]].num-1})."
|
145
|
+
STDOUT.flush
|
146
|
+
|
147
|
+
# Upload files and run commands.
|
148
|
+
ctr = 0
|
149
|
+
while ctr < 10
|
150
|
+
begin
|
151
|
+
Net::SSH.start(ip, 'ec2-user', :keys => [ec2_key_location[[]].loc],
|
152
|
+
:timeout => 5, :paranoid => false) do |session|
|
153
|
+
# Upload init_dir, and the IP and port of the deployer
|
154
|
+
session.scp.upload!("deploy_ip_port", "/home/ec2-user")
|
155
|
+
session.scp.upload!(init_dir[[]].dir, "/home/ec2-user",
|
156
|
+
:recursive => true)
|
157
|
+
# Update the Bud gem
|
158
|
+
channel = session.open_channel do |ch|
|
159
|
+
channel.request_pty do |_, success|
|
160
|
+
raise "Couldn't open a PTY on #{t.node}" if !success
|
161
|
+
end
|
162
|
+
channel.exec("sudo gem update --no-ri --no-rdoc bud")
|
163
|
+
channel.wait
|
164
|
+
end
|
165
|
+
# Run the ruby_command
|
166
|
+
session.exec!('nohup ' + ruby_command[[]].cmd + ' ' + t.localip +
|
167
|
+
' ' + t.node + ' >metarecv.out 2>metarecv.err </dev/null &')
|
168
|
+
end
|
169
|
+
break true
|
170
|
+
rescue Exception
|
171
|
+
ctr += 1
|
172
|
+
print "."
|
173
|
+
STDOUT.flush
|
174
|
+
sleep 10
|
175
|
+
next
|
176
|
+
end
|
177
|
+
end or raise "EC2 SSH failed after 10 retries"
|
178
|
+
|
179
|
+
puts "done"
|
180
|
+
[t.uid, t.node]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
bloom :all_nodes do
|
187
|
+
stdio <~ ready.map {|_,s| ["Ready: #{s}"]}
|
188
|
+
# Persist ready messages
|
189
|
+
ready_tab <= ready.map {|_, s| [s]}
|
190
|
+
# Compute a count of ready messages
|
191
|
+
ready_count <= ready_tab.group(nil, count)
|
192
|
+
# Copy deploy_node into node when all nodes are up
|
193
|
+
node <= join([ready_count, node_count],
|
194
|
+
[ready_count.num, node_count.num]).map do
|
195
|
+
break deploy_node
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
require 'thread'
|
4
|
+
require 'bud/deploy/deployer'
|
5
|
+
|
6
|
+
# Starts up a bunch of Bud instances locally on 127.0.0.1, with ephemeral ports.
|
7
|
+
module LocalDeploy
|
8
|
+
include Deployer
|
9
|
+
|
10
|
+
trap("CLD") {
|
11
|
+
pid = Process.wait
|
12
|
+
puts "Child pid #{pid}: terminated"
|
13
|
+
}
|
14
|
+
|
15
|
+
deploystrap do
|
16
|
+
read, write = IO.pipe
|
17
|
+
if node_count[[]]
|
18
|
+
print "Forking local processes"
|
19
|
+
(0..node_count[[]].num-1).map do |i|
|
20
|
+
Process.fork do
|
21
|
+
# Don't want to inherit our parent's random stuff.
|
22
|
+
srand
|
23
|
+
foo = self.class.new
|
24
|
+
foo.run_bg
|
25
|
+
print "."
|
26
|
+
$stdout.flush
|
27
|
+
# Processes write their port to a pipe.
|
28
|
+
write.print foo.port.to_s + "\n"
|
29
|
+
EventMachine.reactor_thread.join
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Read ports from pipe.
|
34
|
+
(0..node_count[[]].num-1).map do |i|
|
35
|
+
node << [i, "localhost:" + read.readline.rstrip]
|
36
|
+
end
|
37
|
+
puts "done"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/bud/errors.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bud
|
2
|
+
# Root Bud exception type.
|
3
|
+
class BudError < StandardError; end
|
4
|
+
|
5
|
+
# Raised (at runtime) when a type mismatch occurs (e.g., supplying a
|
6
|
+
# non-Enumerable object to the RHS of a Bud statement).
|
7
|
+
class BudTypeError < BudError; end
|
8
|
+
|
9
|
+
# Raised when a primary key constraint is violated.
|
10
|
+
class KeyConstraintError < BudError; end
|
11
|
+
|
12
|
+
# Raised when the input program fails to compile (e.g., due to illegal
|
13
|
+
# syntax).
|
14
|
+
class CompileError < BudError; end
|
15
|
+
end
|