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
data/docs/rebl.md ADDED
@@ -0,0 +1,99 @@
1
+ REBL stands for "Read Eval Bud Loop" and is Bud's REPL. Running REBL by typing in "rebl" causes the "rebl>" prompt to appear. Input is either a collection definition, Bud statement, or REBL command. REBL commands are prefixed by "/"; all other input is interpreted as Bud code. If the input begins with either "table", "scratch" or "channel" (the currently-supported collection types), then the input is processed as a collection definition. Otherwise, the input is processed as a Bud rule.
2
+
3
+ Rules are not evaluated until a user enters one of the evaluation commands; either "/tick [x]" (tick x times, or once if optional argument x is not specified), or "/run" (runs in the background until a breakpoint is hit, execution stops, or a user inputs "/stop"). Rules may be added or deleted at any time, and collections may similarly be added at any time.
4
+
5
+ A breakpoint is a Bud rule that has the "breakpoint" scratch on its left-hand-side. The "/run" command will stop executing at the end of any timestep where a "breakpoint" tuple was derived. Another invocation of "/run" will continue executing from the beginning of the next timestep.
6
+
7
+ Let's step through two examples to better understand REBL. The first example is a centralized all-pairs-all-paths example in a graph, the second example is a distributed ping-pong example. These examples illustrate the use of the commands, which can be listed from within rebl by typing "/help".
8
+
9
+
10
+ # Shortest Paths Example
11
+
12
+ Let's start by declaring some collections. "link" represents directed edges in a graph. "path" represents all pairs of reachable nodes, with the next hop and cost of the path.
13
+
14
+ rebl> table :link, [:from, :to, :cost]
15
+ rebl> table :path, [:from, :to, :next, :cost]
16
+
17
+ "/lscollections" confirms that the collections are defined.
18
+
19
+ rebl> /lscollections
20
+ 1: table :link, [:from, :to, :cost]
21
+ 2: table :path, [:from, :to, :next, :cost]
22
+
23
+ We now define some rules to populate "path" based on "link".
24
+
25
+ rebl> path <= link {|e| [e.from, e.to, e.to, e.cost]}
26
+ rebl> temp :j <= (link*path).pairs(:to => :from)
27
+ rebl> path <= j { |l,p| [l.from, p.to, p.from, l.cost+p.cost] }
28
+
29
+ Furthermore, we decide to print out paths to stdout.
30
+
31
+ rebl> stdio <~ path.inspected
32
+
33
+ We provide some initial data to "link".
34
+
35
+ rebl> link <= [['a','b',1],['a','b',4],['b','c',1],['c','d',1],['d','e',1]]
36
+
37
+ Ticking prints out the paths to stdout.
38
+
39
+ rebl> /tick
40
+ ["a", "c", "b", 5]
41
+ ["a", "e", "b", 4]
42
+ ["a", "b", "b", 1]
43
+ ["b", "d", "c", 2]
44
+ ["a", "c", "b", 2]
45
+ ["a", "d", "b", 6]
46
+ ["b", "e", "c", 3]
47
+ ["b", "c", "c", 1]
48
+ ["a", "d", "b", 3]
49
+ ["c", "e", "d", 2]
50
+ ["a", "e", "b", 7]
51
+ ["a", "b", "b", 4]
52
+ ["d", "e", "e", 1]
53
+ ["c", "d", "d", 1]
54
+
55
+ This might be a bit annoying though. Let's try to remove that rule using "/rmrule". First, we need to figure out which rule it is, using "/lsrules".
56
+
57
+ rebl> /lsrules
58
+ 5: link <= [['a','b',1],['a','b',4],['b','c',1],['c','d',1],['d','e',1]]
59
+ 1: path <= link {|e| [e.from, e.to, e.to, e.cost]}
60
+ 2: temp :j <= (link*path).pairs(:to => :from)
61
+ 3: path <= j { |l,p| [l.from, p.to, p.from, l.cost+p.cost] }
62
+ 4: stdio <~ path.inspected
63
+
64
+ Looks like it's rule 4.
65
+
66
+ rebl> /rmrule 4
67
+
68
+ Note how ticking no longer prints out the paths.
69
+
70
+ rebl> /tick
71
+
72
+
73
+ # Ping-Pong Example
74
+
75
+ We begin by starting up two instances of REBL on the same machine in different terminal windows. Take note of the port number printed when REBL starts up. For example, we might have:
76
+
77
+ REBL1 : "Listening on localhost:33483"
78
+ REBL2 : "Listening on localhost:48183"
79
+
80
+ In each REBL, let us now define the following collections and rules:
81
+
82
+ rebl> channel :ping, [:@dst, :src]
83
+ rebl> ping <~ ping.map {|p| [p.src, p.dst]}
84
+
85
+ In REBL1, type in an initial ping to start things off:
86
+
87
+ rebl> ping <~ [["localhost:48183", ip_port]]
88
+
89
+ Note that no messages are exchanged until we either type "/tick [x]" or "/run". Note that this program will run forever, as pings will contiuously be exchanged. Let's set a breakpoint so both REBLs break once they've received their first ping. In both REBLs, type:
90
+
91
+ rebl> breakpoint <= ping
92
+
93
+ Note that the schema of "breakpoint" is unimportant.
94
+
95
+ Now, let us "/run" both REBLs. At a leisurely pace, type in "/run" to each REBL. Hmm, what happened? Type "/dump ping" on each REBL to see if it got a ping packet:
96
+
97
+ rebl> /dump ping
98
+
99
+ Pings are no longer being exchanged. Of course, if you want to remove a breakpoint, it is as simple as using "/lsrule" and "/rmrule x", where x is the rule ID shown by "/lsrule". If you type a "/run" command and want to stop execution, simply type the "/stop" command.
@@ -0,0 +1,19 @@
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 to and from Bloom-land and Ruby-land. If nothing else, this allows the Ruby process hosting the Bud evaluator to monitor its progress.
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 `run` method or `run_bg` method. Since the Bud runtime typically runs indefinitely, the blocking `run` 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
+
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
+
8
+ * The Bud module provides a Ruby method called `sync_do`, which takes a block of code, and hands it to the Bud runtime for execution at the end of a timestep. The Bud runtime is blocked during this execution, so the state of all Bud collections is fixed for the duration of the block of Ruby code. This is also a blocking call for the caller. Bud also supplies `async_do`, which hands off the code block to the Bud runtime to be executed, but returns immediately to the caller without waiting for the runtime.
9
+
10
+ * The Bud module provides a Ruby method called `register_callback`. Given the name of a Bud collection, this method arranges for the given block of Ruby code to be invoked at the end of any timestep in which any tuples have been inserted into the specified collection. The code block is passed the collection as an argument; this provides a convenient way to examine the items inserted during that timestep. Note that because the Bud runtime is blocked while the callback is invoked, it can also examine any other Bud state freely.
11
+
12
+ ## Bud and Ruby-driven Side Effects ##
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
+
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-effecting Ruby code within a Bloom block may be called an *arbitrary* number of times during a single Bloom timestep, as it works its way to fixpoint.
16
+
17
+ If you must pollute your Bloom statements with side-effecting Ruby, please do it with *idempotent* side-effecting Ruby. Thank you.
18
+
19
+ In future versions of the Bud runtime we plan to limit the Ruby that gets invoked in the Bloom context, and keep you safe from these tendencies. (As with a lot of the Bloom design, we hope to do this in a way that won't make you feel handicapped, just gently cradled in disorderly goodness.)
@@ -0,0 +1,75 @@
1
+ # Visualizations
2
+
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
+
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
+
7
+ ## The Plotter
8
+
9
+ [[https://github.com/bloom-lang/bud/blob/master/util/budplot]]
10
+
11
+ The __plotter__ is a visual static analysis tool that aids in design and early implementation. The most common uses of the __plotter__ are:
12
+
13
+ 1. Visual sanity checking: does the dataflow look like I expected it to look?
14
+ 2. Ensuring that a particular set of mixins is fully specified: e.g., did I forget to include a concrete implementation of a protocol required by other modules?
15
+ The Bloom module system, abstract interfaces and concrete implementations are described in more detail in [[modules]].
16
+ 3. Identifying dead code sections
17
+ 4. Experimenting with different module compositions
18
+ 5. Identifying and iteratively refining a program's points of order
19
+
20
+
21
+ $ ruby budplot
22
+ USAGE:
23
+ ruby budplot LIST_OF_FILES LIST_OF_MODULES
24
+
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
+
27
+ $ ruby budplot kvs/kvs.rb ReplicatedKVS
28
+ Warning: underspecified dataflow: ["my_id", true]
29
+ Warning: underspecified dataflow: ["add_member", true]
30
+ Warning: underspecified dataflow: ["send_mcast", true]
31
+ Warning: underspecified dataflow: ["mcast_done", false]
32
+ fn is ReplicatedKVS_viz_collapsed.svg
33
+ $ open -a /Applications/Google\ Chrome.app/ ReplicatedKVS_viz_collapsed.svg
34
+
35
+ __ReplicatedKVS__ includes the __MulticastProtocol__ and __MembershipProtocol__ protocols, but does not specify which implementation of these abstractions to use. The program is underspecified, and this is represented in the resulting graph (ReplicatedKVS_viz_collapsed.svg) by a node labeled "??" in the dataflow.
36
+
37
+ $ ruby budplot kvs/kvs.rb ReplicatedKVS BestEffortMulticast StaticMembership
38
+ fn is ReplicatedKVS_BestEffortMulticast_StaticMembership_viz_collapsed.svg
39
+ $ open -a /Applications/Google\ Chrome.app/ ReplicatedKVS_BestEffortMulticast_StaticMembership_viz_collapsed.svg
40
+
41
+
42
+ ## The Visualizer
43
+
44
+ [[https://github.com/bloom-lang/bud/blob/master/util/budvis]]
45
+
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
+
48
+ - v = BestEffortReplicatedKVS.new(@opts.merge(:port => 12345))
49
+ - v2 = BestEffortReplicatedKVS.new(@opts.merge(:port => 12346))
50
+
51
+ + v = BestEffortReplicatedKVS.new(@opts.merge(:tag => 'dist_primary', :port => 12345, :trace => true))
52
+ + v2 = BestEffortReplicatedKVS.new(@opts.merge(:tag => 'dist_backup', :port => 12346, :trace => true))
53
+
54
+
55
+ Then I ran the unit test:
56
+
57
+ $ ruby test/tc_kvs.rb
58
+ Loaded suite test/tc_kvs
59
+ Started
60
+ .Created directory: TC_BestEffortReplicatedKVS_dist_primary_2160259460_
61
+ Created directory: TC_BestEffortReplicatedKVS_dist_primary_2160259460_/bud_
62
+ Created directory: TC_BestEffortReplicatedKVS_dist_backup_2159579740_
63
+ Created directory: TC_BestEffortReplicatedKVS_dist_backup_2159579740_/bud_
64
+ ..
65
+ Finished in 4.366793 seconds.
66
+
67
+ 3 tests, 14 assertions, 0 failures, 0 errors
68
+
69
+ Then I ran the visualization utility:
70
+
71
+ $ ruby budvis TC_BestEffortReplicatedKVS_dist_primary_2160259460_/
72
+
73
+ And finally opened the (chronological) first output file:
74
+
75
+ $ open -a /Applications/Google\ Chrome.app/ TC_BestEffortReplicatedKVS_dist_primary_2160259460_/tm_0_expanded.svg
data/examples/README ADDED
@@ -0,0 +1 @@
1
+ A few simple examples are included here, but for more detailed and useful examples please see the [bud-sandbox](http://github.com/bloom-lang/bud-sandbox) repository.
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bud'
3
+
4
+ class HelloWorld
5
+ include Bud
6
+
7
+ bloom do
8
+ stdio <~ [["hello world!"]]
9
+ end
10
+ end
11
+
12
+ HelloWorld.new.tick