Links
Comment on page

Actions and Action Handlers

Head over to GrainContext API to get a more detailed view of the context API.

What are Actions and Action Handlers?

An Action is a message that can be processed by Action Handlers. These messages can be topic events, API calls, or Grain to Grain messages. The entire list of the types of Actions can be found here.
An Action Handler is a method that contains user defined behavior to handle Actions. Action Handlers can contain two types of methods:
Grainite provides the following platform guarantees (and constraints) while processing each Action:
  • Actions are processed in order, per Key (action source). There is no ordering across keys.
  • The action handler has single threaded access to the Grain’s state.
  • The action handler can read/modify the Grain's state.
  • The action handler can send asynchronous messages to other grains or topics.
  • The action handler can access external systems, make queries, calls etc. In case of failure, the handler could be re-executed, and any external side effects repeated.
  • All side effects: changes to self-state, and async messages sent out; are atomic - either all will (eventually) happen, or none will.
Side-effects to external systems are excluded from atomicity guarantee.

Defining a Single Action Handler

Action Handler methods may be per-action, or batch-oriented. Here is the method signature for a per-action handler named handlerMethodName:
Java
Python
public ActionResult handlerMethodName(Action action, GrainContext context) {
...
}
def handler_method_name(action: Action, context: GrainContext) -> ActionResult:
...

Defining a Batch Action Handler

Here is the method signature for a batch action handler named handlerMethodName:
Java
Python
public List<ActionResult> handlerMethodName(List<Action> actions,
GrainContext context) {
...
}
def handler_method_name(actions: list, context: GrainContext)-> list[ActionResult]:
...
Requirements for batch Action Handlers:
  1. 1.
    ActionResults must be returned in the same order as actions order.
  2. 2.
    EITHER full results are returned, with no ActionResult.defers in the results, OR partial results are returned, and only the last entry is a ActionResult.defer.

Accessing Grain value and updating it within the Action Handler

Java
Python
// Grain value type.
public GrainValue implements Serializable {
...
}
public ActionResult handlerMethodName(Action action, GrainContext context) {
// If no previous value exists, this will create a default GrainValue.
GrainValue currentValue = context.getValue().asType(GrainValue.class);
// Process the action and make changes to the currentValue object.
...
// Finally write the currentValue object back to the Grain.
context.setValue(Value.of(currentValue));
}
def handler_method_name(action: Action, context: GrainContext) -> ActionResult:
# We should use the appropriate as_<type>() function depending on the
# the type we expect the underlying value to have.
# I.e. as_bytes(), as_dict(), as_double() (float), as_list(), as_long() (int), or as_string()
current_value = context.get_value().as_list()
# Process the action and make changes to the current_value object.
...
# Finally write the current_value object back to the Grain.
context.set_value(Value(current_value))
The Python API has no equivalent function for the Value.asType()method in the Java API, since asType()expects an object that implements Serializable, whereas interfaces are not a part of normal Python code. Value in Python can instead handle any of types specified in the Python documentation.