Main page
For the cases of reliable communication with a Cartridge cluster under heavy load or in a case of some failure causing
unavailability of a part of the cluster nodes, the methods of client builder with prefix withRetrying
may be useful.
The request retry policy allows specifying the types of exceptions that may be retried.
By default, failed requests will be repeated only for some known network problems, such as
TimeoutException
, TarantoolConnectionException
and TarantoolInternalNetworkException
.
Some retry policies are available in the TarantoolRequestRetryPolicies
class, but you may use your own implementations.
If you want to use proxy calls or retry settings only for a number of requests, you may use configureClient(client)
in TarantoolClientFactory
for making a new configured client instance. Note, that the new instance will share the same
connection pool and basic client settings, and only augment the behavior of the client.
In this example I use custom delete function.
|
function delete_with_error_if_not_found(space_name, key, opts) |
|
local result, err = crud.delete(space_name, key, opts) |
|
if err then |
|
return nil, err |
|
end |
|
|
|
if #result.rows == 0 then |
|
return nil, "Records not found" |
|
end |
|
|
|
return result |
|
end |
You can set up any client. In this case I use CRUD client.
|
private TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> getCrudClient() { |
|
return TarantoolClientFactory.createClient() |
|
// You can connect to multiple routers |
|
.withAddresses( |
|
new TarantoolServerAddress(container.getRouterHost(), container.getMappedPort(3301)), |
|
new TarantoolServerAddress(container.getRouterHost(), container.getMappedPort(3302)), |
|
new TarantoolServerAddress(container.getRouterHost(), container.getMappedPort(3303)) |
|
) |
|
// For connecting to a Cartridge application, |
|
// use the value of cluster_cookie parameter in the init.lua file |
|
.withCredentials(USER_NAME, PASSWORD) |
|
// Number of connections per Tarantool instance |
|
.withConnections(10) |
|
// Specify using the default CRUD proxy operations mapping configuration |
|
.withProxyMethodMapping() |
|
.build(); |
|
} |
And reuse it then I need retrying client.
|
private TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> getRetryingTarantoolClient( |
|
TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> client) { |
|
|
|
retryingCounter = new AtomicInteger(); |
|
|
|
return TarantoolClientFactory.configureClient(client) |
|
// Configure a custom delete function |
|
.withProxyMethodMapping(builder -> builder.withDeleteFunctionName("delete_with_error_if_not_found")) |
|
// Set retrying policy |
|
// First parameter is number of attempts |
|
.withRetryingByNumberOfAttempts(5, |
|
// You can use default predicates from TarantoolRequestRetryPolicies for checking errors |
|
retryNetworkErrors() |
|
// Also you can use your own predicates and combine them with each other |
|
.or(e -> e.getMessage().contains("Unsuccessful attempt")) |
|
.or(e -> { |
|
retryingCounter.getAndIncrement(); |
|
return e.getMessage().contains("Records not found"); |
|
}) |
|
// Or with defaults |
|
.or(retryTarantoolNoSuchProcedureErrors()), |
|
// Also you can set delay in millisecond between attempts |
|
factory -> factory.withDelay(300) |
|
) |
|
.build(); |
|
} |
You don't have to set up basic client if you need retying client only.
All methods of client builder with prefix withRetrying
can be used with createClient
.
In this code I call delete_with_error_if_not_found
(custom delete function) before the record was inserted to the
database. So client recalls delete and removes the record after it was inserted.
|
// Creating multiple clients |
|
TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> crudClient = getCrudClient(); |
|
TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> retryingClient = |
|
getRetryingTarantoolClient(crudClient); |
|
|
|
TarantoolSpaceOperations<TarantoolTuple, TarantoolResult<TarantoolTuple>> space = |
|
crudClient.space(SPACE_NAME); |
|
space.truncate().join(); |
|
|
|
// Use TarantoolTupleFactory for instantiating new tuples |
|
TarantoolTupleFactory tupleFactory = new DefaultTarantoolTupleFactory( |
|
crudClient.getConfig().getMessagePackMapper()); |
|
|
|
// Call delete_with_error_if_not_found before record was inserted |
|
Conditions conditions = Conditions.equals(PK_FIELD_NAME, 1); |
|
CompletableFuture deleteFuture = retryingClient.space(SPACE_NAME).delete(conditions); |
|
retry(() -> assertEquals(1, retryingCounter.get())); |
|
space.insert(tupleFactory.create(1, null, "FIO", 50, 100)).join(); |
|
deleteFuture.join(); |
|
TarantoolResult<TarantoolTuple> selectResult = space.select(conditions).join(); |
|
assertEquals(0, selectResult.size()); |