LorneCash
LorneCash

Reputation: 1614

ServiceStack Performance

Let me start by saying I love the design of ServiceStack as a client. (I've never used it for server side)

I'm writing a C# wrapper for API calls and I keep getting timeout and authentication errors. I've contacted the developers at the other end and they assure me that there are no issues on their end and that I must be doing something wrong. Normally I wouldn't believe them and I'd build a sample project to demonstrate the issue but in this case they pointed me to a web page that will test the same API I'm running in C# and they can re-authenticate as fast as they can click the submit button. I forget the exact site they use for testing but enough of my story... I'm sure I'm doing something wrong I just don't know what.

Here's my Unit Test. If I run it by itself or with one copy it works fine (150-1100ms) but if I make 3 or more copies of it they I will get only 2-3 that pass and the rest will timeout.

[TestMethod]
[Timeout(5000)]
public void Login_Success1()
{
    var client = new JsonServiceClient("apiurl");
    var response = client.Login("XXXAccessKeyXXX", "XXXSecretKeyXXX");

    //Assertions
}

This is my extension method:

public static class Extensions
{
    public static (bool Success, string Message, string Token) Login(this JsonServiceClient client, string accessKey, string secretKey)
    {
        try
        {
            var response = client.Post(new LoginRequest(accessKey, secretKey));
            var authorization = response.Headers.GetValues("Authorization")[0];
            return (true, string.Empty, authorization);
        }
        catch (Exception ex)
        {
            return (false, $"Authentication failed: {ex.Message}", string.Empty);
        }
    }
}

And here's the login request:

[Route("/sessions")]
[DataContract]
internal class LoginRequest
{
    internal LoginRequest(string accessKey, string secretKey)
    {
        AccessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey));
        SecretKey = secretKey ?? throw new ArgumentNullException(nameof(secretKey));
    }

    [DataMember(Name = "accessKey")]
    internal string AccessKey { get; set; }

    [DataMember(Name = "secretKey")]
    internal string SecretKey { get; set; }
}

I think this is all the relevant code but if you feel I missed something please lmk.

Upvotes: 1

Views: 366

Answers (1)

mythz
mythz

Reputation: 143359

Your Request DTO's should implement either IReturn<T> or IReturnVoid otherwise if you're sending just an object you will call the deprecated Post() method:

/// <summary>
/// APIs returning HttpWebResponse must be explicitly Disposed, e.g using (var res = client.Post(url)) { ... }
/// </summary>
[Obsolete("Use: using (client.Post<HttpWebResponse>(requestDto) { }")]
public virtual HttpWebResponse Post(object requestDto)
{
    return Send<HttpWebResponse>(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto);
}

Which because ServiceStack doesn't know how you want the Response deserialized it will return the open HttpWebResponse so you can inspect the Response yourself (as you're doing in your example). But this needs to be explicitly disposed as .NET's HttpWebRequest only allows a couple of concurrent requests open per domain which will cause your App to hang/timeout as it's waiting for Requests to be disposed to stay within the concurrent limit.

The preferred solution is to always annotate Request DTO's that you send with ServiceStack clients with a IReturn or a IReturn<T> interface marker, if it has none or you want to ignore the Response implement IReturnVoid otherwise implement IReturn<ResponseDtoType>:

class LoginRequest : IReturnVoid {}

Which instead calls the non-deprecated Post() method which disposes of the HttpWebResponse.

Otherwise if you want to send plain object DTO's you need to dispose of the HttpWebResponse after usage, e.g:

using (var response = client.Post<HttpWebResponse>(new LoginRequest(accessKey, secretKey)))
{
    var authorization = response.Headers.GetValues("Authorization")[0];
}

API's which implicitly return HttpWebResponse were deprecated to avoid hard to identify issues like this, instead we recommend using the explicit API above which declares the HttpWebResponse return type at the call-site so it's easier to identify it needs to be disposed.

Also note the ServiceStack Service Clients are opinionated for calling ServiceStack Services, for calling other Services we recommend using HTTP Utils instead.

Upvotes: 3

Related Questions