This topic explains how to execute a function in VMware Tanzu GemFire.
Before starting this procedure, define your members and regions where you want to run functions.
Main tasks:
gfsh
deploy
command to deploy the JAR file containing the function code. Deploying the JAR automatically registers the function for you. See Register the Function Automatically by Deploying a JAR for details. Alternatively, you can write the XML or application code to register the function. See Register the Function Programmatically for details.ResultsCollector
implementation and use it in your function execution.To write the function code, you implement the Function
interface in the org.apache.geode.cache.execute
package.
Code the methods you need for the function. These steps do not have to be done in this order.
getId
to return a unique name for your function. You can use this name to access the function through the FunctionService
API.For high availability:
isHa
to return true to indicate to Tanzu GemFire that it can re-execute your function after one or more members failshasResult
to return trueCode hasResult
to return true if your function returns results to be processed and false if your function does not return any data - the fire and forget function.
optimizeForWrite
to return false if your function only reads from the cache, and true if your function updates the cache. The method only works if, when you are running the function, the Execution
object is obtained through a FunctionService
onRegion
call. optimizeForWrite
returns false by default.DATA:WRITE
, implement an override of the Function.getRequiredPermissions()
method. See Authorization of Function Execution for details on this method.execute
method to perform the work of the function.
execute
thread safe to accommodate simultaneous invocations.execute
to accommodate multiple identical calls to the function. Use the RegionFunctionContext
isPossibleDuplicate
to determine whether the call may be a high-availability re-execution. This boolean is set to true on execution failure and is false otherwise.
NoteThe
isPossibleDuplicate
boolean can be set following a failure from another member’s execution of the function, so it only indicates that the execution might be a repeat run in the current member.
ResultSender
object for passing results back to the originator, and function arguments provided by the member where the function originated.FunctionContext
, which is automatically extended to RegionFunctionContext
if you get the Execution
object through a FunctionService
onRegion
call.RegionFunctionContext
holds the Region
object, the Set
of key filters, and a boolean indicating multiple identical calls to the function, for high availability implementations.PartitionRegionHelper
provides access to additional information and data for the region. For single regions, use getLocalDataForContext
. For colocated regions, use getLocalColocatedRegions
.
NoteWhen you use
PartitionRegionHelper.getLocalDataForContext
,putIfAbsent
may not return expected results if you are working on local data set instead of the region.
execute
method. Tanzu GemFire transmits the exception back to the caller as if it had been thrown on the calling side. See the Java API documentation for FunctionException
for more information.Example function code:
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.RegionFunctionContext;
import org.apache.geode.cache.partition.PartitionRegionHelper;
public class MultiGetFunction implements Function {
public void execute(FunctionContext fc) {
if(! (fc instanceof RegionFunctionContext)){
throw new FunctionException("This is a data aware function, and has
to be called using FunctionService.onRegion.");
}
RegionFunctionContext context = (RegionFunctionContext)fc;
Set keys = context.getFilter();
Set keysTillSecondLast = new HashSet();
int setSize = keys.size();
Iterator keysIterator = keys.iterator();
for(int i = 0; i < (setSize -1); i++)
{
keysTillSecondLast.add(keysIterator.next());
}
for (Object k : keysTillSecondLast) {
context.getResultSender().sendResult(
(Serializable)PartitionRegionHelper.getLocalDataForContext(context)
.get(k));
}
Object lastResult = keysIterator.next();
context.getResultSender().lastResult(
(Serializable)PartitionRegionHelper.getLocalDataForContext(context)
.get(lastResult));
}
public String getId() {
return getClass().getName();
}
}
When you deploy a JAR file that contains a Function (in other words, contains a class that implements the Function interface), the Function will be automatically registered via the FunctionService.registerFunction
method.
To register a function by using gfsh
:
gfsh
prompt. If necessary, start a locator and connect to the cluster where you want to run the function.At the gfsh prompt, type the following command:
gfsh>deploy --jar=group1_functions.jar
where group1_functions.jar corresponds to the JAR file that you created in step 1.
Note: When deploying a JAR, the name of the JAR cannot contain any of the following characters: *
, !
, #
, @
, ?
.
If another JAR file is deployed (either with the same JAR filename or another filename) with the same Function, the new implementation of the Function will be registered, overwriting the old one. If a JAR file is undeployed, any Functions that were auto-registered at the time of deployment will be unregistered. Since deploying a JAR file that has the same name multiple times results in the JAR being un-deployed and re-deployed, Functions in the JAR will be unregistered and re-registered each time this occurs. If a Function with the same ID is registered from multiple differently named JAR files, the Function will be unregistered if either of those JAR files is re-deployed or un-deployed.
See Deploying Application JARs to VMware Tanzu GemFire Members for more details on deploying JAR files.
This section applies to functions that are invoked using the Execution.execute(String functionId)
signature. When this method is invoked, the calling application sends the function ID to all members where the Function.execute
is to be run. Receiving members use the ID to look up the function in the local FunctionService
. In order to do the lookup, all of the receiving member must have previously registered the function with the function service.
The alternative to this is the Execution.execute(Function function)
signature. When this method is invoked, the calling application serializes the instance of Function
and sends it to all members where the Function.execute
is to be run. Receiving members deserialize the Function
instance, create a new local instance of it, and run execute from that. This option is not available for non-Java client invocation of functions on servers.
Your Java servers must register functions that are invoked by non-Java clients. You may want to use registration in other cases to avoid the overhead of sending Function
instances between members.
Register your function using one of these methods:
XML:
<cache>
...
</region>
<function-service>
<function>
<class-name>com.bigFatCompany.tradeService.cache.func.TradeCalc</class-name>
</function>
</function-service>
Java:
myFunction myFun = new myFunction();
FunctionService.registerFunction(myFun);
NoteModifying a function instance after registration has no effect on the registered function. If you want to execute a new function, you must register it with a different identifier.
This section assumes that you have already followed the steps for writing and registering the function.
In every member where you want to explicitly execute the function and process the results, you can use the gfsh
command line to run the function or you can write an application to run the function.
Running the Function Using gfsh
At the gfsh prompt, type the following command:
gfsh> execute function --id=function_id
Where function_id equals the unique ID assigned to the function. You can obtain this ID using the Function.getId
method.
See Function Execution Commands for more gfsh
commands related to functions.
Running the Function via API Calls
FunctionService
on*
methods to create an Execute
object. The on*
methods, onRegion
, onMembers
, etc., define the highest level where the function is run. For colocated partitioned regions, use onRegion
and specify any one of the colocated regions. The function run using onRegion
is referred to as a data dependent function - the others as data-independent functions.Use the Execution
object as needed for additional function configuration. You can:
Set
to withFilters
to narrow the execution scope for onRegion
Execution
objects. You can retrieve the key set in your Function
execute
method through RegionFunctionContext.getFilter
.setArguments
. You can retrieve these in your Function
execute
method through FunctionContext.getArguments
.ResultCollector
Call the Execution
object to execute
method to run the function.
getResult
from the results collector returned from execute
and code your application to do whatever it needs to do with the results.
NoteFor high availability, you must call the
getResult
method.
Example of running the function - for executing members:
MultiGetFunction function = new MultiGetFunction();
FunctionService.registerFunction(function);
writeToStdout("Press Enter to continue.");
stdinReader.readLine();
Set keysForGet = new HashSet();
keysForGet.add("KEY_4");
keysForGet.add("KEY_9");
keysForGet.add("KEY_7");
Execution execution = FunctionService.onRegion(exampleRegion)
.withFilter(keysForGet)
.setArguments(Boolean.TRUE)
.withCollector(new MyArrayListResultCollector());
ResultCollector rc = execution.execute(function);
// Retrieve results, if the function returns results
List result = (List)rc.getResult();
This topic applies to functions that return results.
When you execute a function that returns results, the function stores the results into a ResultCollector
and returns the ResultCollector
object. The calling application can then retrieve the results through the ResultCollector
getResult
method. Example:
ResultCollector rc = execution.execute(function);
List result = (List)rc.getResult();
Tanzu GemFire’s default ResultCollector
collects all results into an ArrayList
. Its getResult
methods block until all results are received. Then they return the full result set.
To customize results collecting:
Write a class that extends ResultCollector
and code the methods to store and retrieve the results as you need. Note that the methods are of two types:
addResult
and endResults
are called by Tanzu GemFire when results arrive from the Function
instance SendResults
methodsgetResult
is available to your executing application (the one that calls Execution.execute
) to retrieve the resultsUse high availability for onRegion
functions that have been coded for it:
ResultCollector
clearResults
method to remove any partial results data. This readies the instance for a clean function re-execution.getResult
method. This enables the high availability functionality.In your member that calls the function execution, create the Execution
object using the withCollector
method, and passing it your custom collector. Example:
Execution execution = FunctionService.onRegion(exampleRegion)
.withFilter(keysForGet)
.setArguments(Boolean.TRUE)
.withCollector(new MyArrayListResultCollector());
To execute a data independent function on a group of members or one member in a group of members, you can write your own nested function. You will need to write one nested function if you are executing the function from client to server and another nested function if you are executing a function from server to all members.