6.3. Invoking Stored Procedures Asynchronously

Documentation

VoltDB Home » Documentation » Using VoltDB

6.3. Invoking Stored Procedures Asynchronously

Calling stored procedures synchronously simplifies the program logic because your client application waits for the procedure to complete before continuing. However, for high performance applications looking to maximize throughput, it is better to queue stored procedure invocations asynchronously.

Asynchronous Invocation

To invoke stored procedures asynchronously, use the callProcedureAsync() method. The procedure call will be queued internally for transmission. The immediate return value is a standard Java CompletableFuture object, which will be "completed" when the procedure completes (or an error occurs). For example, to invoke a NewCustomer() stored procedure asynchronously, the call to callProcedureAsync() might look like the following:

CompletableFuture<ClientResponse> future =
    client.callProcedureAsync("NewCustomer",
                              firstname,
                              lastname,
                              custID};

To handle the eventual ClientResponse, you can use any of the features of CompletableFuture that Java provides. These include awaiting completion with get(), declaring a handler for the eventual completion with handle(), and so on.

The following are other important points to note when making asynchronous invocations of stored procedures:

  • Calls to callProcedureAsync() return control to the calling application as soon as the procedure call is locally queued.

  • Errors that occur before the procedure call is queued may be reported via a Java exception. The calling application should include appropriate handling.

  • Once the procedure is queued, any subsequent error (such as an exception in the stored procedure itself or loss of connection to the database) is returned via the CompletableFuture.

  • There is a limit on the local queue size, after which calls will be rejected. The default queue size is 1000 calls, but this can be changed with Client2Config. Robust applications should either ensure they can never exceed the local queue size, or implement appropriate handling. You can configure a handler to receive notifications when the queue is approaching capacity; see the requestBackpressureHandler() method of Client2Config.

  • If the database server queue is full, transmission is temporarily suspended. This condition, known as network backpressure (distinct from request-queue backpressure), is handled internally to the Client2 API. This situation does not normally happen unless the database cluster is not scaled sufficiently for the workload, or there are abnormal spikes in the workload. See Section 6.6.3, “Writing Handlers to Interpret Other Errors” for more information.

Using CompletableFuture with Asynchronous Calls

The CompletableFuture is "completed normally" when the called procedure has been executed on the cluster, and has returned a response. It can also be completed, in this case "exceptionally", when an error or timeout occurs.

Normal completion allows access to a ClientResponse structure, the same structure that is returned in a synchronous invocation. The ClientResponse contains information about the results of execution. In particular, the methods getStatus() and getResults() let you determine whether the stored procedure was successful and evaluate the results of the procedure.

Exceptional completion does not have a ClientResponse structure; the exception itself conveys the error information.

Completions themselves can be processed synchronously or asynchronously, using the standard facilities of CompletableFuture. The VoltDB Java client is single threaded, so synchronous completions are processed one at a time. Consequently, it is good practice to keep synchronous completion processing to a minimum, returning control to the main thread as soon as possible. If more complex processing is required, use one of the available methods for handling the completion asynchronously.