Automatic retry - Xenoage/RpcLib GitHub Wiki
Options for automatic retry
Normally, when an RPC command is called, it has a defined time period to execute. But when the timeout is hit (by default 30 seconds, but you can configure it individually), the function call throws an exception with a timeout failure and is not tried automatically again.
This can be changed for important commands, which should reach the other side as soon as it is online again, maybe even after restarting the computer. For this, the RpcRetryStrategy
enum can be used, which has the value None
by default. There are two other strategies:
-
RetryWhenOnline
: Runs the command as soon as the other peer is online again. When 10 commands with this flag are called, all 10 commands will be executed later in exactly this order. Example use case: A method to add credit to a bank account (when adding 5, 10 and 30 ct, at the very end the other peer should have received all 45 ct). See theIBankAccountRpc.AddMoney
function of the RetryOnClientTest demo project for a working example. -
RetryNewestWhenOnline
: Runs the command as soon as the other peer is online again, but only the newest call with the same method name is retried. Example use case: A method to change a specific configuration (when first setting the name to "MyFirstName", then to "MySecondName" and finally to "MyThirdName", only "MyThirdName" will be sent to the other peer as soon it is online again). See theIBankAccountRpc.ChangeOwnerName
function of the RetryOnClientTest demo project for a working example.
When the other peer is online again, first the commands in this queue will be sent, and after that the recently called "normal" commands will be sent (if their timeout was not already hit). This will ensure that the commands will arrive on the remote peer in the exact order in which they were originally called on the local peer.
Usage
Using these retry strategies is very simple. Just annotate the function in your interface with the RpcOptionsAttribute
, using the RetryStrategy
parameter. Here is an example, using a service for saving log messages on the remote peer:
interface IRemoteLogger : IRpcFunctions {
[RpcOptions(RetryStrategy = RpcRetryStrategy.RetryWhenOnline)]
Task SaveMessage(string message);
}
Return values
Retryable commands may also return values, but of course this works only when the call works at the first attempt. Imagine a retryable command which fails for an hour because of missing internet connection, and then finally works when the internet is back. There is no calling context any more, to which the return value could be delivered, so it is just ignored.
Persisting the command queue
The RPC engine needs some way to persist the remembered commands, but does not include an own database for this. So, when using one of the above strategies, an implementation of the IRpcCommandBacklog
interface has to be provided when initializing the RPC peer, i.e. when calling RpcMain.InitRpcServer
or RpcMain.InitRpcClient
. See DemoRpcCommandBacklog
for a very simple implementation using JSON files for persisting the queue.
In real world projects, it is recommended to save the commands in a database. The ascending ID
property and the TargetPeerID
property of the RpcCommand
class can be used as the composite primary key for each command.