Comment on page
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.
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:
...
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.
ActionResult
s must be returned in the same order as actions order. - 2.EITHER full results are returned, with no
ActionResult.defer
s in the results, OR partial results are returned, and only the last entry is aActionResult.defer
.
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.Last modified 3mo ago