google-api-dotnet-client icon indicating copy to clipboard operation
google-api-dotnet-client copied to clipboard

Simplify customizing the HttpClient timeout

Open SergiyStoyan opened this issue 2 years ago • 3 comments

Hi,

I am experiencing this problem now. My google script is invoked by my app though Google.Apis.Script.v1 nuget package v1.57.0.2638. The script processes a google worksheet. From the google log it is seen that the script runs about 2 minutes (108-130 secs) and completes successfully meaning it returns a value which is logged. Also, it has status Completed in the log. Also, that the script is completed is also seen from the worksheet data. Nevertheless, on the client side the following exception is arisen:

A task was canceled. [System.Threading.Tasks.TaskCanceledException]

Module: CommonLanguageRuntimeLibrary

Stack:   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()    at Google.Apis.Requests.ClientServiceRequest`1.Execute() in C:\Apiary\2022-03-28.08-00-01\Src\Support\Google.Apis\Requests\ClientServiceRequest.cs:line 179

The client code is as follows:

 public object Run(string function, params object[] parameters)
        {
            ExecutionRequest request = new ExecutionRequest
            {
                Function = function,
                Parameters = parameters,
#if DEBUG
                DevMode = true,
#else
                DevMode = false,
#endif
            };
            ScriptsResource.RunRequest runRequest = service.Scripts.Run(request, ScriptId);
            Operation operation = null;
            for (int i = 0; ; i++)
                try
                {
                    operation = runRequest.Execute();
                    break;
                }
                catch (Google.GoogleApiException e)
                {
                    if (i >= RetryMaxCount)
                        throw;
                    if (e.Error?.Code != 500)
                        throw;
                    Log.Warning2("Retrying...", e);
                    System.Threading.Thread.Sleep(RetryDelayMss);
                }
            if (operation.Error != null)
            {
                string message = "Server error: " + operation.Error.ToStringByJson();
                throw new Exception(message);
            }
            return operation.Response["result"];
        }

The exception is thrown from here: runRequest.Execute(); Notice, it is not a GoogleApiException because otherwise it would be caught within the method.

I'm in Europe. A man in the USA using the same app experiences the same error. So, reasoning from the above facts, the error neither depends on the network nor is caused by timeout on the server. On the other hand, it arises only when the target worksheet has much data to process that takes more than 100 secs. When the same script runs less than 100 secs, no exception is thrown. Notice, 100 secs is a guessed number - I have no formal evidence that it is the timeout within Google.Apis.Script lib.

Please share your thoughts on the nature of this issue. Thank you!

SergiyStoyan avatar Oct 03 '22 20:10 SergiyStoyan

I strongly suspect that you're just seeing the default HttpClient timeout of 100s. You haven't shown us how you've initialized service, but that's where I'd expect you to set the timeout for the client. Unfortunately it doesn't look like this is trivial to do out of the box (and we may look at making this easier) but you could do something like this:

public sealed class TimeoutHttpClientInitializer : IConfigurableHttpClientInitializer
{
    private readonly IConfigurableHttpClientInitializer _other;
    private readonly TimeSpan _timeout;

    public TimeoutHttpClientInitializer(IConfigurableHttpClientInitializer other, TimeSpan timeout)
    {
        _other = other;
        _timeout = timeout;
    }

    public void Initialize(ConfigurableHttpClient httpClient)
    {
        _other?.Initialize(httpClient);
        httpClient.Timeout = _timeout;
    }
}

(For more general purpose use it would make sense to have a composite client initializer, and that's probably the route we'll take internally, but a single class will be simpler to get you up and running.)

jskeet avatar Oct 04 '22 08:10 jskeet

Exactly! What I did is just set

service.HttpClient.Timeout = new TimeSpan(0, 0, 0, 300);

where service is:

ScriptService service = new ScriptService(new BaseClientService.Initializer
{
    HttpClientInitializer = Credential,
    ApplicationName = applicationName,
});

Thank you for the clue!

(It would be nice to have Timeout exposed on the surface where it could be easily spotted. At least it would be a requirement to an 'easy-to-do' API around Google.Apis lib.)

SergiyStoyan avatar Oct 04 '22 22:10 SergiyStoyan

Marking as a feature request, we will look into srufacing a Timeout.

amanda-tarafa avatar Nov 15 '22 10:11 amanda-tarafa