silent
silent

Reputation: 16238

Azure Cosmos DB - check if item not exists without throwing error to Application Insights

I built a simple player-tracking API app in ASP.NET Core 3.1 that uses Azure Cosmos DB as its back end.

The API to create a new player entry first checks if an entry with the same ID under a given partition key already exists in Cosmos DB using this:

try
{
    ItemResponse<Player> response = await this._playerContainer.ReadItemAsync<Player>(playerid, new PartitionKey(partitionkey));
    return Conflict();
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
    // There is some more logic happening here so I cannot directly just call CreateItemAsync()... and return the Conflict response that this might return.
    ItemResponse<GameResult> response = await this._gameResultContainer.CreateItemAsync<GameResult>(gameresult, new PartitionKey(gameresult.PlayerId));
    // ...        
    return Accepted();
}

Only if this returns nothing, I go ahead and put the create request in a backend worker queue. Otherwise I return a 409-Conflict to the API caller.

The actual insert happens in an async backend worker. But I want to return to the API caller directly, if his insert will succeed.

All working fine so far. The issue I have is the following: As I am using the Azure Application Insights SDK, any call which does not find an existing item (which should be the normal case here), will automatically create a Error in AppInsights - even though I catch the exception in my code. That's obviously cluttering my logging quite a bit.

Any idea how I can get rid of that or generally how to change the API to get a better behavior for this?

Upvotes: 10

Views: 13202

Answers (2)

maf-soft
maf-soft

Reputation: 2552

You can use ReadManyItemsAsync<T>() instead. It just returns existing items.

static async Task<T> TryReadItemAsync<T>(this Container container,
    string id, PartitionKey partitionKey)
=> (await container.ReadManyItemsAsync<T>(
    new List<(string, PartitionKey)> { (id, partitionKey) })).FirstOrDefault();

Or for existence check for count != 0...

Upvotes: 1

Pavel Shastov
Pavel Shastov

Reputation: 3047

The issue is on the Cosmos DB .NET SDK side. It throws an exception if a document is not found. They can't really change this behavior because clients are relying on it. GitHub Issue

The suggested workaround is to use lower-level Stream API. This way you'll be able to handle 404 behavior on your side.

Something like this:

    using (ResponseMessage responseMessage = await container.ReadItemStreamAsync(
        partitionKey: new PartitionKey(partitionkey),
        id: playerid))
    {
        if (responseMessage.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            ...
            return Accepted();
        }

        if (responseMessage.IsSuccessStatusCode)
        {
            return Conflict();
        }
    }

There's sample code in the repo for doing custom deserialization

Upvotes: 11

Related Questions