EfficientDynamoDb
EfficientDynamoDb copied to clipboard
Feature request: Provide a way to avoid throwing `ConditionalCheckFailedException`
When performing a conditional write (i.e., when specifying a condition expression for PutItem
, UpdateItem
or DeleteItem
), the
ExecuteAsync
method currently throws a ConditionalCheckFailedException
if the condition is not met. However, exceptions are quite slow in .NET because it is assumed that exceptions are exceptional - i.e., do not occur very often. However, that is not always the case when performing a conditional write.
For example, I have an application that writes data in batches, where each batch has a timestamp, and each record is only written if the timestamp of the existing record is earlier than that of the record being written. Furthermore, a single batch can require writing hundreds of thousands of records. If a batch fails towards the end and is re-executed, then most of the records in the batch will be rewritten and will fail the check condition.
Making matters more challenging is that exceptions are REALLY slow when debugging (over 100x slower in my experience), so I often have to wait 10-20 minutes or so on each debugging session before the condition I have breakpointed actually occurs because I'm waiting for all the prior database write operations to complete that fail their check conditions because the record being written was already previously written in the last run.
Therefore, it would be great if the library provided an option to handle condition check failures without throwing an exception. One way to achieve this would be to add a method (e.g., SuppressThrowing
) to the relevant request builders that modified the return type of the ExecuteAsync
method to be a result object (e.g., Task<PutItemResult>
), where the result object could then be inspected to determine the success/failure of the operation.
The result object could also have a convenience method (e.g., ThrowIfFailed
or EnsureSuccess
) that throws an exception if an error occurred. This would allow callers to check the object for any conditions they want to explicitly handle and then optionally invoke the EnsureSuccess
method.
So, a PutItem
operation might look like:
var result = await _ddbContext.PutItem().WithItem(ddbItem)
.WithCondition(filter => Joiner.Or(
filter.On(item => item.Timestamp).NotExists(),
filter.On(item => item.Timestamp).LessThan(ddbItem.Timestamp)))
.SuppressThrowing()
.ExecuteAsync(cancellationToken);
if (result.ExceptionType != ExceptionType.ConditionalCheckFailedException)
{
result.EnsureSuccess();
}