I think this is best to ask through example, and this official example will be good for illustration. We have 2 UDFs that operate on the same bin in a record:
function send(rec, message)
local messages = rec.messages
list.append(messages, message)
rec.messages = messages
aerospike:update(rec)
return list.size(messages)
end
function receive(rec)
local messages = rec.messages
rec.messages = list()
aerospike:update(rec)
return messages
end
Based on my understanding of Aerospike transactions (which is still lacking) I can’t determine if: the UDFs are treated atomically on the bin, or the individual operations in the UDF are atomic EG rec.messages
, aerospike:update(rec)
.
Assuming that only the individual operations are atomic, wouldn’t it be possible to potentially lose messages in this example? For example, assume that two different clients are each continually calling one of these methods on the same record. The interweaving of operations could be as such that this is the order of execution:
function receive(rec)
local messages = rec.messages
# (1) now that we have grabbed 'messages' as a local variable
# 'send' happens and stores a new message.
# make 'messages' an empty list, and save it.
rec.messages = list()
aerospike:update(rec)
# the value inserted at (1) is now lost?
return messages
end
The docs seem to indicate that these UDFs happen in an atomic fashion:
Multiple operations on a record that are directed from a client would not fall under the protection of a single transaction scope. However, using a Record UDF it is possible to perform extensive operations on a record in a single transaction.
This also indicates to me that they are atomic due to the usage of “Compare and Set”:
Moving the execution call into the database means complex operations can be done under the umbrella of a single transaction, such as doing a 2-transaction “Compare and Set” (CAS) mode of operation.
Though “2-transaction” in the above makes me doubt if this is the case.