Describe the bug
The MS docs state that when ReadItemAsync is ran on an entity that does not exist, a 404 exception should be returned. Link to MS docs.
In my testing, no exception is thrown but the CosmosItemResponse<T> is returned with an Resource = null and a StatusCode = 404.
To Reproduce
Have Cosmos SQL API Collection named tweets with partitionKey /subjectId.
using Microsoft.Azure.Cosmos;
using cosmos.test.Models;
using Xunit;
namespace cosmos.test
{
public class CosmosTest
{
private const string _endpoint = "{endpoint}";
private const string _authkey = "{authkey}";
private const string _database = "{database}";
[Fact]
public async void ReadFail()
{
var client = new CosmosClient(_endpoint, _authkey);
await Assert.ThrowsAsync<CosmosException>(async () =>
{
await client.Databases[_database].Containers["tweets"].Items.ReadItemAsync<Tweet>("foo", "not-a-real-id");
});
}
}
}
Expected behavior
An exception of type CosmosException with StatusCode = 404 should be thrown.
Actual behavior
No exception is thrown.
Environment summary
SDK Version: 3.0.0.9-preview
OS Version (e.g. Windows, Linux, MacOSX) MacOSX
Additional context
$ dotnet test
Build started, please wait...
Build completed.
Test run for /Users/mpalumbo7/Code/tweet-api/test/cosmost.test/bin/Debug/netcoreapp2.2/tweet.api.test.dll(.NETCoreApp,Version=v2.2)
Microsoft (R) Test Execution Command Line Tool Version 15.9.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
[xUnit.net 00:00:06.0785620] cosmos.test.CosmosTest.ReadFail [FAIL]
Failed cosmos.test.CosmosTest.ReadFail
Error Message:
Assert.Throws() Failure
Expected: typeof(Microsoft.Azure.Cosmos.CosmosException)
Actual: (No exception was thrown)
Stack Trace:
at cosmos.test.CosmosTest.ReadFail() in /Users/mpalumbo7/Code/tweet-api/test/cosmos.test/CosmosTest.cs:line 20
Total tests: 1. Passed: 0. Failed: 1. Skipped: 0.
Test Run Failed.
Test execution time: 6.7763 Seconds
In v3 it was designed to not throw an exception for 404. Not finding an item is a valid scenario for most workloads. Throwing an exception is rather expensive, and can hurt performance. The documentation needs to be updated.
I figured as much. I鈥檒l close the issue since a PR is now opened to fix the documentation.
Hi @j82w , I am facing the opposite problem here, I am using 3.1.1 (downgraded to 3.0.0 with same results), and ReadItemAsync actually throws an exception instead of returning a ItemResponse as described on the sample documentation. Any ideas?
Thank you so much.
Claiton
Hi @claitonlovato ,
This issue was created while the SDK was still in preview. Please checkout issue #513. Because of this issue and several other users reporting the same issue it was decided to make 404 throw. It also makes it easier to migrate from v2 SDK.
If you don't want it to throw you can use the stream APIs.
Thank you so much for the quick reply @j82w !
Ok, I understand.
Not sure I agree with the change back, as you mentioned throwing an exception is quite expensive for a completely valid scenario.
The only thing is that now the documentation is wrong again, like this page that shows a tutorial using the check against the response StatusCode: https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-dotnet-application
Kind regards,
Claiton
I created an issue for the document to be updated. If performance is a real concern then it is recommended to use the stream APIs. They don't throw any exceptions, and allows for other optimizations like only deserializing when necessary.
@j82w can we have this as an option in RequestOptions or a separate flag? Performance is a concern and to use stream APIs we need to pass serializer everywhere. It would probably improve ever so slightly if CosmosClient.ClientOptions.Serializer would be set to a default one, rather than kept null, if nothing specified, but having it in RequestOption will allow us to reuse it for all of the read APIs, not just the document.
Agreed with @nahk-ivanov - or provide an overload like ReadItemOptionalAsync<T>. For what it's worth, I do prefer an approach like HttpClient which leaves to the developer the choice of calling a validation method such as ItemResponse<T>.EnsureSuccess().
Most helpful comment
@j82w can we have this as an option in
RequestOptionsor a separate flag? Performance is a concern and to use stream APIs we need to pass serializer everywhere. It would probably improve ever so slightly ifCosmosClient.ClientOptions.Serializerwould be set to a default one, rather than keptnull, if nothing specified, but having it inRequestOptionwill allow us to reuse it for all of the read APIs, not just the document.