bud 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/docs/README.md +10 -8
- data/docs/bfs.md +27 -5
- data/docs/bfs.raw +1 -1
- data/docs/bust.md +2 -2
- data/docs/cheat.md +7 -5
- data/docs/getstarted.md +21 -23
- data/docs/intro.md +7 -7
- data/docs/modules.md +10 -10
- data/docs/operational.md +7 -7
- data/docs/ruby_hooks.md +3 -3
- data/docs/visualizations.md +4 -4
- data/examples/bust/bustserver-example.rb +1 -1
- data/examples/deploy/tokenring.rb +1 -1
- data/lib/bud.rb +4 -3
- data/lib/bud/bust/bust.rb +6 -3
- data/lib/bud/collections.rb +1 -1
- data/lib/bud/deploy/ec2deploy.rb +1 -1
- data/lib/bud/deploy/localdeploy.rb +17 -5
- data/lib/bud/rewrite.rb +1 -1
- metadata +4 -4
data/docs/README.md
CHANGED
@@ -3,11 +3,13 @@ Welcome to the documentation for *Bud*, a prototype of Bloom under development.
|
|
3
3
|
|
4
4
|
The documents here are organized to be read in any order, but you might like to try the following:
|
5
5
|
|
6
|
-
*
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
6
|
+
* **intro.md**: A brief introduction to Bud and Bloom
|
7
|
+
* **getstarted.md**: A quickstart to teach you basic Bloom concepts, the use of `rebl` interactive terminal, and the embedding of Bloom code in Ruby via the `Bud` module.
|
8
|
+
* **operational.md**: an operational view of Bloom, to provide a more detailed model of how Bloom code is evaluated by Bud.
|
9
|
+
* **cheat.md**: A concise "cheat sheet" to remind you about Bloom syntax.
|
10
|
+
* **modules.md**: An overview of Bloom's modularity features.
|
11
|
+
* **ruby_hooks.md**: Bud module methods that allow you to interact with the Bud evaluator from other Ruby threads.
|
12
|
+
* **visualizations.md**: Overview of the `budvis` and `budplot` tools for visualizing Bloom program analyses.
|
13
|
+
* **bfs.md**: A walkthrough of the Bloom distributed filesystem.
|
14
|
+
* **bud-sandbox**: a github repository including lots of useful libraries and examples.
|
15
|
+
* **Bud RubyDoc**: The Bud gem ships with RubyDoc on the language constructs and runtime hooks provided by the Bud module. (To see rdoc, you run `gem server` from a command line and open [http://0.0.0.0:8808/](http://0.0.0.0:8808/))
|
data/docs/bfs.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# BFS: A distributed file system in Bloom
|
2
2
|
|
3
|
-
In this document we'll use what we've learned to build a piece of systems software using Bloom. The libraries that ship with
|
3
|
+
In this document we'll use what we've learned to build a piece of systems software using Bloom. The libraries that ship with Bud provide many of the building blocks we'll need to create a distributed,
|
4
4
|
``chunked'' file system in the style of the Google File System (GFS):
|
5
5
|
|
6
6
|
* a [key-value store](https://github.com/bloom-lang/bud-sandbox/blob/master/kvs/kvs.rb) (KVS)
|
@@ -360,20 +360,42 @@ To maintain the durability requirement that `REP_FACTOR` copies of every chunk a
|
|
360
360
|
that maintains a near-consistent view of global state, and takes steps to correct violated requirements.
|
361
361
|
|
362
362
|
__chunk_cache__ is the master's view of datanode state, maintained as described by collecting and pruning heartbeat messages.
|
363
|
+
After defining some helper aggregates (__chunk_cnts_chunk__ or replica count by chunk, and __chunk_cnt_host__ or datanode fill factor),
|
363
364
|
|
364
365
|
cc_demand <= (bg_timer * chunk_cache_alive).rights
|
365
366
|
cc_demand <= (bg_timer * last_heartbeat).pairs {|b, h| [h.peer, nil, nil]}
|
366
367
|
chunk_cnts_chunk <= cc_demand.group([cc_demand.chunkid], count(cc_demand.node))
|
367
368
|
chunk_cnts_host <= cc_demand.group([cc_demand.node], count(cc_demand.chunkid))
|
368
369
|
|
369
|
-
|
370
|
+
we define __lowchunks__ as the set of chunks whose replication factor is too low:
|
371
|
+
|
370
372
|
|
371
373
|
lowchunks <= chunk_cnts_chunk { |c| [c.chunkid] if c.replicas < REP_FACTOR and !c.chunkid.nil?}
|
372
|
-
|
373
|
-
|
374
|
+
|
375
|
+
We define __chosen_dest__ for a given underreplicated chunk as the datanode with
|
376
|
+
the lowest fill factor that is not already in possession of the chunk:
|
377
|
+
|
374
378
|
sources <= (cc_demand * lowchunks).pairs(:chunkid => :chunkid) {|a, b| [a.chunkid, a.node]}
|
375
379
|
# nodes not in possession of such chunks, and their fill factor
|
376
380
|
candidate_nodes <= (chunk_cnts_host * lowchunks).pairs do |c, p|
|
377
381
|
unless chunk_cache_alive.map{|a| a.node if a.chunkid == p.chunkid}.include? c.host
|
378
382
|
[p.chunkid, c.host, c.chunks]
|
379
|
-
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
best_dest <= candidate_nodes.argagg(:min, [candidate_nodes.chunkid], candidate_nodes.chunks)
|
387
|
+
chosen_dest <= best_dest.group([best_dest.chunkid], choose(best_dest.host))
|
388
|
+
|
389
|
+
and the __best_src__ as any arbitrary node that is in possession of the chunk:
|
390
|
+
|
391
|
+
best_src <= sources.group([sources.chunkid], choose(sources.host))
|
392
|
+
|
393
|
+
Finally, we initiate a background replication job for a given chunk by inserting
|
394
|
+
the chosen chunkid, source and destination into __copy_chunk__
|
395
|
+
|
396
|
+
copy_chunk <= (chosen_dest * best_src).pairs(:chunkid => :chunkid) do |d, s|
|
397
|
+
[d.chunkid, s.host, d.host]
|
398
|
+
end
|
399
|
+
|
400
|
+
|
401
|
+
|
data/docs/bfs.raw
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# BFS: A distributed file system in Bloom
|
2
2
|
|
3
|
-
In this document we'll use what we've learned to build a piece of systems software using Bloom. The libraries that ship with
|
3
|
+
In this document we'll use what we've learned to build a piece of systems software using Bloom. The libraries that ship with Bud provide many of the building blocks we'll need to create a distributed,
|
4
4
|
``chunked'' file system in the style of the Google File System (GFS):
|
5
5
|
|
6
6
|
* a [key-value store](https://github.com/bloom-lang/bud-sandbox/blob/master/kvs/kvs.rb) (KVS)
|
data/docs/bust.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
BUST stands for BUd State Transfer and it is a REST interface to
|
1
|
+
BUST stands for BUd State Transfer and it is a REST interface to Bud. BUST consists of a Bud implementation of a client and server. The client implements bindings to a subset of the Ruby Nestful library, and the server is a lightweight HTTP server written in Ruby. Note that the BUST server currently sets the "Access-Control-Allow-Origin: *" HTTP header to override web browsers' same-origin policy.
|
2
2
|
|
3
3
|
Right now BUST supports "GET" and "POST" requests, and may support "DELETE" and "PUT" requests in the future.
|
4
4
|
|
@@ -18,7 +18,7 @@ In your class, make sure to:
|
|
18
18
|
|
19
19
|
include Bust
|
20
20
|
|
21
|
-
That's it! Now a BUST server will be started up when your class is instantiated.
|
21
|
+
That's it! Now a BUST server will be started up when your class is instantiated. This server will listen on an open port; it will print out "Bust server listening on port xxxxx" to let you know which port it has selected. But you can select a specific port by passing a port via the "bust_port" option when you instantiate your class.
|
22
22
|
|
23
23
|
You can test out the BUST server using Ruby's "net/http" library if you want, and you can also check out "BUST Inspector", a sample AJAX application that allows you to view the state of a bud instance.
|
24
24
|
|
data/docs/cheat.md
CHANGED
@@ -55,7 +55,7 @@ Default attributes: `[:@address, :val] => []`
|
|
55
55
|
(Bloom statements with channel on lhs must use async merge (`<~`).)
|
56
56
|
|
57
57
|
channel :msgs
|
58
|
-
channel :req_chan, [
|
58
|
+
channel :req_chan, [:cartnum, :storenum, :@server] => [:command, :params]
|
59
59
|
|
60
60
|
### periodic ###
|
61
61
|
System timer manifested as a scratch collection.<br>
|
@@ -97,7 +97,7 @@ Left-hand-side (lhs) is a named `BudCollection` object.<br>
|
|
97
97
|
Right-hand-side (rhs) is a Ruby expression producing a `BudCollection` or `Array` of `Arrays`.<br>
|
98
98
|
BloomOp is one of the 5 operators listed below.
|
99
99
|
|
100
|
-
|
100
|
+
### Bloom Operators ###
|
101
101
|
merges:
|
102
102
|
|
103
103
|
* `left <= right` (*instantaneous*)
|
@@ -115,7 +115,7 @@ insert:<br>
|
|
115
115
|
Note that unlike merge/delete, insert expects a single fact on the rhs, rather
|
116
116
|
than a collection.
|
117
117
|
|
118
|
-
|
118
|
+
### Collection Methods ###
|
119
119
|
Standard Ruby methods used on a BudCollection `bc`:
|
120
120
|
|
121
121
|
implicit map:
|
@@ -154,7 +154,7 @@ implicit map:
|
|
154
154
|
|
155
155
|
stdio <~ bc.inspected
|
156
156
|
|
157
|
-
`chan.payloads`:
|
157
|
+
`chan.payloads`: projects `chan` to non-address columns. only defined for channels
|
158
158
|
|
159
159
|
# at sender
|
160
160
|
msgs <~ requests {|r| "127.0.0.1:12345", r}
|
@@ -183,6 +183,8 @@ implicit map:
|
|
183
183
|
* Summary aggs: `count`, `sum`, `avg`
|
184
184
|
* Structural aggs: `accum`
|
185
185
|
|
186
|
+
Note that custom aggregation can be written using `reduce`.
|
187
|
+
|
186
188
|
## Collection Combination (Join) ###
|
187
189
|
To match items across two (or more) collections, use the `*` operator, followed by methods to filter/format the result (`pairs`, `matches`, `combos`, `lefts`, `rights`).
|
188
190
|
|
@@ -225,7 +227,7 @@ Like `pairs`, but implicitly includes a block that projects down to the left ite
|
|
225
227
|
`rights(`*hash pairs*`)`:
|
226
228
|
Like `pairs`, but implicitly includes a block that projects down to the right item in each pair.
|
227
229
|
|
228
|
-
`flatten
|
230
|
+
`flatten`:<br>
|
229
231
|
`flatten` is a bit like SQL's `SELECT *`: it produces a collection of concatenated objects, with a schema that is the concatenation of the schemas in tablelist (with duplicate names disambiguated.) Useful for chaining to operators that expect input collections with schemas, e.g. group:
|
230
232
|
|
231
233
|
out <= (r * s).matches.flatten.group([:a], max(:b))
|
data/docs/getstarted.md
CHANGED
@@ -107,14 +107,14 @@ See how the second tick produced no output this time? After the first timestep,
|
|
107
107
|
### Summing Up ###
|
108
108
|
In these initial examples we learned about a few simple but important things:
|
109
109
|
|
110
|
-
* **rebl**: the interactive Bloom terminal and its `/` commands.
|
111
|
-
* **Bloom collections**: unordered sets of items, which are set up by collection declarations. So far we have seen persistent `table` and transient `scratch` collections. By default, collections are structured as \[key,
|
110
|
+
* **rebl**: the interactive Bloom terminal and its slash (`/`) commands.
|
111
|
+
* **Bloom collections**: unordered sets of items, which are set up by collection declarations. So far we have seen persistent `table` and transient `scratch` collections. By default, collections are structured as \[key,val\] pairs.
|
112
112
|
* **Bloom statements**: expressions of the form *lhs op rhs*, where the lhs is a collection and the rhs is either a collection or an array-of-arrays.
|
113
113
|
* **Bloom timestep**: an atomic single-machine evaluation of a block of Bloom statements.
|
114
114
|
* **Bloom merge operators**:
|
115
115
|
* The `<=` merge operator for *instantaneously* merging things into a collection.
|
116
|
-
* The `<~` merge operator for *asynchronously* merging things into collections outside the control of tick evaluation: e.g. terminal output
|
117
|
-
* **stdio**: a built-in Bloom collection that, when
|
116
|
+
* The `<~` merge operator for *asynchronously* merging things into collections outside the control of tick evaluation: e.g. terminal output.
|
117
|
+
* **stdio**: a built-in Bloom collection that, when placed on the lhs of an asynch merge operator `<~`, prints its contents to stdout.
|
118
118
|
* **inspected**: a method of Bloom collections that transforms the elements to be suitable for textual display on the terminal.
|
119
119
|
|
120
120
|
## Chat, World! ##
|
@@ -147,7 +147,7 @@ This defines a [Ruby mixin module](http://www.ruby-doc.org/docs/ProgrammingRuby/
|
|
147
147
|
1. It contains a Bloom `state` block, containing collection declarations. When embedding Bloom in Ruby, all Bloom collection declarations must appear in a `state` block of this sort.
|
148
148
|
2. This particular state block uses a kind of Bloom collection we have not seen before: a `channel`. A channel collection is a special kind of scratch used for network communication. It has a few key features:
|
149
149
|
|
150
|
-
* Unlike the default \[key,
|
150
|
+
* Unlike the default \[key,val\] structure of scratches and tables, channels default to the structure \[address,val\]: the first field is a destination IP string of the form 'host:port', and the second field is a payload to be delivered to that destination--typically a Ruby array. (For the record, the default key of a channel collection is the *pair* \[address,val\]).
|
151
151
|
* Any Bloom statement with a channel on the lhs must use the async merge (`<~`) operator. This instructs the runtime to attempt to deliver each rhs item to the address stored therein. In an async merge, each item in the collection on the right will appear in the collection on the lhs *eventually*. But this will not happen instantaneously, and it might not happen atomically--items in the collection on the rhs may "straggle in" individually over time at the destination. And if you're unlucky, this may happen after an arbitrarily long delay (possibly never). The use of `<~` for channels reflects the typical uncertainty of real-world network delivery. (Don't worry, the Bud sandbox provides libraries to wrap that uncertainty up in convenient ways.)
|
152
152
|
|
153
153
|
Given this protocol (and the Ruby constant at the bottom), we're now ready to examine `examples/chat/chat_server.rb` in more detail:
|
@@ -188,7 +188,7 @@ The first is pretty simple:
|
|
188
188
|
|
189
189
|
nodelist <= connect.payloads
|
190
190
|
|
191
|
-
This says that whenever messages arrive on the channel named "connect", their payloads (i.e. their non-address field) should be instantaneously merged into the table nodelist, which will store them persistently. Note that nodelist has a \[key/
|
191
|
+
This says that whenever messages arrive on the channel named "connect", their payloads (i.e. their non-address field) should be instantaneously merged into the table nodelist, which will store them persistently. Note that nodelist has a \[key/val\] pair structure, so we expect the payloads will have that structure as well.
|
192
192
|
|
193
193
|
The next Bloom statement is more complex. Remember the description in the "basic idea" at the beginning of this section: the server needs to accept inbound chat messages from clients, and forward them to other clients.
|
194
194
|
|
@@ -196,16 +196,16 @@ The next Bloom statement is more complex. Remember the description in the "basi
|
|
196
196
|
|
197
197
|
The first thing to note is the lhs and operator in this statement. We are merging items (asynchronously, of course!) into the mcast channel, where they will be sent to their eventual destination.
|
198
198
|
|
199
|
-
The rhs is our first introduction to the `*` operator of Bloom collections, and the `pairs` method after it. You can think of the `*` operator as "all-pairs": it produces a Bloom collection containing all pairs of mcast and nodelist items. The `pairs` method iterates through these pairs, passing them through a code block via the block arguments `m` and `n`. Finally, for each such pair the block produces an item containing the `key` attribute of the nodelist item, and the `val` attribute of the mcast item. This is structured as a proper \[address,
|
199
|
+
The rhs is our first introduction to the `*` operator of Bloom collections, and the `pairs` method after it. You can think of the `*` operator as "all-pairs": it produces a Bloom collection containing all pairs of mcast and nodelist items. The `pairs` method iterates through these pairs, passing them through a code block via the block arguments `m` and `n`. Finally, for each such pair the block produces an item containing the `key` attribute of the nodelist item, and the `val` attribute of the mcast item. This is structured as a proper \[address, val\] entry to be merged back into the mcast channel. Putting this together, this statement *multicasts inbound payloads on the mcast channel to all nodes in the chat*.
|
200
200
|
|
201
201
|
The remaining lines of plain Ruby simply instantiate and run the ChatServer class (which includes the `Bud` module) using an ip and port given on the command line (or the default from ChatProtocol.rb).
|
202
202
|
|
203
203
|
#### `*`'s and Clouds ####
|
204
204
|
You can think of out use of the `*` operator in the rhs of the second statement in a few different ways:
|
205
205
|
|
206
|
-
* If you're familiar with event-loop programming, this implements an *event handler* for messages on the mcast channel: whenever an mcast message arrives, this handler performs lookups in the nodelist table to form new messages. (It is easy to add "filters" to these handlers
|
206
|
+
* If you're familiar with event-loop programming, this implements an *event handler* for messages on the mcast channel: whenever an mcast message arrives, this handler performs lookups in the nodelist table to form new messages. (It is easy to add "filters" to these handlers as arguments to `pairs`.) The resulting messages are dispatched via the mcast channel accordingly. This is a very common pattern in Bloom programs: handling channel messages via lookups in a table.
|
207
207
|
|
208
|
-
* If you're familiar with SQL databases, the rhs is essentially a query that is run at each timestep, performing a CROSS JOIN of the mcast and nodelist "tables", with the SELECT clause captured by the block. (It is easy to add WHERE clauses to these joins
|
208
|
+
* If you're familiar with SQL databases, the rhs is essentially a query that is run at each timestep, performing a CROSS JOIN of the mcast and nodelist "tables", with the SELECT clause captured by the block. (It is easy to add WHERE clauses to these joins as arguments to `pairs`.) The resulting "tuples" are "inserted" into the lhs asynchronously (and typical on remote nodes). This is a general-purpose way to think about the * operator. But as you've already seen, many common use cases for Bloom's * operator don't "feel" like database queries, because one or more of the collections is a scratch that is "driving" the program.
|
209
209
|
|
210
210
|
We expect that people doing distributed programming are probably familiar with both of these metaphors, and they're both useful. It's fairly common to think about rules in the first form, although the second form is actually closer to the underlying semantics of the language (which come from a temporal logic called [Dedalus](http://www.eecs.berkeley.edu/Pubs/TechRpts/2009/EECS-2009-173.html)).
|
211
211
|
|
@@ -222,36 +222,34 @@ And here's the code:
|
|
222
222
|
include Bud
|
223
223
|
include ChatProtocol
|
224
224
|
|
225
|
-
def initialize(nick, server, opts)
|
225
|
+
def initialize(nick, server, opts={})
|
226
226
|
@nick = nick
|
227
227
|
@server = server
|
228
228
|
super opts
|
229
229
|
end
|
230
230
|
|
231
|
-
# send connection request to server on startup
|
232
231
|
bootstrap do
|
233
232
|
connect <~ [[@server, [ip_port, @nick]]]
|
234
233
|
end
|
235
234
|
|
236
|
-
bloom
|
237
|
-
# send mcast requests to server
|
235
|
+
bloom do
|
238
236
|
mcast <~ stdio do |s|
|
239
237
|
[@server, [ip_port, @nick, Time.new.strftime("%I:%M.%S"), s.line]]
|
240
238
|
end
|
241
|
-
|
242
|
-
stdio <~ mcast
|
243
|
-
[left_right_align(m.val[1].to_s + ": " \
|
244
|
-
+ (m.val[3].to_s || ''),
|
245
|
-
"(" + m.val[2].to_s + ")")]
|
246
|
-
end
|
239
|
+
|
240
|
+
stdio <~ mcast { |m| [pretty_print(m.val)] }
|
247
241
|
end
|
248
242
|
|
249
243
|
# format chat messages with timestamp on the right of the screen
|
250
|
-
def
|
251
|
-
|
244
|
+
def pretty_print(val)
|
245
|
+
str = val[1].to_s + ": " + (val[3].to_s || '')
|
246
|
+
pad = "(" + val[2].to_s + ")"
|
247
|
+
return str + " "*[66 - str.length,2].max + pad
|
252
248
|
end
|
253
249
|
end
|
254
250
|
|
251
|
+
|
252
|
+
|
255
253
|
if ARGV.length == 2
|
256
254
|
server = ARGV[1]
|
257
255
|
else
|
@@ -260,13 +258,13 @@ And here's the code:
|
|
260
258
|
|
261
259
|
puts "Server address: #{server}"
|
262
260
|
program = ChatClient.new(ARGV[0], server, :read_stdin => true)
|
263
|
-
program.
|
261
|
+
program.run_fg
|
264
262
|
|
265
263
|
The ChatClient class has a typical Ruby `initialize` method that sets up two local instance variables: one for this client's nickname, and another for the 'IP:port' address string for the server. It then calls the initializer of the Bud superclass passing along a hash of options.
|
266
264
|
|
267
265
|
The next block in the class is the first Bloom `bootstrap` block we've seen. This is a set of Bloom statements that are evaluated only once, just before the first "regular" timestep of the system. In this case, we bootstrap the client by sending a message to the server on the connect channel, containing the client's address (via the built-in Bud instance method `ip_port`) and chosen nickname.
|
268
266
|
|
269
|
-
After that comes a bloom block, with the name `:chatter`. It contains two statements: one to take stdio input from the terminal and send it to the server via mcast, and another to receive mcasts and place them on stdio output. The first statement has the built-in `stdio` scratch on the rhs: this includes any lines of terminal input that arrived since the last timestep. For each line of terminal input, the `do...end` block formats an `mcast` message destined to the address in the instance variable `@server`, with an array as the payload. The rhs of the second statement takes `mcast` messages that arrived since the last timestep. For each message `m`, the `m.val` expression in the block returns the message payload; the call to the Ruby instance method `
|
267
|
+
After that comes a bloom block, with the name `:chatter`. It contains two statements: one to take stdio input from the terminal and send it to the server via mcast, and another to receive mcasts and place them on stdio output. The first statement has the built-in `stdio` scratch on the rhs: this includes any lines of terminal input that arrived since the last timestep. For each line of terminal input, the `do...end` block formats an `mcast` message destined to the address in the instance variable `@server`, with an array as the payload. The rhs of the second statement takes `mcast` messages that arrived since the last timestep. For each message `m`, the `m.val` expression in the block returns the message payload; the call to the Ruby instance method `pretty_print` formats the message so it will look nice on-screen. These formatted strings are placed (asynchronously, as before) into `stdio` on the left.
|
270
268
|
|
271
269
|
The remaining lines are Ruby driver code to instantiate and run the ChatClient class (which includes the `Bud` module) using arguments from the command line. Note the option `:read_stdin => true` to `ChatClient.new`: this causes the Bud runtime to capture stdin via the built-in `stdio` collection.
|
272
270
|
|
data/docs/intro.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# *Bud*: Ruby <~ Bloom #
|
2
2
|
|
3
|
-
Bud is a prototype of the [*Bloom*](http://bloom-lang.org) language for distributed programming, embedded as a DSL in Ruby. "Bud" stands for *Bloom under development*. The current
|
3
|
+
Bud is a prototype of the [*Bloom*](http://bloom-lang.org) language for distributed programming, embedded as a DSL in Ruby. "Bud" stands for *Bloom under development*. The current release is the initial alpha, targeted at "friends and family" who would like to engage at an early stage in the language design.
|
4
4
|
|
5
5
|
## Distributed Code in Bloom ##
|
6
6
|
The goal of Bloom is to make distributed programming far easier than it has been with traditional languages. The key features of Bloom are:
|
@@ -15,22 +15,22 @@ The goal of Bloom is to make distributed programming far easier than it has been
|
|
15
15
|
|
16
16
|
## Alpha Goals and Limitations ##
|
17
17
|
|
18
|
-
We had three main goals in preparing this release. The first was to flesh out the shape of the Bloom language: initial syntax and semantics, and the "feel" of embedding it as a DSL. The second goal was to build tools for reasoning about Bloom programs: both automatic program
|
18
|
+
We had three main goals in preparing this release. The first was to flesh out the shape of the Bloom language: initial syntax and semantics, and the "feel" of embedding it as a DSL. The second goal was to build tools for reasoning about Bloom programs: both automatic program analyses, and tools for surfacing those analyses to developers.
|
19
19
|
|
20
20
|
The third goal was to start a feedback loop with developers interested in the potential of the ideas behind the language. We are optimistic that the principles underlying Bloom can make distributed programming radically simpler. But we realize that those ideas only matter if programmers can adopt them naturally. We intend Bud to be the beginning of an iterative design partnership with developers who see value in betting early on these ideas, and shaping the design of the language.
|
21
21
|
|
22
|
-
In developing this alpha release, we explicitly set aside some issues that we intend to revisit in future. The first limitation is performance: Bud
|
22
|
+
In developing this alpha release, we explicitly set aside some issues that we intend to revisit in future. The first limitation is performance: Bud alpha is not intended to excel in single-node performance in terms of either latency, throughput or scale. We do expect major improvements on all these fronts in future releases: many of the known performance problems have known solutions that we've implemented in prior systems. The second main limitation involves integration issues embedding Bloom as a DSL in Ruby. In the spectrum from flexibility to purity, we leaned decidedly toward flexibility. The barriers between Ruby and Bloom code are very fluid in the alpha, and we do relatively little to prevent programmers from ad-hoc mixtures of the two. Aggressive use of Ruby within Bloom statements is likely to do something *interesting*, but not necessarily predictable or desirable. This is an area where we expect to learn more from experience, and make some more refined decisions for the beta release.
|
23
23
|
|
24
24
|
### Friends and Family: Come On In ###
|
25
|
-
Although our team has many years of development experience, Bud is still open-source academic software built
|
25
|
+
Although our team has many years of development experience, Bud is still open-source academic software built by a small group of researchers.
|
26
26
|
|
27
|
-
This
|
27
|
+
This alpha is targeted at "friends and family", and at developers who'd like to become same. This is definitely the bleeding edge: we're in a rapid cycle of learning about this new style of programming, and exposing what we learn in new iterations of the language. If you'd like to jump on the wheel with us and play with Bud, we'd love your feedback--both success stories and constructive criticism.
|
28
28
|
|
29
29
|
## Getting Started ##
|
30
30
|
We're shipping Bud with a [sandbox](http://github.com/bloom-lang/bud-sandbox) of libraries and example applications for distributed systems. These illustrate the language and how it can be used, and also can serve as mixins for new code you might want to write. You may be surprised at how short the provided Bud code is, but don't be fooled.
|
31
31
|
|
32
|
-
To get you started with Bud, we've provided a [quick-start tutorial](getstarted.md), instructions for [deploying distributed Bud](
|
32
|
+
To get you started with Bud, we've provided a [quick-start tutorial](getstarted.md), instructions for [deploying distributed Bud](deploy.md) programs on Amazon's EC2 cloud, and a number of other docs you can find linked from the [README](README.md).
|
33
33
|
|
34
|
-
We welcome both constructive criticism and (hopefully occasional) smoke-out-your-ears, hair-tearing shouts of frustration. Please point your feedback cannon at the [Bloom mailing list](http://groups.google.com/group/bloom-lang)
|
34
|
+
We welcome both constructive criticism and (hopefully occasional) smoke-out-your-ears, hair-tearing shouts of frustration. Please point your feedback cannon at the [Bloom mailing list](http://groups.google.com/group/bloom-lang).
|
35
35
|
|
36
36
|
Happy Blooming!
|
data/docs/modules.md
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
# Code Structuring and Reuse in
|
1
|
+
# Code Structuring and Reuse in Bud
|
2
2
|
|
3
3
|
## Language Support
|
4
4
|
|
5
5
|
### Ruby Mixins
|
6
6
|
|
7
|
-
The basic unit of reuse in
|
7
|
+
The basic unit of reuse in Bud is the mixin functionality provided by Ruby itself. Bud code is structured into modules, each of which may have its own __state__ and __bootstrap__ block and any number of __bloom__ blocks (described below). A module or class may mix in a Bud module via Ruby's _include_ statement. _include_ causes the specified module's code to be expanded into the local scope.
|
8
8
|
|
9
9
|
### Bloom Blocks
|
10
10
|
|
11
|
-
While the order and grouping of
|
11
|
+
While the order and grouping of Bud rules have no semantic significance, rules can be grouped and tagged within a single module using __bloom__ blocks. Bloom blocks serve two purposes:
|
12
12
|
|
13
13
|
1. Improving readability by grouping related or dependent rules together.
|
14
14
|
2. Supporting name-based overriding.
|
15
15
|
|
16
|
-
(1) is self-explanatory. (2) represents one of several extensibility mechanisms provided by
|
16
|
+
(1) is self-explanatory. (2) represents one of several extensibility mechanisms provided by Bud. If a Module B includes a module A which contains a basket X, B may supply a bloom block X and in so doing replaces the set of rules defined by (A.)X with its own set. For example:
|
17
17
|
|
18
18
|
require 'rubygems'
|
19
19
|
require 'bud'
|
@@ -46,11 +46,11 @@ While the order and grouping of BUD rules have no semantic significance, rules c
|
|
46
46
|
The program above will print "Hello, 1", because the module HelloTwo overrides the bloom block named __hi__. If we give the bloom block in HelloTwo a distinct name, the program will print "Hello, 1" and "Hello, 2" (in no guaranteed order).
|
47
47
|
|
48
48
|
|
49
|
-
### The
|
49
|
+
### The Bud Module Import System
|
50
50
|
|
51
51
|
For simple programs, composing modules via _include_ is often sufficient. But the flat namespace provided by mixins can make it difficult or impossible to support certain types of reuse. Consider a module Q that provides a queue-like functionality via an input interface _enqueue_ and an output interface _dequeue_, each with a single attribute (payload). A later module may wish to employ two queues (say, to implement a scheduler). But it cannot include Q twice! It would be necessary to rewrite Q's interfaces so as to support multiple ``users.''
|
52
52
|
|
53
|
-
In addition to _include_,
|
53
|
+
In addition to _include_, Bud supports the _import_ keyword, which instantiates a Bud module under a namespace alias. For example:
|
54
54
|
|
55
55
|
module UserCode
|
56
56
|
import Q => :q1
|
@@ -67,18 +67,18 @@ In addition to _include_, BUD supports the _import_ keyword, which instantiates
|
|
67
67
|
|
68
68
|
### Structuring
|
69
69
|
|
70
|
-
In summary,
|
70
|
+
In summary, Bud extends the basic Ruby code structuring mechanisms (classes and modules) with bloom blocks, for finer-granularity grouping of rules
|
71
71
|
within modules, and the import system, for scoped inclusion.
|
72
72
|
|
73
73
|
### Composition
|
74
74
|
|
75
|
-
Basic code composition can achieved using the Ruby mixin system. If the flat namespace causes ambiguity (as above) or hinders readability, the import system provides the ability to scope code inclusions.
|
75
|
+
Basic code composition can achieved using the Ruby mixin system. If the flat namespace causes ambiguity (as above) or hinders readability, the Bud import system provides the ability to scope code inclusions.
|
76
76
|
|
77
77
|
### Extension and Overriding
|
78
78
|
|
79
|
-
Extending the existing functionality of a
|
79
|
+
Extending the existing functionality of a Bud program can be achieved in a number of ways. The simplest (but arguably least flexible) is via bloom block overriding, as described in the Hello example above.
|
80
80
|
|
81
|
-
The import system can be used to implement finer-grained overriding, at the collection level. Consider a module BlackBox that provides an input interface __iin__ and an output interface __iout__. Suppose that we wish to "use" BlackBox, but need to provide additional functionality. We may extend one or both of its interfaces by _import_
|
81
|
+
The import system can be used to implement finer-grained overriding, at the collection level. Consider a module BlackBox that provides an input interface __iin__ and an output interface __iout__. Suppose that we wish to "use" BlackBox, but need to provide additional functionality. We may extend one or both of its interfaces by _import_-ing BlackBox, redeclaring the interfaces, and gluing them together. For example, the module UsesBlackBox shown below interposes additional logic (indicated by ellipses) upstream of BlackBox's input interface, and provides ``extended'' BlackBox functionality.
|
82
82
|
|
83
83
|
module UsesBlackBox
|
84
84
|
import BlackBox => :bb
|
data/docs/operational.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# An Operational View Of Bloom #
|
2
|
-
You may ask yourself: well, what does a Bloom program *mean*? You may ask yourself: How do I read this Bloom code? ([You may tell yourself, this is not my beautiful house.](http://www.youtube.com/watch?v=I1wg1DNHbNU)
|
2
|
+
You may ask yourself: well, what does a Bloom program *mean*? You may ask yourself: How do I read this Bloom code? ([You may tell yourself, this is not my beautiful house.](http://www.youtube.com/watch?v=I1wg1DNHbNU))
|
3
3
|
|
4
|
-
There is a formal answer to these questions about Bloom, but there's also a more more approachable answer.
|
4
|
+
There is a formal answer to these questions about Bloom, but there's also a more more approachable answer. Briefly, the formal answer is that Bloom's semantics are based in finite model theory, via a temporal logic language called *Dedalus* that is described in [a paper from Berkeley](http://www.eecs.berkeley.edu/Pubs/TechRpts/2009/EECS-2009-173.html).
|
5
5
|
|
6
6
|
While that's nice for proving theorems (and writing [program analysis tools](visualizations.md)), many programmers don't find model-theoretic discussions of semantics terribly helpful or even interesting. It's usually easier to think about how the language *works* at some level, so you can reason about how to use it.
|
7
7
|
|
@@ -16,7 +16,7 @@ Each machine runs an evaluator that works in a loop, as depicted in this figure:
|
|
16
16
|
|
17
17
|
Each iteration of this loop is a *timestep* for that node; each timestep is associated with a monotonically increasing timestamp (which is accessible via the `budtime` method in Bud). Timesteps and timestamps are not coordinated across nodes; any such coordination has to be programmed in the Bloom language itself.
|
18
18
|
|
19
|
-
A Bloom timestep has 3 main phases:
|
19
|
+
A Bloom timestep has 3 main phases (from left to right):
|
20
20
|
|
21
21
|
1. *setup*: All scratch collections are set to empty. Network messages and periodic timer events are received from the runtime and placed into their designated `channel` and `periodic` scratches, respectively, to be read in the rhs of statements. Note that a batch of multiple messages/events may be received at once.
|
22
22
|
2. *logic*: All Bloom statements for the program are evaluated. In programs with recursion through instantaneous merges (`<=`), the statements are repeatedly evaluated until a *fixpoint* is reached: i.e. no new lhs items are derived from any rhs.
|
@@ -31,7 +31,7 @@ It is important to understand how the Bloom collection operators fit into these
|
|
31
31
|
|
32
32
|
## Atomicity: Timesteps and Deferred Operators ##
|
33
33
|
|
34
|
-
The only instantaneous Bloom operator is a merge (`<=`), which can only introduce additional items into a collection--it can not delete or change existing items. As a result, all state within a Bloom timestep is *immutable*: once an item is in a collection at timestep *T*, it stays in that collection throughout timestep *T*.
|
34
|
+
The only instantaneous Bloom operator is a merge (`<=`), which can only introduce additional items into a collection--it can not delete or change existing items. As a result, all state within a Bloom timestep is *immutable*: once an item is in a collection at timestep *T*, it stays in that collection throughout timestep *T*. (And forever after, the fact that the item was in that collection at timestep *T* remains true.)
|
35
35
|
|
36
36
|
To get atomic state change in Bloom, you exploit the combination of two language features:
|
37
37
|
|
@@ -45,7 +45,7 @@ State "update" is achieved in Bloom via a pair of deferred statements, one posit
|
|
45
45
|
|
46
46
|
This atomically replaces the entry for key 1 with the value "newval" at the start of the next timestep.
|
47
47
|
|
48
|
-
Any reasoning about atomicity in Bloom programs is built on this simple foundation. It's really all you need. In the bud-sandbox we show how to build more powerful atomicity constructs using it, including things like enforcing [
|
48
|
+
Any reasoning about atomicity in Bloom programs is built on this simple foundation. It's really all you need. In the bud-sandbox we show how to build more powerful atomicity constructs using it, including things like enforcing [ordering of items across timesteps](https://github.com/bloom-lang/bud-sandbox/tree/master/ordering), and protocols for [agreeing on ordering of distributed updates](https://github.com/bloom-lang/bud-sandbox/tree/master/paxos) across all nodes.
|
49
49
|
|
50
50
|
## Recursion in Bloom ##
|
51
51
|
Because Bloom is data-driven rather than call-stack-driven, recursion may feel a bit unfamiliar at first.
|
@@ -69,7 +69,7 @@ Have a look at the following classic "transitive closure" example, which compute
|
|
69
69
|
|
70
70
|
The recursion in the second Bloom statement is easy to see: the lhs and rhs both contain the path collection, so path is defined in terms of itself.
|
71
71
|
|
72
|
-
You can think of this being computed by reevaluating the bloom block over and over--within a single timestep--until no more new paths are found. In each iteration, we find new paths that are one hop longer than the longest paths found previously. When no new items are found in an iteration, we are at what's called a *fixpoint
|
72
|
+
You can think of this being computed by reevaluating the bloom block over and over--within phase 2 of a single timestep--until no more new paths are found. In each iteration, we find new paths that are one hop longer than the longest paths found previously. When no new items are found in an iteration, we are at what's called a *fixpoint*, and we can move to phase 3.
|
73
73
|
|
74
74
|
Hopefully that description is fairly easy to understand. You can certainly construct more complicated examples of recursion--just as you can in a traditional language (e.g., simultaneous recursion.) But understanding this example of simple recursion is probably sufficient for most needs.
|
75
75
|
|
@@ -85,7 +85,7 @@ The `argmax` expression in the rhs of this statement finds the items in path tha
|
|
85
85
|
|
86
86
|
It's interesting to think about how to evaluate this statement. Consider what happens after a single iteration of the path-finding logic listed above. We will have 1-hop paths between some pairs. But there will likely be multi-hop paths between those pairs that cost more. So it would be premature after a single iteration to put anything out on stdio. In fact, we can't be sure what should go out to stdio until we have hit a fixpoint with respect to the path collection. That's because `argmax` is a logically *non-monotonic* operator: as we merge more items into its input collection, it may have to "retract" an output they would previously have produced.
|
87
87
|
|
88
|
-
The Bud runtime takes care of this problem for you under the covers, by breaking your statements in *strata* (layers) via a process called *stratification*. The basic idea is simple. The goal is to postpone evaluating non-monotonic operators until fixpoint is reached on their input collections. Stratification basically breaks up the statements in a Bloom program into layers that are separated by non-monotonic operators, and evaluates the layers in order.
|
88
|
+
The Bud runtime takes care of this problem for you under the covers, by breaking your statements in *strata* (layers) via a process called *stratification*. The basic idea is simple. The goal is to postpone evaluating non-monotonic operators until fixpoint is reached on their input collections. Stratification basically breaks up the statements in a Bloom program into layers that are separated by non-monotonic operators, and evaluates the layers in order. (Note: the singular form of strata is *stratum*).
|
89
89
|
|
90
90
|
For your reference, the basic non-monotonic Bloom operators include `group, reduce, argmin, argmax`. Also, statements that embed Ruby collection methods in their blocks are often non-monotonic--e.g., methods like `all?, empty?, include?, none?` and `size`.
|
91
91
|
|
data/docs/ruby_hooks.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
## Ruby/Bloom interactions in Bud ##
|
2
|
-
Bud embeds Bloom as a [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) in Ruby. On a given node, it is often useful to run a Ruby thread, and pass information
|
2
|
+
Bud embeds Bloom as a [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) in Ruby. On a given node, it is often useful to run a Ruby thread, and pass information between one vanilla Ruby thread, and another one running Bloom code.
|
3
3
|
|
4
|
-
As described in the [Getting Started](getstarted.md) document, we embed Bloom code into Ruby by including the `Bud` module in some Ruby class, and putting Bloom blocks into the class. We then typically allocate a new instance of that class in Ruby, and invoke either its `
|
4
|
+
As described in the [Getting Started](getstarted.md) document, we embed Bloom code into Ruby by including the `Bud` module in some Ruby class, and putting Bloom blocks into the class. We then typically allocate a new instance of that class in Ruby, and invoke either its `run_fg` method or `run_bg` method. Since the Bud runtime typically runs indefinitely, the blocking `run_fg` call essentially hands the thread over to Bud. The `run_bg` call puts the Bud runtime into a second thread and returns to the caller.
|
5
5
|
|
6
6
|
To support the `run_bg` case further, the Bud runtime provides hooks for Ruby threads to register code with the runtime for execution during Bloom timesteps. These hooks always run at the end of a timestep, after all Bloom statements have been processed for that timestep. There are two basic types of hooks:
|
7
7
|
|
@@ -12,7 +12,7 @@ To support the `run_bg` case further, the Bud runtime provides hooks for Ruby t
|
|
12
12
|
## Bud and Ruby-driven Side Effects ##
|
13
13
|
The Bloom language itself is a pure logic language based in Dedalus. Like any logic language, it has no notion of "mutable state" or "side effects". Each item that appears in a Bloom collection at timestep T will *forever* be recorded as having been in that collection at timestep T, at least in a formal sense. The temporal logic of Dedalus is a lot like a versioning system, where old versions of items are never removed.
|
14
14
|
|
15
|
-
In the context of the existing Bud prototype, though, it easy to step outside the bright lines of pure Bloom using straight Ruby code and libraries. Methods like `sync_do` and callbacks allow Ruby code to be run that can mess with Bloom collections in a way that Bloom doesn't model. Similarly, Ruby blocks *within* the rhs of a Bloom statement (e.g. after a collection name or a `pairs` call) can produce visible side-effects in unpredictable ways. In particular, be aware that side-
|
15
|
+
In the context of the existing Bud prototype, though, it easy to step outside the bright lines of pure Bloom using straight Ruby code and libraries. Methods like `sync_do` and callbacks allow Ruby code to be run that can mess with Bloom collections in a way that Bloom doesn't model. Similarly, Ruby blocks *within* the rhs of a Bloom statement (e.g. after a collection name or a `pairs` call) can produce visible side-effects in unpredictable ways. In particular, be aware that any side-effect-producing Ruby code within a Bloom block may be called an *arbitrary* number of times during a single Bloom timestep, as Bud works its way to fixpoint.
|
16
16
|
|
17
17
|
If you must pollute your Bloom statements with side-effecting Ruby, please do it with *idempotent* side-effecting Ruby. Thank you.
|
18
18
|
|
data/docs/visualizations.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Visualizations
|
2
2
|
|
3
|
-
|
3
|
+
Bud programs compile naturally to dataflows, and dataflows have a natural graphical representation. Plotting a program as a graph can be useful to developers at various stages of program design, implementation and debugging. Bud predicate dependency graphs (or PDGs) are described on [[PDGs]].
|
4
4
|
|
5
|
-
|
5
|
+
Bud ships with two visualization utilities, __plotter__ and __visualizer__. Both use _GraphViz_ to draw a directed graph representing the program state and logic. __plotter__ provides a static analysis of the program, identifying sources and sinks of the dataflow, unconnected components, and points of order corresponding to logically nonmonotonic path edges. __visualizer__ is an offline debugging tool that analyses the trace of a (local) Bud execution and provides an interactive representation of runtime state over time.
|
6
6
|
|
7
7
|
## The Plotter
|
8
8
|
|
@@ -22,7 +22,7 @@ The __plotter__ is a visual static analysis tool that aids in design and early i
|
|
22
22
|
USAGE:
|
23
23
|
ruby budplot LIST_OF_FILES LIST_OF_MODULES
|
24
24
|
|
25
|
-
As its usage message indicates, __plotter__ expects a list of ruby input files, followed by a list of
|
25
|
+
As its usage message indicates, __plotter__ expects a list of ruby input files, followed by a list of Bud modules to mix in.
|
26
26
|
|
27
27
|
$ ruby budplot kvs/kvs.rb ReplicatedKVS
|
28
28
|
Warning: underspecified dataflow: ["my_id", true]
|
@@ -43,7 +43,7 @@ __ReplicatedKVS__ includes the __MulticastProtocol__ and __MembershipProtocol__
|
|
43
43
|
|
44
44
|
[[https://github.com/bloom-lang/bud/blob/master/util/budvis]]
|
45
45
|
|
46
|
-
To enable tracing, we need to set __:trace => true__ in the
|
46
|
+
To enable tracing, we need to set __:trace => true__ in the __Bud__ constructor, and optionally provide a __:tag__ to differentiate between traces by a human-readable name (rather than by object_id). I modified the unit test `test/tc_kvs.rb` as follows:
|
47
47
|
|
48
48
|
- v = BestEffortReplicatedKVS.new(@opts.merge(:port => 12345))
|
49
49
|
- v2 = BestEffortReplicatedKVS.new(@opts.merge(:port => 12346))
|
@@ -30,7 +30,7 @@ module TokenRing
|
|
30
30
|
bloom :pass_token do
|
31
31
|
# Persist the token for as long as necessary
|
32
32
|
token_persist <= token
|
33
|
-
token_persist <- join([token_persist, next_node]).map {|t,_| [t]}
|
33
|
+
token_persist <- join([token_persist, next_node]).map {|t,_| [t.loc]}
|
34
34
|
# Pass on the token
|
35
35
|
token <~ join([token_persist, next_node]).map {[next_node[[]].node]}
|
36
36
|
stdio <~ token.map {["#{ip_port}: Got token!"]}
|
data/lib/bud.rb
CHANGED
@@ -17,6 +17,8 @@ require 'bud/storage/tokyocabinet'
|
|
17
17
|
require 'bud/storage/zookeeper'
|
18
18
|
require 'bud/viz'
|
19
19
|
|
20
|
+
$em_stopped = Queue.new
|
21
|
+
|
20
22
|
# We monkeypatch Module to add support for Bloom state and code declarations.
|
21
23
|
class Module
|
22
24
|
|
@@ -165,7 +167,6 @@ module Bud
|
|
165
167
|
@budtime = 0
|
166
168
|
@inbound = []
|
167
169
|
@done_bootstrap = false
|
168
|
-
@em_stopped = Queue.new
|
169
170
|
@joinstate = {} # joins are stateful, their state needs to be kept inside the Bud instance
|
170
171
|
|
171
172
|
# Setup options (named arguments), along with default values
|
@@ -372,7 +373,7 @@ module Bud
|
|
372
373
|
if stop_em
|
373
374
|
schedule_shutdown(true)
|
374
375
|
# Wait until EM has completely shutdown before we return.
|
375
|
-
|
376
|
+
$em_stopped.pop
|
376
377
|
else
|
377
378
|
schedule_and_wait do
|
378
379
|
do_shutdown(false)
|
@@ -519,7 +520,7 @@ module Bud
|
|
519
520
|
q << true
|
520
521
|
end
|
521
522
|
# Executed only after EventMachine::stop_event_loop is done
|
522
|
-
|
523
|
+
$em_stopped << true
|
523
524
|
end
|
524
525
|
# Block waiting for EM's event loop to start up.
|
525
526
|
q.pop
|
data/lib/bud/bust/bust.rb
CHANGED
@@ -10,6 +10,7 @@ HTTP_VERBS = ["GET", "POST"] #, "DELETE"]
|
|
10
10
|
# a RESTful interface to Bloom code
|
11
11
|
module Bust
|
12
12
|
include Bud
|
13
|
+
attr_reader :bust_port
|
13
14
|
|
14
15
|
# used this for inspiration:
|
15
16
|
# http://blogs.msdn.com/b/abhinaba/archive/2005/10/14/474841.aspx
|
@@ -33,7 +34,7 @@ module Bust
|
|
33
34
|
BustClass.new(bud, q)
|
34
35
|
end
|
35
36
|
# Wait for socket to be ready before we return from bootstrap.
|
36
|
-
q.pop
|
37
|
+
@bust_port = q.pop
|
37
38
|
end
|
38
39
|
|
39
40
|
class BustClass
|
@@ -100,9 +101,11 @@ module Bust
|
|
100
101
|
|
101
102
|
def initialize(bud, q)
|
102
103
|
# allow user-configurable port
|
103
|
-
server = TCPServer.new(bud.ip, (bud.options[:bust_port] or
|
104
|
+
server = TCPServer.new(bud.ip, (bud.options[:bust_port] or 0))
|
105
|
+
port = server.addr[1]
|
106
|
+
puts "Bust server listening on #{bud.ip}:#{port}"
|
104
107
|
# We're now ready to accept connections.
|
105
|
-
q <<
|
108
|
+
q << port
|
106
109
|
|
107
110
|
loop do
|
108
111
|
session = server.accept
|
data/lib/bud/collections.rb
CHANGED
data/lib/bud/deploy/ec2deploy.rb
CHANGED
@@ -160,8 +160,8 @@ module EC2Deploy
|
|
160
160
|
raise "Couldn't open a PTY on #{t.node}" if !success
|
161
161
|
end
|
162
162
|
channel.exec("sudo gem update --no-ri --no-rdoc bud")
|
163
|
-
channel.wait
|
164
163
|
end
|
164
|
+
channel.wait
|
165
165
|
# Run the ruby_command
|
166
166
|
session.exec!('nohup ' + ruby_command[[]].cmd + ' ' + t.localip +
|
167
167
|
' ' + t.node + ' >metarecv.out 2>metarecv.err </dev/null &')
|
@@ -7,17 +7,27 @@ require 'bud/deploy/deployer'
|
|
7
7
|
module LocalDeploy
|
8
8
|
include Deployer
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def stop_bg
|
11
|
+
super
|
12
|
+
for p in @pids
|
13
|
+
Process.kill("INT", p)
|
14
|
+
end
|
15
|
+
Process.waitall
|
16
|
+
trap("CLD", "DEFAULT")
|
17
|
+
end
|
14
18
|
|
15
19
|
deploystrap do
|
20
|
+
trap("CLD") {
|
21
|
+
pid = Process.wait
|
22
|
+
puts "Child pid #{pid}: terminated"
|
23
|
+
}
|
24
|
+
|
16
25
|
read, write = IO.pipe
|
17
26
|
if node_count[[]]
|
18
27
|
print "Forking local processes"
|
28
|
+
@pids = []
|
19
29
|
(0..node_count[[]].num-1).map do |i|
|
20
|
-
Process.fork do
|
30
|
+
@pids << Process.fork do
|
21
31
|
# Don't want to inherit our parent's random stuff.
|
22
32
|
srand
|
23
33
|
foo = self.class.new
|
@@ -34,6 +44,8 @@ module LocalDeploy
|
|
34
44
|
(0..node_count[[]].num-1).map do |i|
|
35
45
|
node << [i, "localhost:" + read.readline.rstrip]
|
36
46
|
end
|
47
|
+
read.close
|
48
|
+
write.close
|
37
49
|
puts "done"
|
38
50
|
end
|
39
51
|
end
|
data/lib/bud/rewrite.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Peter Alvaro
|
@@ -18,7 +18,7 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2011-04-
|
21
|
+
date: 2011-04-07 00:00:00 -07:00
|
22
22
|
default_executable: rebl
|
23
23
|
dependencies:
|
24
24
|
- !ruby/object:Gem::Dependency
|