bud 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/LICENSE +9 -0
  2. data/README +30 -0
  3. data/bin/budplot +134 -0
  4. data/bin/budvis +201 -0
  5. data/bin/rebl +4 -0
  6. data/docs/README.md +13 -0
  7. data/docs/bfs.md +379 -0
  8. data/docs/bfs.raw +251 -0
  9. data/docs/bfs_arch.png +0 -0
  10. data/docs/bloom-loop.png +0 -0
  11. data/docs/bust.md +83 -0
  12. data/docs/cheat.md +291 -0
  13. data/docs/deploy.md +96 -0
  14. data/docs/diffs +181 -0
  15. data/docs/getstarted.md +296 -0
  16. data/docs/intro.md +36 -0
  17. data/docs/modules.md +112 -0
  18. data/docs/operational.md +96 -0
  19. data/docs/rebl.md +99 -0
  20. data/docs/ruby_hooks.md +19 -0
  21. data/docs/visualizations.md +75 -0
  22. data/examples/README +1 -0
  23. data/examples/basics/hello.rb +12 -0
  24. data/examples/basics/out +1103 -0
  25. data/examples/basics/out.new +856 -0
  26. data/examples/basics/paths.rb +51 -0
  27. data/examples/bust/README.md +9 -0
  28. data/examples/bust/bustclient-example.rb +23 -0
  29. data/examples/bust/bustinspector.html +135 -0
  30. data/examples/bust/bustserver-example.rb +18 -0
  31. data/examples/chat/README.md +9 -0
  32. data/examples/chat/chat.rb +45 -0
  33. data/examples/chat/chat_protocol.rb +8 -0
  34. data/examples/chat/chat_server.rb +29 -0
  35. data/examples/deploy/tokenring-ec2.rb +26 -0
  36. data/examples/deploy/tokenring-local.rb +17 -0
  37. data/examples/deploy/tokenring.rb +39 -0
  38. data/lib/bud/aggs.rb +126 -0
  39. data/lib/bud/bud_meta.rb +185 -0
  40. data/lib/bud/bust/bust.rb +126 -0
  41. data/lib/bud/bust/client/idempotence.rb +10 -0
  42. data/lib/bud/bust/client/restclient.rb +49 -0
  43. data/lib/bud/collections.rb +937 -0
  44. data/lib/bud/depanalysis.rb +44 -0
  45. data/lib/bud/deploy/countatomicdelivery.rb +50 -0
  46. data/lib/bud/deploy/deployer.rb +67 -0
  47. data/lib/bud/deploy/ec2deploy.rb +200 -0
  48. data/lib/bud/deploy/localdeploy.rb +41 -0
  49. data/lib/bud/errors.rb +15 -0
  50. data/lib/bud/graphs.rb +405 -0
  51. data/lib/bud/joins.rb +300 -0
  52. data/lib/bud/rebl.rb +314 -0
  53. data/lib/bud/rewrite.rb +523 -0
  54. data/lib/bud/rtrace.rb +27 -0
  55. data/lib/bud/server.rb +43 -0
  56. data/lib/bud/state.rb +108 -0
  57. data/lib/bud/storage/tokyocabinet.rb +170 -0
  58. data/lib/bud/storage/zookeeper.rb +178 -0
  59. data/lib/bud/stratify.rb +83 -0
  60. data/lib/bud/viz.rb +65 -0
  61. data/lib/bud.rb +797 -0
  62. 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