I have several extension methods for interacting with the Google Calendar API, and these all use an ExecuteWithRetry
function for wrapping API calls within retry logic:
private static readonly int maxAttempts = 4;
private static readonly int retryFirst = 5000;
private static readonly int retryExponent = 4;
private static async Task<TResponse> ExecuteWithRetryAsync<TResponse>(Func<Task<TResponse>> func)
{
TResponse response = default;
for (var attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
response = await func();
break;
}
catch (Google.GoogleApiException) when (attempt < maxAttempts)
{
var backoff = retryFirst * (int)Math.Pow(retryExponent, attempt - 1);
await Task.Delay(backoff);
}
}
return response;
}
public static async Task<IList<Event>> FetchAllWithRetryAsync(this EventsResource.ListRequest listRequest, DateTime? after = null, DateTime? before = null)
{
listRequest.TimeMin = after;
listRequest.TimeMax = before;
var pageStreamer = new PageStreamer<Event, EventsResource.ListRequest, Events, string>(
(request, token) => request.PageToken = token,
response => response.NextPageToken,
response => response.Items
);
return await ExecuteWithRetryAsync(() => pageStreamer.FetchAllAsync(listRequest, CancellationToken.None));
}
public static async Task<TResponse> ExecuteWithRetryAsync<TResponse>(this IClientServiceRequest<TResponse> request)
=> await ExecuteWithRetryAsync(() => request.ExecuteAsync());
public static async Task ExecuteWithRetryAsync(this BatchRequest request)
=> await ExecuteWithRetryAsync(async () => { await request.ExecuteAsync(); return Task.FromResult(0); });
General feedback welcome.
I'm particularly uneasy about the last function. Basically BatchRequest.ExecuteAsync()
returns a Task
rather than a Task<TResponse>
, so I'm using
async () => { await request.ExecuteAsync(); return Task.FromResult(0); }
for it to be accepted as a Func<Task<TResponse>>
. Is there a cleaner way of doing this?
Task.FromResult(0)
. \$\endgroup\$MyVoid
type (like unit), but I'm not sure that's better. \$\endgroup\$Task
directly, i.e.public static Task<TResponse> ExecuteWithRetryAsync<TResponse>(...) => ExecuteWithRetryAsync(() => request.ExecuteAsync());
\$\endgroup\$