1
votes

To test the use of triggers within DocumentDB, I:

  • Uploaded the sample metadata post trigger
  • Created a metadata document with the property "isMetadata": true
  • Executed some document writes with C# including a RequestOption with PostTriggerInclude = new List<string> { "updateMetadata" }

Unfortunately, the metadata file is sitting there with 0's blissfully unaffected by the trigger when my code is inserting documents. I verified the selection SQL in the trigger to make sure it should be able to find the metadata doc to edit it. There do not seem to be logs that I can use to investigate further.

How does one debug a DocumentDB trigger?

(and as the issue is probably PICNIC, is there anything obviously wrong with my setup?)

updateMetaData script

Metadata doc:

  {
    "id": "metadata",
    "isMetadata": true,
    "minSize": 0,
    "maxSize": 0,
    "totalSize": 0,
  }

Code snippet:

var docResp =   await client.CreateDocumentAsync(collectionUri, webhit,
                    new RequestOptions
                    {
                        PostTriggerInclude = new List<string> { "updateMetadata" }
                    }
                );
1

1 Answers

2
votes

In stored procedures or triggers, all the Error that the script throws will be transported back and translated into client side exceptions, with stack trace. So one way to debug the script is by using throw new Error(...) as a breakpoint in the script, stepping through the script and throwing at different points to validate that the code behaves as expected.

Assuming you are using partitioned collection. The triggers, just like stored procedures, are scoped in a single partition (https://azure.microsoft.com/en-us/documentation/articles/documentdb-programming/#database-program-transactions). Therefore, what the triggers can access and update must be within that partition where the triggers are running.

In your example, without the change of your trigger script, you will need to pre-create one metadata document for each partition key value. Then, to get the final statistics of the collection, you will need to query all the metadata documents (there will be more than one) and do your final aggregation across these metadata documents on the client side.

Another option is to auto-create the metadata document inside the trigger. This will avoid creating more than necessary metadata documents since you only need one metadata document per partition. Here is the my modification of your original script that does the creation:

   /**

    * This script runs as a trigger:

    * for each inserted document, look at document.size and update aggregate properties of metadata document: minSize, maxSize, totalSize.

    */

    function updateMetadata() {

        // HTTP error codes sent to our callback funciton by DocDB server.

        var ErrorCode = {

            RETRY_WITH: 449,

        }



        var collection = getContext().getCollection();

        var collectionLink = collection.getSelfLink();



        // Get the document from request (the script runs as trigger, thus the input comes in request).

        var doc = getContext().getRequest().getBody();



        // Check the doc (ignore docs with invalid/zero size and metaDoc itself) and call updateMetadata.

        if (!doc.isMetadata && doc.size != undefined && doc.size > 0) {

            getAndUpdateMetadata();

        }



        function getAndUpdateMetadata() {

            // Get the meta document. We keep it in the same collection. it's the only doc that has .isMetadata = true.

            var isAccepted = collection.queryDocuments(collectionLink, 'SELECT * FROM TestColl r WHERE r.isMetadata = true', function (err, feed, options) {

                if (err) throw err;

                var metaDoc;
                if (!feed || !feed.length) {
            // Create the meta doc for this partition, using the partition key value from the request document
                    metaDoc = {
                        "id": "metadata",
                        "isMetadata": true,
                        "minSize": 0,
                        "maxSize": 0,
                        "totalSize": 0,
                    }
                    metaDoc.partitionKey = doc.partitionKey;
                }
                else {
                    // Found the metadata document for this partition. So just use it
                    metaDoc = feed[0];
                }


                // Update metaDoc.minSize:

                // for 1st document use doc.Size, for all the rest see if it's less than last min.

                if (metaDoc.minSize == 0) metaDoc.minSize = doc.size;

                else metaDoc.minSize = Math.min(metaDoc.minSize, doc.size);



                // Update metaDoc.maxSize.

                metaDoc.maxSize = Math.max(metaDoc.maxSize, doc.size);



                // Update metaDoc.totalSize.

                metaDoc.totalSize += doc.size;



                // Update/replace the metadata document in the store.
                var isAccepted;
                if (!feed || !feed.length) {
            // Create the metadata document if it doesn't exist
                    isAccepted = collection.createDocument(collectionLink, metaDoc, function (err) {

                        if (err) throw err;

                        // Note: in case concurrent updates causes conflict with ErrorCode.RETRY_WITH, we can't read the meta again 

                        //       and update again because due to Snapshot isolation we will read same exact version (we are in same transaction).

                        //       We have to take care of that on the client side.

                    });
                }
                else {
            // Replace the metadata document
                    isAccepted = collection.replaceDocument(metaDoc._self, metaDoc, function (err) {

                        if (err) throw err;

                        // Note: in case concurrent updates causes conflict with ErrorCode.RETRY_WITH, we can't read the meta again 

                        //       and update again because due to Snapshot isolation we will read same exact version (we are in same transaction).

                        //       We have to take care of that on the client side.

                     });
                }

                if (!isAccepted) throw new Error("The call replaceDocument(metaDoc) returned false.");
            });

            if (!isAccepted) throw new Error("The call queryDocuments for metaDoc returned false.");

        }

    }

Hope this helps. Feel free to post back if you have further questions.

Thanks.

Lengning