Description:
Background
NdbApi supports batched execution of operations. Each operation can be given an error handling mechanism of either IgnoreError, or AbortOnError. Where AbortOnError is chosen, the transaction will be aborted if the operation has an error.
The NdbTransaction.execute() Api returns an integer rc. Iff the transaction aborts, the rc will be non-zero. If the transaction execution succeeds, but some operation(s) had ignored errors, the rc will be zero, but the transaction's error object (as returned by getNdbError()) will be set to the first operation error that occurred.
This results in a usage pattern where on execute() returning 0, the transaction's error object is checked to determine whether any operations had ignorable errors. If the transaction's error object is 'No error', then there were no errors.
Blobs
The Ndb implementation of Blobs uses extra control paths in the NdbApi execute() call to write the Blob header row and part row(s) separately. This involves multiple internal execute() calls. The nature of the Blob implementation is mostly hidden from users.
Problem
An operation batch is defined with some operations having the IgnoreError property, and some operations affecting Blobs. When executing, the IgnoreError operations hit ignorable errors. On return from execute(), the rc is 0, but the transaction's error object is also set to 'No Error'. It should be set to the first error received on an operation.
This anomaly can mean that code using NdbApi can miss operation failures where Blobs are included in the same batch, with application-specific effects.
Ndb replication uses IgnoreError operations to detect serialisation conflicts. Where Blobs are included in the same batch, these serialisation conflicts may not be properly handled.
How to repeat:
Create batch of will-fail IgnoreError operations before some Blob operations involving part table operations (e.g. > 255 bytes read/written). Execute and observe the setting of the NdbTransaction error object relative to the operation errors that occurred.
In general, if any executed operation has an error, then the transaction's error object should be set.
Suggested fix:
Modify Blob execute() layer code to avoid trampling / resetting transaction errors from other operations in the same batch.