SmartExecutor
The SmartExecutor service allows to execute "gCube Tasks" and monitor their execution status. Each instance of the SmartExecutor service can run the "gCube Tasks" related to the plugins available on such an instance.
Each instance of the SmartExecutor service publishes descriptive information about the co-deployed plugins.
Clients may interact with the SmartExecutor service through a library (SmartExecutor Client) of high-level facilities to simplify the discovery of available plugins in those instances. Each client can request to execute a "gCube Tasks" or getting informations about the state of their execution.
Contents
The Service
SmartExecutor service allows tasks execution through the use of co-deployed plugins. The service allows to pass inputs parameter to the plugin requested to run.
The execution is invoked every time it matches the scheduling parameters. The way to schedule the plugin execution is indicated by scheduling parameter (see Launch Inputs). Passing a null scheduling means a one shot execution, that is start immediately and just one time. Moreover, a new SmartExecutor instance could take care of a plugin when the node on which it was previously allocated crashes or is overloaded.
Plugins
At startup time the SmartExecutor discover the available plugins (using java.util.ServiceLoader). For each discovered plugin the SmartExecutor gather some mandatory information (e.g. version, name) and asks to the plugin to discover the capabilities of the machine. The discovered capabilities can be used by clients to select one instance instead of another (e.g. available python version, available R version, a certain hardware capability). The machine capabilities are plugin dependend so that is responsibility of the plugin to discover them.
The SmartExecutor publish the mandatory information and the discovered capabilities on IS using a ServiceEndpoint with Category: VREManagement and Name: SmartExecutor.
The SmartExecutor Client search for this service endpoints to retrieve available SmartExecutor instances running a certain plugin.
Client
The SmartExecutor service provides a client library to simplify the following procedures:
- discover service instances that can execute the target task. This requires interaction with the Information System.
- launch the execution of the task with one the discovered instances.
- monitor the execution of the running task.
In order to use the SmartExecutor client library, you have to declare this dependency in your pom.xml file
<dependency> <groupId>org.gcube.vremanagement</groupId> <artifactId>smart-executor-client</artifactId> </dependency>
You can now instanciate the SmartExecutorProxy object that will allow you to perform the operations cited above:
// instanciate the proxy (you need the plugin name) SmartExecutorProxy proxy = ExecutorPlugin.getExecutorProxy(YourSmartExecutorPlugin.NAME).build(); // launch the plugin passing the inputs it needs Map<String, Object> inputs = new HashMap<String, Object>(); ... // set the parameters (give the inputs, the scheduling options and so on) LaunchParameter parameter = new LaunchParameter(YourSmartExecutorPlugin.NAME, inputs); parameter.setScheduling(...); // launch (for example one shot) String uuidPluginLaunched = proxy.launch(parameter); // after a while you can check the status of the launch, for example PluginState state = proxy.getState(uuidPluginLaunched); switch(state){ case CREATED : ....; case DONE : ....; case FAILED : ....; case ... : ....; default : ....; }
Instance Discovery
SmartExecutor Client provide some facilities to get the SmartExecutor service instances.
It allows to retrieve one of the the running SmartExecutor services filtering them in the following way:
- By plugin name only (that's the case in the example shown above);
- By plugin name AND one or more of the following filters:
- KEY-VALUE list. Each KEY-VALUE must be found on the Service Endpoints.
- XQuery conditions to filter the Service Endpoints. The conditions are added to the standard query performed.
- List of endpoints to be selected within the available one.
In the latter cases you can use the following available functions:
public static ProxyBuilder<SmartExecutorProxy> getExecutorProxy(String pluginName, Tuple<String, String> ... tuples); public static ProxyBuilder<SmartExecutorProxy> getExecutorProxy(String pluginName, Tuple<String, String>[] tuples, ServiceEndpointQueryFilter serviceEndpointQueryFilter, EndpointDiscoveryFilter endpointDiscoveryFilter);
Using SmartExecutor Client a proxy for remote invocation can be obtained. Using this proxy is very easy to call a remote method as the method was local.
Managing Tasks
One of the API exposed by the SmartExecutor service is used to launch a plugin execution instance. Each time a task launch is executed the service (after some checks) create a separated thread, instantiate the related plugin and invoke the execution passing inputs parameters. The service return immediately after the task is launched (task is executed asynchronously) The string representation of the ID (an UUID) is retuned to the clients. That ID can be used to monitor the task execution.
Launch Inputs
The invocation parameters are:
- name of the plugin to be instantiated and run (MANDATORY)
- inputs to pass to plugin instance execution (MANDATORY)
- capabilities the plugin has to satisfy to correctly run an execution (e.g. the machine has a certain version of python interpreter) (OPTIONAL)
- scheduling strategy to launch the execution (e.g. every day at 22:00) (OPTIONAL)
- persist if the scheduling has to be persisted. If the SmartExecutor instance terminate another SmartExecutor must take in charge that scheduling. Due to a scheduling can survive over the time is strongly recommend to declare the needed capabilities.
Please note that the capabilities matches are checked before launching the plugin execution. This is a service check and is conceptually different from filtering in instance discovery using the client.
Monitor Task Execution
The ID returned invoking the launch can be used to monitor an execution.
The possible state for a task are:
- CREATED : The Task is created but not still running.
- RUNNING : The Task is running.
- STOPPED : The Task has been stopped.
- DONE : The Task terminated successfully.
- FAILED : The Task failed the execution.
- DISCARDED : The Task has been discarded by the scheduler. This happen only for repetitive or recurrent tasks and only when the launch parameter require that the previous task must be completed.
The are two API to monitor a task execution.
- The first one to retrieve the current state of the last execution.
- The second one to retrieve the current state of the nth execution (execution number).
The latter is used to monitor scheduled tasks. The execution number is used to identify the nth run launched respecting the scheduling strategy.
To monitor the run and die tasks the first API should be used. You could use the second API providing the value 1 for (execution number).
Requesting the state for en execution that has not been performed raise an exception. The methods invocation are the following for the two cases:
// retrieve the state about the last execution PluginState state = proxy.getState(uuidPluginLaunched); // retrieve the state about the n-th execution PluginState stateN = proxy.getState(uuidPluginLaunched, executionNumber);
Unschedule a Task Execution
There is an API to stop a running execution. When stopped the final state will be set to STOPPED'. If the task was already finished this method has no effect. The task to stop is identified by the ID returned from launch method.
This method stop the current execution, if any. If the plugin is scheduled to be repeated the method stops only the current execution. If this method is used on a Run and Die task the effect is stopping the current execution if any.
// stop (or at least try to stop) the execution of the plugin given its uuid boolean stopped = proxy.stop(uuidPluginLaunch);
There is also an advanced mechanism that takes into account the fact that the plugin schedule can be of type repeat and/or that another SmartExecutor node must take care of a plugin that was running on a node that went down or is overloaded.
// Unschedule the current execution and if the task was of type repeated, no other iteration will occur. // If the task was set to be Global, no other iteration will be launched on the current node, but another // SmartExecutor node will take care of them. boolean unscheduled = proxy.unschedule(uuidPluginLaunch); // unschedule globally (stop current iteration and the following ones) boolean unscheduled = proxy.unschedule(uuidPluginLaunch, true);
Plugin Development
The simplest way to start to develop a plugin is to checkout the HelloWorld plugin
Each plugin must declare:
- Name
- Version
- Description
Moreover the plugin is able to discover the machine capabilities it wants to publish, to allows clients to filters instances, by providing them as a key-value map
The mandatory information and the discovered capabilities are published in the ServiceEndpoint by the SmartExecutor service.
SmartExecutor plugins may have arbitrary size and dependencies.
Each plugin must implements at least two classes:
- The first one has to extend the org.gcube.vremanagement.executor.plugin.PluginDeclaration interface. This class is responsible of providing the mandatory information and discover the capabilities. this class is used by the SmartExecutor service just one time at application startup, to populate the ServiceEndpoint.
- The second class had to implement org.gcube.vremanagement.executor.plugin.Plugin<? extends PluginDeclaration> class. This class must implements two method. The first one responsible to launch the plugin execution with the provided input parameters. The second method is used to perform termination actions when an execution is stopped due to a client request.