2
votes

I try to update an document in Azure Functions when a CosmosDB document inserted/updated.

However when I update the document inside the function, the function is triggered again and causes an infinite loop.

private static DocumentClient _documentClient = new DocumentClient(new Uri(serviceEndpoint), key);

[FunctionName(nameof(MyFunction))]
public static async Task RunAsync([CosmosDBTrigger(
    databaseName: "MyDatabase",
    collectionName: "MyCollection",
    ConnectionStringSetting = "MyDbConnectionString",
    LeaseCollectionName = "leases",
    CreateLeaseCollectionIfNotExists = true,
    LeaseCollectionPrefix = nameof(MyFunction))]IReadOnlyList<Document> input,
    ILogger log)
{
    var replacementsTasks = new List<Task>();

    foreach (var item in input)
    {
        item.SetPropertyValue("Updated", true);
        replacementsTasks.Add(_documentClient.ReplaceDocumentAsync(item));
    }

    await Task.WhenAll(replacementsTasks);
}

How can I prevent this? Can I use an CosmosDB out result with the modified document from the trigger?

Update 1

I do not want to use another collection. This would double the pricing for CosmosDB. I tried the following with an CosmosDB in- and output. I get the same result however. Infinite loop.

[FunctionName(nameof(DownloadImages))]
public static void Run(
    [CosmosDBTrigger(
    databaseName: database,
    collectionName: collection,
    ConnectionStringSetting = connectionStringName,
    LeaseCollectionName = "leases",
    CreateLeaseCollectionIfNotExists = true,
    LeaseCollectionPrefix = nameof(MyFunction))]IReadOnlyList<Document> input,
    [CosmosDB(database, collection, Id = "id", ConnectionStringSetting = connectionStringName)] out dynamic document,
    ILogger log)
{

    if(input.Count != 1) throw new ArgumentException();

    document = input.Single();
    document.myValue = true;
}
2
You can create a Database with Shared RUs across its Containers.JoeBrockhaus

2 Answers

3
votes

If you cannot use another collection then there is really no other option. The Trigger effectively triggers when a new document is inserted or updated. If your Trigger is updating / inserting documents in the same collection it is monitoring, it will effectively create a loop.

This is like using a QueueTrigger and inserting messages in the same Queue, the infinite loop applies to any Trigger mechanism.

One thing you could do though, is to filter those already updated:

private static DocumentClient _documentClient = new DocumentClient(new Uri(serviceEndpoint), key);

[FunctionName(nameof(MyFunction))]
public static async Task RunAsync([CosmosDBTrigger(
    databaseName: "MyDatabase",
    collectionName: "MyCollection",
    ConnectionStringSetting = "MyDbConnectionString",
    LeaseCollectionName = "leases",
    CreateLeaseCollectionIfNotExists = true,
    LeaseCollectionPrefix = nameof(MyFunction))]IReadOnlyList<Document> input,
    ILogger log)
{
    var replacementsTasks = new List<Task>();

    foreach (var item in input)
    {
        if (!item.GetPropertyValue<bool>("Updated")) {
            item.SetPropertyValue("Updated", true);
            replacementsTasks.Add(_documentClient.ReplaceDocumentAsync(item));
        }
    }

    await Task.WhenAll(replacementsTasks);
}
0
votes

There is suggested way to do this mentioned in the following documentation https://docs.microsoft.com/en-us/azure/cosmos-db/serverless-computing-database

An Azure Cosmos DB trigger can be used with an output binding to a different Azure Cosmos DB container. After a function performs an action on an item in the change feed you can write it to another container (writing it to the same container it came from would effectively create a recursive loop). Or, you can use an Azure Cosmos DB trigger to effectively migrate all changed items from one container to a different container, with the use of an output binding.

Input bindings and output bindings for Azure Cosmos DB can be used in the same Azure Function. This works well in cases when you want to find certain data with the input binding, modify it in the Azure Function, and then save it to the same container or a different container, after the modification.