Lifecycle of an Action Handler

This page uses Java-specific code to illustrate general concepts that also apply when developing on Grainite with Python. You may refer to the API pages and App Configuration Options to see equivalent Python code.

There are 3 phases in the lifecycle of an Action Handler:

Initialization Phase

When an Action is received for a Grain, Grainite first initializes the class that contains the Action Handler. As part of this initialization, the initGrainiteConfig method is called along with a map of configs defined in the app YAML configuration.

For example, consider this app configuration:

app.yaml
...
  tables:
    - table_name: user_table
      key_type: string
      action_handlers:
        - type: java
          class_name: com.example.app.UserHandler
          config:
            foo: bar
            key: 123
          actions:
            - action_name: userEvent
              method_name: userEventHandler
...

There is an action handler for the class com.example.app.UserHandler which has a method to handle the userEvent action. In addition, the handler also has 2 configurations defined - foo and key.

When the userEvent action arrives for a Grain, Grainite will first initialize the com.example.app.UserHandler class and call the initGrainiteConfig with the defined config as part of the initialization:

initGrainiteConfig is an optional method that doesn't need to be defined.

Action handlers can access the config directly by using context.getConfig().

class UserHandler {
    public void initGrainiteConfig(Map<String, String> config) {
      String configFoo = config.get("foo"); // Equals "bar"
      String configKey = config.get("key"); // Equals "123"
    }
    
    public ActionResult userEventHandler(Action action, GrainContext context) {
      // Buisness logic to handler the `userEvent` Action.
    }
}

Execution Phase

After initialization is completed, Grainite will then call the userEventHandler method on the initialized object. The method is expected to return an ActionResult.

Since Grainite batches requests internally, it is very likely that there are multiple Actions that need to be called on the ActionHandler. In such cases, the previously initialized class object is called again for the rest of Actions.

Error Handling Phase

An Action goes through this phase only if an action handler method throws an exception. In this phase, Grainite will do the following:

  • Any changes made to the Grain state, as part of processing this action, will be rolled back.

    • Any and all pending messages queued as part of this action will not be sent.

  • If there is an exception handler associated with the Action, it will be invoked and the resulting ActionResult will be used for the Action.

  • If the action is NOT a SyncRequest, the action will be retried with an exponential backoff.

    • Since Grainite provides a per-key ordering guarantee, the rest of the actions won't be processed until this Action returns an ActionResult.

    • Additionally, this ActionHandler will be marked in a "paused" state until the failed Action returns an ActionResult.

  • If the action IS a SyncRequest, the invoker will receive a failure.

Last updated