7.2.1 Handling NDB API Errors

Abstract

This section describes how NDB API errors can be detected and mapped onto particular operations.

NDB API errors can be generated in either of two ways:

Errors raised during operation definition.  Errors generated during operation definition result in a failure return code from the method called. The actual error can be determined by examining the relevant NdbOperation object, or the operation's NdbTransaction object.

Errors raised during operation execution.  Errors occurring during operation execution cause the transaction of which they are a part to be aborted unless the AO_IgnoreError abort option is set for the operation.

Important

If you have worked with older versions of the NDB API, you should be aware that, beginning with MySQL Cluster NDB 6.2.0, the AbortOption type is a member of NdbOperation. See Section 2.3.24.1.1, “The NdbOperation::AbortOption Type”, for more information.

By default, read operations are run with AO_IgnoreError, and write operations are run with AbortOnError, but this can be overridden by the user. When an error during execution causes a transaction to be aborted, the execute() method returns a failure return code. If an error is ignored due to AO_IgnoreError being set on the operation, the execute() method returns a success code, and the user must examine all operations for failure using NdbOperation::getNdbError(). For this reason, the return value of getNdbError() should usually be checked, even if execute() returns success. If the client application does not keep track of NdbOperation objects during execution, then NdbTransaction::getNextCompletedOperation() can be used to iterate over them.

You should also be aware that use of NdbBlob can result in extra operations being added to the batches executed. This means that, when iterating over completed operations using getNextCompletedOperation(), you may encounter operations related to NdbBlob objects which were not defined by your application.

Note

A read whose LockMode is CommittedRead cannot be AbortOnError. In this case, it is always be IgnoreError.

In all cases where operation-specific errors arise, an execution error with an operation is marked against both the operation and the associated transaction object. Where there are multiple operation errors in a single NdbTransaction::execute() call, due to operation batching and the use of AO_IgnoreError, only the first is marked against the NdbTransaction object. The remaining errors are recorded against the corresponding NdbOperation objects only.

It is also possible for errors to occur during execution—such as a data node failure—which are marked against the transaction object, but not against the underlying operation objects. This is because these errors apply to the transaction as a whole, and not to individual operations within the transaction.

For this reason, applications should use NdbTransaction::getNdbError() as the first way to determine whether an NdbTransaction::execute() call failed. If the batch of operations being executed included operations with the AO_IgnoreError abort option set, then it is possible that there were multiple failures, and the completed operations should be checked individually for errors using NdbOperation::getNdbError().

Implicit NdbTransaction::execute() calls in scan and BLOB methods.  Scan operations are executed in the same way as other operations, and also have implicit execute() calls within the NdbScanOperation::nextResult() method. When NdbScanOperation::nextResult() indicates failure (that is, if the method returns -1), the transaction object should be checked for an error. The NdbScanOperation may also contain the error, but only if the error is not operation-specific.

Some BLOB manipulation methods also have implicit internal execute() calls, and so can experience operation execution failures at these points. The following NdbBlob methods can generate implicit execute() calls; this means that they also require checks of the NdbTransaction object for errors via NdbTransaction::getNdbError() if they return an error code:

Summary.  In general, it is possible for an error to occur during execution (resulting in a failure return code) when calling any of the following methods:

If this happens, the NdbTransaction::getNdbError() method should be called to identify the first error that occurred. When operations are batched, and there are IgnoreError operations in the batch, there may be multiple operations with errors in the transaction. These can be found by using NdbTransaction::getNextCompletedOperation() to iterate over the set of completed operations, calling NdbOperation::getNdbError() for each operation.

When IgnoreError has been set on any operations in a batch of operations to be executed, the NdbTransaction::execute() method indicates success even where errors have actually occurred, as long as none of these errors caused a transaction to be aborted. To determine whether there were any ignored errors, the transaction error status should be checked using NdbTransaction::getNdbError(). Only if this indicates success can you be certain that no errors occurred. If an error code is returned by this method, and operations were batched, then you should iterate over all completed operations to find all the operations with ignored errors.

Example (pseudocode).  We begin by executing a transaction which may have batched operations and a mix of AO_IgnoreError and AbortOnError abort options:

int execResult= NdbTransaction.execute(args);
Note

For the number and permitted values of args, see Section 2.3.29.2.4, “NdbTransaction::execute().

Next, because errors on AO_IgnoreError operations do not affect execResult—that is, the value returned by execute()—we check for errors on the transaction:

NdbError err= NdbTransaction.getNdbError();

if (err.code != 0)
{
    

An nonzero value for the error code means that an error was raised on the transaction. This could be due to any of the following conditions:

   if (execResult != 0)
   {
      

The transaction has been aborted. The recommended strategy for handling the error in this case is to test the transaction error status and take appropriate action based on its value:

      switch (err.status)
      {
        case value1:
          //  statement block handling value1 ...
        case value2:
          //  statement block handling value2 ...
          //  (etc. ...)
        case valueN:
          //  statement block handling valueN ...
      }

Since the transaction was aborted, it is generally necessary to iterate over the completed operations (if any) and find the errors raised by each only if you wish to do so for reporting purposes.

   }
   else
   {
      

The transaction itself was not aborted, but there must be one or more ignored errors. In this case, you should iterate over the operations to determine what happened and handle the cause accordingly.

   }
}

To handle a NdbScanOperation::nextResult() which returns -1, indicating that the operation failed (omitting cases where the operation was successful):

int nextrc= NdbScanOperation.nextResult(args);
      
Note

For the number and permitted values of args, see Section 2.3.28.2.6, “NdbScanOperation::nextResult().

if (nextrc == -1)
{

First, you should check the NdbScanOperation object for any errors:

  NdbError err= NdbScanOperation.getNdbError();

  if (err.code == 0)
  {
      

No error was found in the scan operation; the error must belong to the transaction as whole.

  }
    err= NdbTransaction.getNdbError();
      

Now you can handle the error based on the error status:

    switch (err.status)
    {
      case value1:
        //  statement block handling value1 ...
      case value2:
        //  statement block handling value2 ...
        //  (etc. ...)
      case valueN:
        //  statement block handling valueN ...
    }
}

For information about NDB API error classification and status codes, see Section 7.2.3, “NDB Error Classifications”. While you should not rely on a specific error code or message text in your NDB API applications—since error codes and messages are both subject to change over time—it can be useful to check error codes and messages to help determine why a particular failure occurred. For more information about these, see Section 7.2.2, “NDB Error Codes and Messages”. For more about NdbError and the types of information which can be obtained from NdbError objects, see Section 2.3.19, “The NdbError Structure”.