Currently distal is not hosted in a maven repository, so in order to use it you have to build all the components locally. This means that you have to download (or clone) the distal-base
and distal-dsl
repositories, and call sbt publish-local
in both of them. If you require also the sbt plugins (e.g., for sbt distal-run-local
) you have to do the same with distal-sbtplugins
. Then you can use distal by including it as a library depency, just like any other scala-library:
libraryDependencies += "ch.epfl.lsr" %% "distal" % "0.1-SNAPSHOT"
Note that, because distal requires scala macros you’ll have to set scalaVersion := "2.10.1"
or similar.
As Distal targets fault-tolerant distributed algorithms, its DSL is designed to resemble to the pseudocode found in papers or books on the subject. As we target algorithms for message-passing systems, sending and receiving messages are the central elements of Distal.
Here’s a basic example
case class RoundMsg(rnd :Int) extends Message
class SimpleRoundBasedProtocol(val ID :String) extends DSLProtocol {
var round: Int = 0
val MAJORITY = ALL.size/2 + 1
UPON RECEIVING START DO {
s =>
round = round + 1
| SEND RoundMsg(round) TO ALL
}
UPON RECEIVING RoundMsg WITH {_.rnd < round} DO {
msg =>
| DISCARD msg
}
UPON RECEIVING RoundMsg WITH {_.rnd == round} TIMES MAJORITY DO {
msgs =>
round = round + 1
| SEND RoundMsg(round) TO ALL
| TRIGGER STATECHANGED() // round has changed
| DISCARD msgs
}
}
First we define a message (all messages need to extend Message
and have a companion object). The we define a protocol, where processes send messages to all others, and proceed to the next round once a majority has been received from the current round. Messages from previous rounds are discarded.
Note that messages from the future are not handled (yet), this is why we TRIGGER STATECHANGED()
in order to reevaluate guards for previously received (undiscarded) messages.
Rules basically look like this:
UPON RECEIVING <MESSAGE> <GUARD>* DO { <var> => <code> }
where <MESSAGE>
is a message type (or rather its companion object), each <GUARD>
is one of TIMES
, WITH
, and SAME
(see below), <var>
is a variable name that will be bound to the message (or set of messages) matched by this rule in the <code>
that follows, which is basically any Scala code or any of DiStaL’s actions (see below).
WITH
(MessageType => Boolean): If predicate is true, the rule matches the message(s). Similar to WHERE.TIMES
(=> Int): If the result of the computation equals the number of messages that match the other guards, the rule matches. Not really similar to COUNT.SAME
(MessageType => T): Extracts some type T
, will match for all messages having the same extracted value. Similar to GROUP BY.| SEND <message> TO <locations>
: Sends the message to all specified protocols.| TRIGGER <message>
: basically send to this, except that the message is put in the front of this protocols queue.| AFTER <time> DO { <code> }
, executes code after <time>
has passed, e.g., specified by 1(SECONDS)
, 5(MILLISECONDS)
.| DISCARD <messages>
: Mark this messages as not being used later (no later rule will match them).Examples can be found in the examples repository at github.