A special situation to consider when calling VoltDB stored procedures is error handling. The VoltDB client interface catches most exceptions, including connection errors, errors thrown by the stored procedures themselves, and even exceptions that occur in asynchronous callbacks. These error conditions are not returned to the client application as exceptions. However, the application can still receive notification and interpret these conditions using the client interface.
The following sections explain how to identify and interpret errors that occur when executing stored procedures and in asynchronous callbacks. These include:
If an error occurs in a stored procedure (such as an SQL constraint violation), VoltDB catches the error and returns
information about it to the calling application as part of the ClientResponse
class. The
ClientResponse
class provides several methods to help the calling application determine whether the
stored procedure completed successfully and, if not, what caused the failure. The two most important methods are
getStatus()
and getStatusString()
.
static class MyCallback implements ProcedureCallback { @Override public void clientCallback(ClientResponse clientResponse) { final byte AppCodeWarm = 1; final byte AppCodeFuzzy = 2; if (clientResponse.getStatus() != ClientResponse.SUCCESS) { System.err.println(clientResponse.getStatusString()); } else { if (clientResponse.getAppStatus() == AppCodeFuzzy) { System.err.println(clientResponse.getAppStatusString()); }; myEvaluateResultsProc(clientResponse.getResults()); } } }
The
| |
If a | |
If you want the stored procedure to provide additional information to the calling application, there are two
more methods to the In the stored procedure, you can use the methods /* stored procedure code */ final byte AppCodeWarm = 1; final byte AppCodeFuzzy = 2; . . . setAppStatusCode(AppCodeFuzzy); setAppStatusString("I'm not sure about that..."); . . . |
One particular error that needs special handling is if a connection or a stored procedure call times out. By
default, the client interface only waits a specified amount of time (two minutes) for a stored procedure to complete. If
no response is received from the server before the timeout period expires, the client interface returns control to your
application, notifying it of the error. For synchronous procedure calls, the client interface returns the error
CONNECTION_TIMEOUT to the procedure call. For asynchronous calls, the client interface invokes the callback including the
error information in the clientResponse
object.
It is important to note that CONNECTION_TIMEOUT does not necessarily mean the synchronous procedure failed. In fact, it is very possible that the procedure may complete and return information after the timeout error is reported. The timeout is provided to avoid locking up the client application when procedures are delayed or the connection to the cluster hangs for any reason.
Similarly, if no response of any kind is returned on a connection (even if no transactions are pending) within the
specified timeout period, the client connection will timeout. When this happens, the connection is closed, any open stored
procedures on that connection are closed with a return status of CONNECTION_LOST, and then the client status listener
callback method connectionLost()
is invoked. Unlike a procedure timeout, when the connection times out,
the connection no longer exists, so your client application will receive no further notifications concerning pending
procedures, whether they succeed or fail.
CONNECTION_LOST does not necessarily mean a pending asynchronous procedure failed. It is possible that the procedure completed but was unable to return its status due to a connection failure. The goal of the connection timeout is to notify the client application of a lost connection in a timely manner, even if there are no outstanding procedures using the connection.
There are several things you can do to address potential timeouts in your application:
Change the timeout period by calling either or both the methods setProcedureCallTimeout()
and
setConnectionResponseTimeout()
on the ClientConfig
object. The default timeout
period is 2 minutes for both procedures and connections. You specify the timeout period in milliseconds, where a value
of zero disables the timeout altogether. For example, the following client code resets the procedure timeout to 90
seconds and the connection timeout period to 3 minutes, or 180 seconds:
config = new ClientConfig("advent","xyzzy"); config.setProcedureCallTimeout(90 * 1000); config.setConnectionResponseTimeout(180 * 1000); client = ClientFactory.createClient(config);
Catch and respond to the timeout error as part of the response to a procedure call. For example, the following code excerpt from a client callback procedure reports the error to the console and ends the callback:
static class MyCallback implements ProcedureCallback { @Override public void clientCallback(ClientResponse response) { if (response.getStatus() == ClientResponse.CONNECTION_TIMEOUT) { System.out.println("A procedure invocation has timed out."); return; }; if (response.getStatus() == ClientResponse.CONNECTION_LOST) { System.out.println("Connection lost before procedure response."); return; };
Set a status listener to receive the results of any procedure invocations that complete after the client interface times out. See the following Section 6.5.3, “Writing a Status Listener to Interpret Other Errors” for an example of creating a status listener for delayed procedure responses.
Certain types of errors can occur that the ClientResponse
class cannot notify you about
immediately. In these cases, an error happens and is caught by the client interface outside of the normal stored procedure
execution cycle. If you want your application to address these situations, you need to create a listener, which is a
special type of asynchronous callback that the client interface will notify whenever such errors occur. The types of
errors that a listener addresses include:
If a connection to the database cluster is lost or times out and there are outstanding asynchronous requests
on that connection, the ClientResponse
for those procedure calls will indicate that the
connection failed before a return status was received. This means that the procedures may or may not have completed
successfully. If no requests were outstanding, your application might not be notified of the failure under normal
conditions, since there are no callbacks to identify the failure. Since the loss of a connection can impact the
throughput or durability of your application, it is important to have a mechanism for general notification of lost
connections outside of the procedure callbacks.
If backpressure causes the client interface to wait, the stored procedure is never queued and so your application does not receive control until after the backpressure is removed. This can happen if the client applications are queuing stored procedures faster than the database cluster can process them. The result is that the execution queue on the server gets filled up and the client interface will not let your application queue any more procedure calls. Two ways to handle this situation programmatically are to:
Let the client pause momentarily to let the queue subside. The asynchronous client interface does this automatically for you.
Create multiple connections to the cluster to better distribute asynchronous calls across the database nodes.
An error can occur in an asynchronous callback after the stored procedure completes. These exceptions are also
trapped by the VoltDB client, but occur after the ClientResponse
is returned to the
application.
Procedure invocations that time out in the client may later complete on the server and return results. Since the client application can no longer react to this response inline (for example, with asynchronous procedure calls, the associated callback has already received a connection timeout error) the client may want a way to process the returned results.
For the sake of example, the following status listener does little more than display a message on standard output. However, in real world applications the listener would take appropriate actions based on the circumstances.
/* * Declare the status listener */ ClientStatusListenerExt mylistener = new ClientStatusListenerExt() { @Override public void connectionLost(String hostname, int port, int connectionsLeft, DisconnectCause cause) { System.out.printf("A connection to the database has been lost." + "There are %d connections remaining.\n", connectionsLeft); } @Override public void backpressure(boolean status) { System.out.println("Backpressure from the database " + "is causing a delay in processing requests."); } @Override public void uncaughtException(ProcedureCallback callback, ClientResponse r, Throwable e) { System.out.println("An error has occurred in a callback " + "procedure. Check the following stack trace for details."); e.printStackTrace(); } @Override public void lateProcedureResponse(ClientResponse response, String hostname, int port) { System.out.printf("A procedure that timed out on host %s:%d" + " has now responded.\n", hostname, port); } }; /* * Declare the client configuration, specifying * a username, a password, and the status listener */ ClientConfig myconfig = new ClientConfig("username", "password", mylistener); /* * Create the client using the specified configuration. */ Client myclient = ClientFactory.createClient(myconfig);
By performing the operations in the order as described here, you ensure that all connections to the VoltDB database cluster use the same credentials for authentication and will notify the status listener of any error conditions outside of normal procedure execution.
Declare a | |
The
| |
Define the client configuration | |
Create a client with the specified configuration. |