Query idempotence
Quick overview
A request is idempotent if executing it multiple times leaves the database in the same state as executing it only once.
-
basic.request.default-idempotence
in the configuration (defaults to false). - can be overridden per statement Statement.setIdempotent or StatementBuilder.setIdempotence.
- retries and speculative executions only happen for idempotent statements.
For example:
-
update my_table set list_col = [1] where pk = 1
is idempotent: no matter how many times it gets executed,list_col
will always end up with the value[1]
; -
update my_table set list_col = [1] + list_col where pk = 1
is not idempotent: iflist_col
was initially empty, it will contain[1]
after the first execution,[1, 1]
after the second, etc.
Idempotence matters because the driver sometimes re-runs requests automatically:
retries: if we’re waiting for a response from a node and the connection gets dropped, the default retry policy automatically retries on another node. But we can’t know what went wrong with the first node: maybe it went down, or maybe it was just a network issue; in any case, it might have applied the changes already. Therefore non-idempotent requests are never retried.
speculative executions: if they are enabled and a node takes too long to respond, the driver queries another node to get the response faster. But maybe both nodes will eventually apply the changes. Therefore non-idempotent requests are never speculatively executed.
In most cases, you need to flag your statements manually:
SimpleStatement statement =
SimpleStatement.newInstance("SELECT first_name FROM user WHERE id=1")
.setIdempotent(true);
// Or with a builder:
SimpleStatement statement =
SimpleStatement.builder("SELECT first_name FROM user WHERE id=1")
.setIdempotence(true)
.build();
If you don’t, they default to the value defined in the configuration by the
basic.request.default-idempotence
option; out of the box, it is set to false
.
When you prepare a statement, its idempotence carries over to bound statements:
PreparedStatement pst = session.prepare(
SimpleStatement.newInstance("SELECT first_name FROM user WHERE id=?")
.setIdempotent(true));
BoundStatement bs = pst.bind(1);
assert bs.isIdempotent();
The query builder tries to infer idempotence automatically; refer to its manual for more details.