# Retry policy configuration

After a query fails the driver might decide to retry it based on its `Retry Policy` and the query itself.
Retry policy can be configured for `Session` or just for a single query.

## Retry policies

By default there are three retry policies:

* [Fallthrough Retry Policy](https://rust-driver.docs.scylladb.com/stable/retry-policy/fallthrough.md) - never retries, returns all errors straight to the user
* [Default Retry Policy](https://rust-driver.docs.scylladb.com/stable/retry-policy/default.md) - used by default, might retry if there is a high chance of success
* [Downgrading Consistency Retry Policy](https://rust-driver.docs.scylladb.com/stable/retry-policy/downgrading-consistency.md) - behaves as [Default Retry Policy](https://rust-driver.docs.scylladb.com/stable/retry-policy/default.md), but also,
  in some more cases, it retries **with lower `Consistency`**.

It’s possible to implement a custom `Retry Policy` by implementing the traits `RetryPolicy` and `RetrySession`.

## Changing the consistency level in a retry

The `RetryDecision::RetrySameTarget` and `RetryDecision::RetryNextTarget` variants can
request that the retry be executed with a different consistency level than the attempt
that failed.

#### WARNING
The [load balancing](https://rust-driver.docs.scylladb.com/stable/load-balancing/load-balancing.md) plan is computed once per
query, from the consistency level of the first attempt, and is **not** recomputed when
a retry changes that level. A load balancing policy that routes based on the consistency
level - for instance, one that keeps the plan within the local datacenter for
`LOCAL_QUORUM` - will therefore hand out targets picked for the original level. Retrying
on the next target with a changed consistency level may then send the query somewhere
that does not satisfy it.

Always choose RetryPolicy and LoadBalancingPolicy that don’t conflict in their behaviors
and assumptions regarding consistency. Our [Default Retry Policy](https://rust-driver.docs.scylladb.com/stable/retry-policy/default.md)
never changes consistency, so it should be safe to use with any LBP.

## Idempotence and retry policies

Retry policies and [speculative execution](https://rust-driver.docs.scylladb.com/stable/speculative-execution/speculative.md)
treat idempotence differently:

* **Speculative execution** strictly requires `is_idempotent = true`.
  Without it, speculative fibers do not start at all, even if a
  `SpeculativeExecutionPolicy` is configured on the `Session`.
* **Retry policies** receive `is_idempotent` as one input on `RequestInfo`
  and decide for themselves. They can - and the bundled policies do -
  retry some non-idempotent statements when the failure mode makes
  re-execution safe (for example, the [Default Retry Policy](https://rust-driver.docs.scylladb.com/stable/retry-policy/default.md)
  retries on `IsBootstrapping` and `UnableToAllocStreamId` regardless of
  idempotence, because the request is known not to have reached the
  server). Conversely, on errors where the request may have already
  partially executed (broken connection, `Overloaded`, `ServerError`,
  `TruncateError`), retries only happen when the statement is marked
  idempotent.

Marking a statement idempotent therefore unlocks speculative execution
entirely *and* widens the set of error classes the retry policies will act
on.

## Query idempotence

A query is idempotent if it can be applied multiple times without changing the result of the initial application

Specifying that a query is idempotent increases the chances that it will be retried in case of failure.
Idempotent queries can be retried in situations where retrying non idempotent queries would be dangerous.

Idempotence has to be specified manually, the driver is not able to figure it out by itself.

```rust
use scylla::statement::unprepared::Statement;
use scylla::statement::prepared::PreparedStatement;

// Specify that a Statement is idempotent
let mut my_query: Statement = Statement::new("SELECT a FROM ks.tab");
my_query.set_is_idempotent(true);


// Specify that a PreparedStatement is idempotent
let mut prepared: PreparedStatement = session
    .prepare("SELECT a FROM ks.tab")
    .await?;

prepared.set_is_idempotent(true);
```
