yevhen / Streamstone

Event store for Azure Table Storage
Other
395 stars 64 forks source link

Exception could be improved for request rate exceeded on a cosmosdb backed table #41

Open johlrich opened 6 years ago

johlrich commented 6 years ago

If you hit your provisioned RU/s rate limit you'll currently get the following message:

An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll: 'One or more errors occurred.'
 Inner exceptions found, see $exception in variables window for more details.
 Innermost exception     Microsoft.WindowsAzure.Storage.StorageException : Element 0 in the batch returned an unexpected response code.

The exception has HttpStatusCode set to 429 it could be used to instead throw a nicer exception about rate limits + suggesting a retry.

The only bummer is that I don't see a way using the StorageException to get at the http response header x-ms-retry-after-ms which contains how long you must wait unless I'm missing something. Could still be better to have something explicit even with that?

yevhen commented 6 years ago

I'll have a look on a weekend. Should be doable.

The only bad thing is that I have absolutely no idea how to write a failing test for this scenario. Perhaps, we can try to create an explicit test which could be run manually against real CosmosDb (not emulator) .

jkonecki commented 6 years ago

CosmosDB emulator supports rate limiting - there is an option in the context menu of the tray icon to enable / disable rate limiting.

On Fri, 1 Dec 2017 22:31 Yevhen Bobrov, notifications@github.com wrote:

I'll have a look on a weekend. Should be doable.

The only bad thing is that I have absolutely no idea how to write a failing test for this scenario. Perhaps, we can try to create an explicit test which could be run manually against real CosmosDb (not emulator) .

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/yevhen/Streamstone/issues/41#issuecomment-348631823, or mute the thread https://github.com/notifications/unsubscribe-auth/ABsWj18gkxe1CB1EJZf3VmgTJ0676cysks5s8H5MgaJpZM4Qxyf0 .

yevhen commented 6 years ago

@jkonecki Oh, cool! Didn't know. I don't even have it installed on my machine ))

yevhen commented 6 years ago

@johlrich How do you connect to table api? Connection string format? Have you tried running against local cosmosdb emulator?

johlrich commented 6 years ago

Normal connection string format for Storage Tables, just different url worked against an account provisioned in the portal: DefaultEndpointsProtocol=https;AccountName=youraccountname;AccountKey=...;TableEndpoint=https://youraccountname.table.cosmosdb.azure.com:443/;

Unfortunately, I don't believe the emulator can be used at this time since it doesn't support the table endpoints.

johlrich commented 6 years ago

@yevhen For manual testing, folks without a current azure subscription should still be able to run the test with one of the time limited instances they started offering. https://azure.microsoft.com/en-us/try/cosmosdb/

As an aside, something I'm finding interesting about this approach is that you can still connect to the documentdb api through its endpoint (youraccountname.documents.azure.com) with the same key. When the API needs a Database/Collection name you supply the literal "TablesDB" for database and the tablename for collection. So far this has worked both with the DocumentDB sdk to dynamically tune provisioned RU/s throughput as well as subscribe to the table with the ChangeFeed support.

yevhen commented 6 years ago

I've checked it and I'm afraid that Azure SDK doesn't correctly handle the throttling scenario. What's interesting is that I've then inspected (with Fiddler) the api response and it also doesn't contain this information. Moreover, the response status code is still 202 (Accepted) while 429 is mentioned in the body 😞

Looks like CosmosDb Table api support is half-baked at this moment. @johlrich you need to open an issue against Cosmos, since SS doesn't do anything destructive to error information returned by the service.

jkonecki commented 6 years ago

You can obtain HTTP status code of the throttled requests:

Please note Console.WriteLine(ex.RequestInformation.HttpStatusCode);

        [Test]
        public async Task When_hitting_request_limit_during_writes()
        {
            const int streamsToWrite = 1000; // 800RU the default limit for CosmosDb table

            await Task.WhenAll(Enumerable.Range(1, streamsToWrite).Select(async streamIndex =>
            {
                var partition = new Partition(table, $"test-RU-limit-on-writes-stream-{streamIndex}");
                var stream = new Stream(partition);

                var events = Enumerable.Range(1, 10)
                    .Select(CreateEvent)
                    .ToArray();

                try
                {
                    await Stream.WriteAsync(stream, events);
                }
                catch (StorageException ex)
                {
                    Console.WriteLine(ex.RequestInformation.HttpStatusCode);

                    Console.WriteLine(ex);
                    if (Debugger.IsAttached)
                        Environment.Exit(-1);
                }
            }));
        }