To customize the interval between invocations of the task's stored procedure, you create a custom task that implements the IntervalGenerator interface. The custom task consists of the Java class you create, and the SQL statements to load and declare the task. At run time, for each invocation of the task, VoltDB uses the custom class to determine how long to wait before invoking the stored procedure specified in the task definition.
Let's look at an example. Say we want a task, similar to the previous example, that periodically deletes unused sessions. The task can use the same stored procedure as before, this time with a fixed batch size passed in as an argument when declaring the task:
CREATE PROCEDURE PurgeOldSessions DIRECTED AS DELETE FROM session WHERE last_access < DATEADD(MINUTE,-5,NOW()) ORDER BY sessionID ASC LIMIT ?;
Now we want to customize how frequently the procedure runs — increasing the frequency if it always deletes the full batch, or decreasing the frequency if fewer records are deleted each time. To do this we create a custom task that checks how many records were actually deleted in each run. If the count of deleted records is less than 80% of the batch size, increase the interval between runs. On the other hand, if the number deleted records equals the batch size, increase the frequency by reducing the interval. Finally, we can use parameters to the custom task to specify the minimum, maximum, and starting frequency, as well as the batch size.
The first step is to write a Java class that implements the IntervalGenerator interface. This must be a static class whose constructor takes no arguments. In our example, we will also declare class variables for a helper object that is used in subsequent methods and minimum, maximum, and initial values for interval, plus the batch size.
The class must, at a minimum, declare or override three methods:
initialize() The | |
GetFirstInterval() The g | |
A callback method Finally, your custom task must have a callback method (the method you specify when creating the Interval
object), which is invoked when the task completes. The callback method must return another Interval, similar to
In our example, the callback compares the batch size to the number of records deleted by the last run and makes appropriate adjustments to the next interval. It increases the frequency by reducing the interval if the full batch was used, it decreases the frequency by extending the interval if less than 80% was used, and it keeps the interval within the specified minimum and maximum. Note that the callback method can be any name you choose. It does not have to be |
Example 9.2, “Custom Task Implementing IntervalGenerator” shows the completed example task class, with the key elements highlighted.
Example 9.2. Custom Task Implementing IntervalGenerator
package mytasks; import java.util.concurrent.TimeUnit; import org.voltdb.VoltTable; import org.voltdb.client.ClientResponse; import org.voltdb.task.*; public class PurgeIntervals implements IntervalGenerator { private TaskHelper helper; private long min, max, delta, batchsize; public void initialize(TaskHelper helper, long min, long max, long delta, long batch) { this.min = min; this.max = max; this.batchsize = batch; } public Interval getFirstInterval() { return new Interval(this.delta, TimeUnit.MILLISECONDS, this::callback); } /* * Callback to handle the result of the task * and return next Action. */ private Interval callback(ActionResult result) { /* Find out how many records were deleted */ ClientResponse response = result.getResponse(); VoltTable[] results = response.getResults(); long count = results[0].fetchRow(0).getLong(0); /* If less than 80%, increase by 10% */ if (count < this.batchsize * .8) this.delta += this.max * .1; /* If equal to batch size, decrease by 10% */ if (count == this.batchsize) this.delta -= this.max * .1; /* Stay within min & max */ if (this.delta > this.max) this.delta = this.max; if (this.delta < this.min) this.delta = this.min; return new Interval(this.delta, TimeUnit.MILLISECONDS, this::callback); } }
Once you complete your Java source code, you compile, debug, and package it into a JAR file the same way you compile
and package stored procedures. In fact, you can package tasks, procedures, and other classes (such as user-defined
functions) into a single or separate JARs depending on your application and operational needs. The following example
compiles the Java classes in the src/
folder and packages them into the JAR file
sessiontasks.jar
:
$ javac -classpath "/opt/voltdb/voltdb/*" \ -d ./obj src/*.java $ jar cvf sessiontasks.jar -C obj .
You then load the classes from the JAR file into VoltDB using the sqlcmd LOAD CLASSES directive:
LOAD CLASSES sessiontasks.jar;
Finally, once the custom class is loaded into the database, you can declare the task and start it running. You declare the task using the CREATE TASK statement, replacing the ON SCHEDULE static interval with FROM CLASS specifying the classpath of your new class. In our example, the custom task also requires four arguments: a minimum, maximum and starting interval, plus the batch size. (The same batch size passed to the stored procedure PurgeOldSessions.) The following statement creates the custom task with a minimum interval of 100ms (a tenth of a second), a maximum of 10 seconds, an initial interval of 1 second, and a batch size of 500 records.
CREATE TASK timecleanup ON SCHEDULE FROM CLASS mytasks.PurgeIntervals WITH (100,10000,1000,500) PROCEDURE PurgeOldSessions WITH (500) RUN ON PARTITIONS;
Because the stored procedure PurgeOldSessions is a directed procedure (that is, it runs separately on every partition on the cluster), the task must be declared to RUN ON PARTITIONS.
The task starts as soon as it is declared, unless you include the DISABLE clause. Alternately, you can use the ALTER TASK statement to change the state of the task. For example, the following statement disables our newly created task:
ALTER TASK timecleanup DISABLE;