Actions and Service Invocations
Services and clients can perform simple actions, including invoking other services. Services and clients can invoke other services operations either synchronously or asynchronously. The main difference is that synchronous invocations will paused the task until a response is received.
Synchronous Invocations
Synchronous invocations—or "queries" in MAD— will wait for a response before to proceed. Note that MAD assumes non-blocking I/O, so the worker will be released but the execution of the code snippet will only continues once a reply has been received. See the example below:
client Browser {
every 10 {
query DB/Select
think 5
}
}
In this example, we define a client that synchronously invokes the operation DB/Select
and then thinks for 5 simulation
steps. In this case, the Browser will starts thinking only once it has received a reply (possibly an error) from the DB
service. If the invocation had been asynchronous, the Browser would have started to "think" as soon as it would have emitted
the request to the DB/Select
operation.
Timeouts
In practice, we do not want to wait forever for the response and we consider that a request has failed once we waited for a given time. The request has timed out. MAD lets you specify timeouts on (synchronous invocations only) as shown in the following example, where we limit the waiting time to 10 simulation steps.
client Browser {
every 5 {
query DB/Select {timeout: 10}
think 5
}
}
Asynchronous Invocations
Asynchronous invocations do not pause the caller, as opposed to synchronous ones (see above). They represent signals or messages where no response is expected. The caller therefore send the message and proceeds with the remaining actions.
client Browser {
every 10 {
invoke DB/Select
think 5
}
}
In this example for instance, every 10 simulation steps, the Browser
sends a request to the DB/Select
operations and
immediately starts thinking for 5 simulation steps, without waiting for any response.
Retry
Invocations be they synchronous or asynchronous may fail. By default, such a failure will abort the current task and propagates to the caller if this happens during the execution of an operation. It is possible however to 'wait and retry' using alternative back-off policies.
client Browser {
every 10 {
retry(limit=10) {
query DB/Select
}
}
}
In this example, the Browser will try to query to 'Select' operation of the 'DB' service, retry 10 if necessary. If all these ten attempts fails, the Browser will record a failure. By default, the Browser waits a constant time (10 simulation steps) before to retry, but we may also specify back-off policies (exponential of fibonnacci) as follows:
client Browser {
every 10 {
retry(limit=10, delay:exponential(5)) {
query DB/Select
}
}
}
Here, we specify an exponential backoff policy. The browser will first wait for 5 minutes and then increases the waiting time exponentially.
Ignoring Errors
It may be acceptable to simply ignore errors, because the actions are optional or because their completion do not matter.
MAD offers the ignore
primitive to delimit a block of actions where error will be ignored and the following instructions
will be executed.
client Browser {
every 5 do {
ignore {
invoke Logger/log_query
}
query DB/Select
}
}
In this example, when the asynchronous invocation invoke Logger/log_query
fails, MAD will just ignore the error and
proceed with the synchronous invocation query DB/Select
.
Thinking
Thinking is a blocking "No-op" operation. It emulate compute-intensive internal tasks. It accepts a fixed duration during which the underlying worker will be blocked.
service DB {
operation Select {
think 55
}
}
This example specifies that the select
operation will take 55 simulation steps.
Failing
Service may also fail, either systematically or with a given probability.
service DB {
operation Select {
fail 0.25
}
}
This example specifies that the select
operation may fail with 0.25 probability.