{"id":3352,"date":"2021-09-14T07:00:36","date_gmt":"2021-09-14T14:00:36","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cosmosdb\/?p=3352"},"modified":"2021-09-09T15:32:17","modified_gmt":"2021-09-09T22:32:17","slug":"getting-started-end-to-end-example-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cosmosdb\/getting-started-end-to-end-example-2\/","title":{"rendered":"Getting started with Azure Cosmos DB \u2013 end to end example, part 2"},"content":{"rendered":"<p>This is the second in a two-part series in which we walk through the steps for realizing your first Azure Cosmos DB implementation. In this post, we will optimize the queries we created in <a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/getting-started-end-to-end-example-1\/\" target=\"_blank\" rel=\"noopener\">part 1<\/a>.<\/p>\n<p>The table below shows the RUs consumed for each query.<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>Query<\/strong><\/td>\n<td><strong>Loops<\/strong><\/td>\n<td><strong>Items returned<\/strong><\/td>\n<td><strong>RU consumption<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>6<\/td>\n<td>5001<\/td>\n<td>208<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>1<\/td>\n<td>10<\/td>\n<td>3<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>6<\/td>\n<td>5002<\/td>\n<td>191<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>1<\/td>\n<td>1<\/td>\n<td>3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><span style=\"font-size: 18pt;\">Performance tuning<\/span><\/h2>\n<p>Now that we have created the first version of our application, our next step is optimizing RU consumption. The first step in this process is to optimize the Indexing policy which is set on the container level. The default Indexing Policy indexes all the property paths of our JSON.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-3353\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-graphical-user-interface-des-1.png\" alt=\"A picture containing graphical user interface Description automatically generated\" width=\"399\" height=\"383\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-graphical-user-interface-des-1.png 777w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-graphical-user-interface-des-1-300x288.png 300w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-graphical-user-interface-des-1-768x737.png 768w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-graphical-user-interface-des-1-24x24.png 24w\" sizes=\"(max-width: 399px) 100vw, 399px\" \/><\/p>\n<p>This has a negative impact on write operations since index management also consumes RUs. To optimize the Indexing Policy we need to examine which property paths are used as filters in our read queries. Only these property paths will be included in the policy as you can see below.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-3354\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-text-description-automatical-1.png\" alt=\"A picture containing text Description automatically generated\" width=\"308\" height=\"379\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-text-description-automatical-1.png 593w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/08\/a-picture-containing-text-description-automatical-1-244x300.png 244w\" sizes=\"(max-width: 308px) 100vw, 308px\" \/><\/p>\n<p>We now repeat the Insert operation which initially consumed 9 RUs. With the optimized Index Policy, this operation now takes 7 RUs &#8211; saving 2 RUs per item written to Azure Cosmos DB. This may seem not much, but in scenarios where an application is writing thousands of items every minute, optimizing the Index Policy can have a huge impact. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/how-to-manage-indexing-policy\" target=\"_blank\" rel=\"noopener\">Manage indexing policies in Azure Cosmos DB<\/a>.<\/p>\n<p>Now that we have optimized writing items to Azure Cosmos Dhttps:\/\/docs.microsoft.com\/azure\/cosmos-db\/how-to-manage-indexing-policyB we will now take a look at optimizing our read queries.<\/p>\n<h3><span style=\"font-size: 18pt;\">Optimizing Query 1<\/span><\/h3>\n<p>The purpose of query 1 is to return all top-ranked participants for a selected event. This is our initial query:<\/p>\n<pre class=\"prettyprint\">SELECT\u00a0*\u00a0FROM\u00a0c\u00a0 \r\nWHERE c.Eventname\u00a0=\u00a0'&lt;eventname&gt;' \r\nAND c.Eventdate\u00a0=\u00a0'&lt;eventdate' \r\nORDER BY c.TotalScore\u00a0asc<\/pre>\n<p>However, for our use case, we don\u2019t need to see all items. All we need to see is the first 100 participants. By using TOP and Order By we can optimize our query.<\/p>\n<p>The TOP keyword returns the first N number of items in an undefined order. It will simply stop processing any other items. By adding Order By to our query the items TOP touches become predictable: it\u2019s always the 100 highest scores. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/sql-query-keywords#top\" target=\"_blank\" rel=\"noopener\">SQL keywords for Azure Cosmos DB<\/a><\/p>\n<pre class=\"prettyprint\">SELECT\u00a0TOP 100 *\u00a0FROM\u00a0c\u00a0\r\nWHERE c.Eventname\u00a0=\u00a0'&lt;eventname&gt;'\r\nAND c.Eventdate\u00a0=\u00a0'&lt;eventdate'\r\nORDER BY c.TotalScore\u00a0asc<\/pre>\n<p>When running our test again the results quite different:<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>Query<\/strong><\/td>\n<td><strong>Items returned<\/strong><\/td>\n<td><strong>Initial RU charge<\/strong><\/td>\n<td><strong>Optimized RU charge<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>100<\/td>\n<td>208<\/td>\n<td>7<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h3><span style=\"font-size: 18pt;\">Optimizing Query 2<\/span><\/h3>\n<p>The purpose of query 2 is to return all events for a year a person participated in.<\/p>\n<pre class=\"prettyprint\">SELECT c.Eventname FROM c \r\nWHERE c.Eventdate &gt; '&lt;startdate&gt;' AND c.Eventdate &lt; '&lt;enddate&gt;'\r\nAND c.ParticipantId = '&lt;id&gt;'\r\n<\/pre>\n<p>This query doesn\u2019t use the partition key and therefor hits all physical partitions. However, we only have at most 100 GB of data in our container, which means a maximum of two physical partitions. The latency for querying two physical partitions is very minimal. Since it\u2019s also highly unlikely that a person will participate in hundreds of events in a single year, we will not optimize this query to use the partition key as a filter. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/partitioning-overview\" target=\"_blank\" rel=\"noopener\">Partitioning and horizontal scaling in Azure Cosmos DB<\/a><\/p>\n<h3><span style=\"font-size: 18pt;\">Optimizing Query 3<\/span><\/h3>\n<p>The purpose of query 3 is to return all participants for a given event.<\/p>\n<pre class=\"prettyprint\">SELECT\u00a0c.ParticipantFirstname,\u00a0c.ParticipantLastname,\u00a0c.ParticipantId\u00a0\u00a0FROM\u00a0c\u00a0\r\nWHERE c.Eventname\u00a0=\u00a0'&lt;eventname&gt;'<\/pre>\n<p>In this case, we cannot use the TOP statement since we might need to show all the participants. However, it\u2019s very likely a user will not always browse through the entire list of participants. Therefor we can optimize this query by using pagination in our application and by using query pagination in Azure Cosmos DB.<\/p>\n<p>Query pagination for Azure Cosmos DB can be implemented by using a continuation token in our application. Continuation tokens can be used as a bookmark for the query&#8217;s progress. Query executions in Azure Cosmos DB are stateless on the server side and can be resumed at any time using this token. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/sql-query-pagination\" target=\"_blank\" rel=\"noopener\">Pagination in Azure Cosmos DB<\/a><\/p>\n<p>Our application will implement pagination and show 100 items per page. A user can click next to view the next 100 items. The first time Query 2 is executed we retrieve and store the continuation token in our application. We also make sure we set the MaxItemCount to 100. For any subsequent calls we will pass the continuation token to retrieve the next 100 items.<\/p>\n<pre class=\"prettyprint\">            QueryDefinition query = new QueryDefinition(\"SELECT c.ParticipantFirstname, c.ParticipantLastname, c.ParticipantId  FROM c WHERE c.Eventname = @Eventname\")\r\n                .WithParameter(\"@Eventname\", eventName);\r\n\r\n            string continuationToken = string.Empty;\r\n            using (FeedIterator&lt;dynamic&gt; resultset = _container.GetItemQueryIterator&lt;dynamic&gt;(query, null, new QueryRequestOptions() { MaxItemCount = 100 }))\r\n            {\r\n                FeedResponse&lt;dynamic&gt; response = await resultset.ReadNextAsync();\r\n                Console.WriteLine(\"Q3 took {0} ms. RU consumed: {1}, Number of items : {2}\", response.Diagnostics.GetClientElapsedTime().TotalMilliseconds, response.RequestCharge, response.Count);\r\n\r\n                foreach (var item in response)\r\n                {\r\n                    list.Add(item);\r\n                }\r\n\r\n                continuationToken = response.ContinuationToken;\r\n            }<\/pre>\n<p>The code snippet above shows that we set MaxItemCount property of QueryRequestOptions object to 100. This instructs Azure Cosmos DB to only return 100 items. From the FeedResponse object, we grab the ContinuationToken value and cache this in our application. When the user decides to view the next 100 items in the application the query will be executed again. However, this time we add the continuation token value instructing Azure Cosmos DB to return the next 100 items.<\/p>\n<pre class=\"prettyprint\">            QueryDefinition query = new QueryDefinition(\"SELECT c.ParticipantFirstname, c.ParticipantLastname, c.ParticipantId  FROM c WHERE c.Eventname = @Eventname\")\r\n                .WithParameter(\"@Eventname\", eventName);\r\n\r\n            using (FeedIterator&lt;dynamic&gt; resultset = _container.GetItemQueryIterator&lt;dynamic&gt;(query, continuationToken, new QueryRequestOptions() { MaxItemCount = 100 }))\r\n            {\r\n                FeedResponse&lt;dynamic&gt; response = await resultset.ReadNextAsync();\r\n                Console.WriteLine(\"Q3 took {0} ms. RU consumed: {1}, Number of items : {2}\", response.Diagnostics.GetClientElapsedTime().TotalMilliseconds, response.RequestCharge, response.Count);\r\n\r\n                foreach (var item in response)\r\n                {\r\n                    list.Add(item);\r\n                }\r\n\r\n                continuationToken = response.ContinuationToken;\r\n            }<\/pre>\n<p>When running our test again the results are:<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>Query<\/strong><\/td>\n<td><strong>Items returned<\/strong><\/td>\n<td><strong>Initial RU charge<\/strong><\/td>\n<td><strong>Optimized RU charge<\/strong><\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>100<\/td>\n<td>191<\/td>\n<td>6<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h3><span style=\"font-size: 18pt;\">Optimizing Query 4<\/span><\/h3>\n<p>The purpose of query 4 is to show the total score for a single participant per event. This always returns one item.<\/p>\n<pre class=\"prettyprint\">SELECT\u00a0c.ParticipantFirstname,\u00a0c.ParticipantLastname,\u00a0c.TotalScore\u00a0FROM\u00a0c\u00a0\r\nWHERE c.ParticipantId\u00a0=\u00a0'&lt;id&gt;'\r\nAND c.Eventname\u00a0=\u00a0'&lt;eventname&gt;'<\/pre>\n<p>When a query always returns one item and also uses the partition key as a filter we can optimize this by using a point read. A point read is a key value look up based on partition key value and item Id. This is the most efficient way of retrieving a single item in Azure Cosmos DB and always consumes 1 RU. Executing a point read can only be done in the SDK by using the ReadItemAsync method. For more information see, <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/optimize-cost-reads-writes#point-reads\" target=\"_blank\" rel=\"noopener\">Optimizing the cost of your requests in Azure Cosmos DB<\/a><\/p>\n<pre class=\"prettyprint\">ItemResponse&lt;Marathon&gt; response = await _container.ReadItemAsync&lt;Marathon&gt;(id, new PartitionKey(eventName));<\/pre>\n<p>When running our test again the results are:<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>Query<\/strong><\/td>\n<td><strong>Items returned<\/strong><\/td>\n<td><strong>Initial RU charge<\/strong><\/td>\n<td><strong>Optimized RU charge<\/strong><\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>1<\/td>\n<td>3<\/td>\n<td>1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h3><span style=\"font-size: 18pt;\">Optimizing Indexing policy<\/span><\/h3>\n<p>Now our queries are optimized greatly, we can tune the index policy by optimizing our range indexes. When analyzing Query 1 we see that two filters in the WHERE clause and an Order By is used. In case of multiple filters we can leverage composite indexes to achieve faster index lookup time. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/index-policy#composite-indexes\" target=\"_blank\" rel=\"noopener\">Azure Cosmos DB indexing policies | Microsoft Docs<\/a>.<\/p>\n<pre class=\"prettyprint\">\"compositeIndexes\": [\r\n        [\r\n            {\r\n                \"path\": \"\/Eventname\",\r\n                \"order\": \"ascending\"\r\n            },\r\n            {\r\n                \"path\": \"\/Eventdate\",\r\n                \"order\": \"ascending\"\r\n            },\r\n            {\r\n                \"path\": \"\/TotalScore\",\r\n                \"order\": \"ascending\"\r\n            }\r\n        ]\r\n    ]<\/pre>\n<p>When running our test again the results are:<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>Query<\/strong><\/td>\n<td><strong>Items returned<\/strong><\/td>\n<td><strong>Initial RU charge<\/strong><\/td>\n<td><strong>Optimized RU charge<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>100<\/td>\n<td>7<\/td>\n<td>6<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>We can see the RU consumption is slightly less and the index lookup time is around 10% faster. The differences will become even more noticeable on larger scale scenarios, where you might have over a million of documents in your container.<\/p>\n<h2><span style=\"font-size: 18pt;\">Monitoring<\/span><\/h2>\n<p>Once our application is finished we need to understand how to monitor Azure Cosmos DB to detect any issues or performance bottlenecks. The following metrics are very useful to monitor :<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/monitor-request-unit-usage\" target=\"_blank\" rel=\"noopener\"><strong>Total Request Units<\/strong><\/a>. This metric shows the average amount of RUs consumed. When you see the average amount increasing this might indicate there are more requests executed by Azure Cosmos DB.<\/li>\n<li><strong>Total Requests<\/strong>. Using this metric we can exactly see how many requests are processed by Azure Cosmos DB. We can even split this by operation type to understand which operation is executed the most.<\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/monitor-normalized-request-units\" target=\"_blank\" rel=\"noopener\"><strong>Normalized RU consumption<\/strong><\/a>. This metric shows you how much percent of the provisioned RUs are used. When this value reaches 100% any subsequent requests in that second might be throttled, depending on how many physical partitions you are having. A root cause could be that more requests are being executed per second. In this case, you should provision more RU\u2019s. Another root cause could be inefficient queries are executed. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/troubleshoot-request-rate-too-large\" target=\"_blank\" rel=\"noopener\">Troubleshoot Azure Cosmos DB request rate too large exceptions<\/a><\/li>\n<li><strong>Provisioned Throughput and Auto scale max throughput. <\/strong>In case you have configured autoscale throughput you might want to monitor how many RU\u2019s are being used to determine the effectiveness of auto scale. Auto scale max throughput metric shows the max autoscale limit you have configured. Provisioned throughput shows how many RU are used for any given second. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/provision-throughput-autoscale\" target=\"_blank\" rel=\"noopener\">Create Azure Cosmos containers and databases in autoscale mode.<\/a><\/li>\n<\/ul>\n<p>All the information above and more can be viewed using Insights for Azure Cosmos DB. This is a pre-created Azure Monitor Workbook that brings all these metrics together. For more information, see <a href=\"https:\/\/docs.microsoft.com\/azure\/azure-monitor\/insights\/cosmosdb-insights-overview\" target=\"_blank\" rel=\"noopener\">Monitor Azure Cosmos DB with Azure Monitor Cosmos DB insights &#8211; Azure Monitor<\/a><\/p>\n<p>&nbsp;<\/p>\n<h3>Get started<\/h3>\n<ul>\n<li>Find out more about <a href=\"https:\/\/azure.microsoft.com\/services\/cosmos-db\/\" target=\"_blank\" rel=\"noopener\">Azure Cosmos DB<\/a><\/li>\n<li>Four ways to <a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/four-ways-to-try-azure-cosmos-db-free\/\" target=\"_blank\" rel=\"noopener\">dev\/test free with Azure Cosmos DB<\/a><\/li>\n<li>The code used is <a href=\"https:\/\/github.com\/cyrille-visser\/cosmosdb\" target=\"_blank\" rel=\"noopener\">published in GitHub<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The second of a two-part series about creating your first Azure Cosmos DB implementation. Learn how to improve performance by optimizing queries and monitoring your database account. <\/p>\n","protected":false},"author":68471,"featured_media":61,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[14,1611],"tags":[],"class_list":["post-3352","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-core-sql-api","category-data-architecture"],"acf":[],"blog_post_summary":"<p>The second of a two-part series about creating your first Azure Cosmos DB implementation. Learn how to improve performance by optimizing queries and monitoring your database account. <\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/3352","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/users\/68471"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/comments?post=3352"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/3352\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media\/61"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media?parent=3352"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/categories?post=3352"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/tags?post=3352"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}