PageSourceSearch

https://azurecertprep.github.io/assets/js/6ad0bc5e.b84cec90.js

js azurecertprep.github.io collected 2026-10-03 08:42:11 UTC 31,785 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkazurecertprep=globalThis.webpackChunkazurecertprep||[]).push([[1623],{35604(e,t,n){n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>c,default:()=>u,frontMatter:()=>a,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"az-305/data-storage/challenge-18","title":"Challenge 18: Design a Semi-Structured Data Solution","description":"60-90 min | Estimated cost 20-25%","source":"@site/docs/az-305/02-data-storage/challenge-18.md","sourceDirName":"az-305/02-data-storage","slug":"/az-305/data-storage/challenge-18","permalink":"/docs/az-305/data-storage/challenge-18","draft":false,"unlisted":false,"editUrl":"https://github.com/azurecertprep/azurecertprep.github.io/tree/main/docs/az-305/02-data-storage/challenge-18.md","tags":[],"version":"current","sidebarPosition":5,"frontMatter":{"sidebar_position":5,"title":"Challenge 18: Design a Semi-Structured Data Solution"},"sidebar":"az305Sidebar","previous":{"title":"Challenge 17: Design Database Protection","permalink":"/docs/az-305/data-storage/challenge-17"},"next":{"title":"Challenge 19: Design an Unstructured Data Solution","permalink":"/docs/az-305/data-storage/challenge-19"}}');var s=n(74848),r=n(28453),o=n(80892);const a={sidebar_position:5,title:"Challenge 18: Design a Semi-Structured Data Solution"},c="Challenge 18: design a Semi-Structured Data solution",l={},d=[{value:"Introduction",id:"introduction",level:2},{value:"Exam skills covered",id:"exam-skills-covered",level:2},{value:"Design tasks",id:"design-tasks",level:2},{value:"Part 1: Service and API selection",id:"part-1-service-and-api-selection",level:3},{value:"Part 2: Data modeling and partitioning",id:"part-2-data-modeling-and-partitioning",level:3},{value:"Part 3: consistency and global distribution",id:"part-3-consistency-and-global-distribution",level:3},{value:"Success criteria",id:"success-criteria",level:2},{value:"Hints",id:"hints",level:2},{value:"Learning resources",id:"learning-resources",level:2},{value:"Knowledge check",id:"knowledge-check",level:2},{value:"Validation lab",id:"validation-lab",level:2},{value:"Step 1: create the Cosmos DB account and container",id:"step-1-create-the-cosmos-db-account-and-container",level:3},{value:"Step 2: insert documents with different partition keys",id:"step-2-insert-documents-with-different-partition-keys",level:3},{value:"Step 3: compare single-partition vs cross-partition query cost",id:"step-3-compare-single-partition-vs-cross-partition-query-cost",level:3},{value:"Step 4: change consistency level and observe RU impact",id:"step-4-change-consistency-level-and-observe-ru-impact",level:3},{value:"Step 5: test TTL -- automatic document expiration",id:"step-5-test-ttl----automatic-document-expiration",level:3},{value:"Cleanup",id:"cleanup",level:2}];function h(e){const t={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components},{Details:n}=t;return n||function(e,t){throw new Error("Expected "+(t?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.header,{children:(0,s.jsx)(t.h1,{id:"challenge-18-design-a-semi-structured-data-solution",children:"Challenge 18: design a Semi-Structured Data solution"})}),"\n",(0,s.jsx)(t.admonition,{title:"Estimated Time and Cost",type:"info",children:(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"60-90 min"})," | ",(0,s.jsx)(t.strong,{children:"Estimated cost"}),": $10-25 | ",(0,s.jsx)(t.strong,{children:"Exam Weight: 20-25%"})]})}),"\n",(0,s.jsx)(t.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsx)(t.p,{children:"SensorGrid is an industrial IoT platform that monitors 50,000 devices deployed across manufacturing facilities in North America, Europe, and Asia-Pacific. Each device transmits telemetry events (temperature, vibration, pressure, humidity) every 5 seconds, resulting in approximately 1 million events per second at peak. Events are JSON documents with a variable schema: different device types include different sensor readings, firmware versions add new fields over time, and some events include nested arrays of sub-readings."}),"\n",(0,s.jsx)(t.p,{children:"The platform has two primary access patterns. First, operators need real-time dashboards showing the latest state of any device with sub-10ms read latency (point reads by device ID). Second, engineers run historical analytics queries spanning days or weeks of data for a specific facility or device type, where response times of 2-5 seconds are acceptable. The current data volume is 2TB and grows by 500GB per month."}),"\n",(0,s.jsx)(t.p,{children:"SensorGrid's budget for the data layer is $3,000/month. The CTO wants to minimize operational overhead (no managing cluster
1s or shards manually) and requires multi-region availability with automatic failover. The engineering team has experience with MongoDB query syntax from a previous project but is open to other APIs if the trade-offs justify it. Data retention policy requires hot data for 90 days, after which it should be archived or moved to cold storage to control costs."}),"\n",(0,s.jsx)(t.h2,{id:"exam-skills-covered",children:"Exam skills covered"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"Recommend a solution for storing semi-structured data"}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"design-tasks",children:"Design tasks"}),"\n",(0,s.jsx)(t.h3,{id:"part-1-service-and-api-selection",children:"Part 1: Service and API selection"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsx)(t.li,{children:"Evaluate Azure Cosmos DB as the primary data store for SensorGrid's requirements. Compare it against alternatives (Azure Table Storage, MongoDB Atlas on Azure) and justify your recommendation."}),"\n",(0,s.jsx)(t.li,{children:"Select the most appropriate Cosmos DB API for this workload. Compare NoSQL (native), MongoDB, PostgreSQL, Cassandra, and Gremlin APIs. Consider the team's MongoDB experience, the query patterns required, and long-term flexibility."}),"\n",(0,s.jsx)(t.li,{children:"If you recommend the NoSQL API, explain how SQL-like queries and the change feed provide advantages over the MongoDB API for this IoT scenario. If you recommend MongoDB API, explain how wire protocol compatibility reduces migration effort."}),"\n",(0,s.jsx)(t.li,{children:"Evaluate whether Azure Table Storage could handle any portion of this workload at lower cost (for simpler key-value lookups of device state)."}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"part-2-data-modeling-and-partitioning",children:"Part 2: Data modeling and partitioning"}),"\n",(0,s.jsxs)(t.ol,{start:"5",children:["\n",(0,s.jsx)(t.li,{children:"Design the partition key strategy for the telemetry events container. Evaluate candidates: device ID, facility ID, device type, timestamp, or a synthetic key combining multiple fields. Consider the access patterns (point reads by device, range queries by time, queries by facility)."}),"\n",(0,s.jsx)(t.li,{children:"Calculate the expected RU (Request Unit) consumption for the two primary access patterns: (a) point read of latest device state, (b) query returning 1 hour of history for a single device. Estimate the provisioned throughput needed."}),"\n",(0,s.jsx)(t.li,{children:"Design the document structure for telemetry events. Decide whether to store each reading as an individual document or batch multiple readings into a single document (bucketing pattern). Analyze the trade-offs in RU cost, query flexibility, and write throughput."}),"\n",(0,s.jsx)(t.li,{children:"Design a TTL (time-to-live) strategy to automatically expire data after 90 days, reducing storage costs without manual cleanup jobs."}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"part-3-consistency-and-global-distribution",children:"Part 3: consistency and global distribution"}),"\n",(0,s.jsxs)(t.ol,{start:"9",children:["\n",(0,s.jsx)(t.li,{children:"Select the appropriate consistency level for each access pattern: (a) real-time dashboard reads (latest device state), (b) historical analytics queries. Evaluate strong, bounded staleness, session, consistent prefix, and eventual consistency. Document the RU cost implications of each level."}),"\n",(0,s.jsx)(t.li,{children:"Design the multi-region deployment topology. Determine which regions should have write capability (single-write vs multi-write) and how many read regions to deploy given the device distribution."}),"\n",(0,s.jsx)(t.li,{children:"Evaluate multi-region writes for scenarios where devices in each region write to the nearest Cosmos DB instance. Address conflict resolution strategy (Last Writer Wins vs custom merge procedures)."}),"\n",(0,s.jsx)(t.li,{children:"Design a cost optimization strategy including autoscale throughput, serverless tier for development environments, and hierarchical partition keys for improved data distribution."}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"success-criteria",children:"Success criteria"}),"\n",(0,s.jsx)(o.A,{storageKey:"az305-challenge-18",items:["Selected and justified the Cosmos DB API choice with clear comparison against alternatives","Designed partition key strategy that avoids hot partitions and supports both access patterns","Estimated RU consumption and selected appropriate throughput provisioning mode (manual, autoscale, or serverless)","Selected consistency levels appropriate to each access pattern with documented trade-offs","Designed multi-region topology with automatic failover and clear write region strategy","Implemented TTL and data lifecycle strategy to control storage costs within budget"]}),"\n",(0,s.jsx)(t.h2,{id:"hints",children:"Hints"}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"Hint 1: Cosmos DB API Selection"}),(0,s.jsx)(t.p,{children:"The NoSQL API (formerly SQL API) is the native Cosmos DB API with the richest feature set: SQL-like query language, change feed, hierarchical partition keys, full indexing control, and best SDK support. The MongoDB API provides wire-protocol compatibility for teams migrating from MongoDB. For greenfield IoT projects, the NoSQL API typically offers better performance optimization and lower RU costs because it has no wire-protocol translation overhead. Choose MongoDB API only if you have existing MongoDB application code you cannot modify."})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"Hint 2: Partition Key for IoT Data"}),(0,s.jsxs)(t.p,{children:["For IoT telemetry, common partition key strategies: (1) Device ID: excellent for point reads of a single device but creates hot partitions if one device generates far more data; (2) Synthetic key like ",(0,s.jsx)(t.code,{children:"deviceId_YYYYMMDD"}),": distributes data evenly and supports time-based queries within a device; (3) Hierarchical partition keys (preview/GA): allow multi-level keys like ",(0,s.jsx)(t.code,{children:"/tenantId/deviceId"})," for both broad and narrow queries. Avoid timestamp alone as a partition key (creates hot partitions at the current time)."]})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"Hint 3: Request Unit Estimation"}),(0,s.jsx)(t.p,{children:"Cosmos DB cost fundamentals: a point read of a 1KB document costs 1 RU. Writes cost approximately 5-10 RUs per 1KB document. Queries cost varies based on complexity (cross-partition queries cost more). For 1M writes/second at 1KB each, you would need a
1pproximately 5-10 million RU/s, which would be extremely expensive. This is why document bucketing (batching 10-60 readings per document) dramatically reduces write RUs by reducing the number of individual write operations."})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"Hint 4: Consistency Levels and Cost"}),(0,s.jsx)(t.p,{children:"Cosmos DB consistency levels from strongest to weakest: Strong, Bounded Staleness, Session, Consistent Prefix, Eventual. Strong consistency costs 2x the RUs of eventual consistency for reads (because it must read from the quorum). Session consistency (default) provides read-your-own-writes within a session at 1x RU cost. For dashboards showing latest device state, Session consistency is often sufficient. For cross-region reads where slight staleness is acceptable, Eventual or Consistent Prefix minimizes cost."})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"Hint 5: Autoscale vs Provisioned Throughput"}),(0,s.jsx)(t.p,{children:"Autoscale throughput automatically scales between 10% and 100% of a configured maximum RU/s. You pay for the highest RU/s the system scales to in each hour. It is ideal for variable or unpredictable workloads. Manual provisioned throughput is cheaper when load is predictable and steady. For IoT with 1M events/second during peaks but lower volume during off-peak, autoscale prevents over-provisioning. You can also set autoscale max at 4x your baseline to handle spikes."})]}),"\n",(0,s.jsx)(t.h2,{id:"learning-resources",children:"Learning resources"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/introduction",children:"Azure Cosmos DB overview"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/choose-api",children:"Choose an API in Azure Cosmos DB"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/partitioning-overview",children:"Partitioning and horizontal scaling in Azure Cosmos DB"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/consistency-levels",children:"Consistency levels in Azure Cosmos DB"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/request-units",children:"Request Units in Azure Cosmos DB"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/provision-throughput-autoscale",children:"Azure Cosmos DB autoscale provisioned throughput"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/distribute-data-globally",children:"Distribute data globally with Azure Cosmos DB"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/time-to-live",children:"Time to Live (TTL) in Azure Cosmos DB"})}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"knowledge-check",children:"Knowledge check"}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"1. An IoT platform ingests 1 million events per second from globally distributed devices. Each event is a 1KB JSON document. Why is document bucketing (batching) critical for cost management in Cosmos DB?"}),(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Document bucketing reduces the number of write operations and therefore total RU consumption."})," A single 1KB write costs approximately 5-10 RUs. At 1M events/second, that would require 5-10M RU/s (costing $25,000-50,000+/month). By batching 60 readings into a single 5KB document (one per device per minute), you reduce write operations to ~16,600/second at approximately 15-20 RUs each, dramatically lowering costs to a manageable level."]})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"2. A Cosmos DB container uses device ID as the partition key. During peak hours, 10% of devices generate 90% of the telemetry. What problem will occur and how do you solve it?"}),(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Hot partition problem."}
1)," The 10% of high-volume devices will overwhelm their logical partitions, causing throttling (HTTP 429 errors) while other partitions remain underutilized. Solutions: (1) Use a synthetic partition key combining device ID with a time component (e.g., ",(0,s.jsx)(t.code,{children:"deviceId_YYYYMMDD"}),") to distribute writes over more logical partitions; (2) Use hierarchical partition keys to add sub-partitioning; (3) Implement a write-behind buffer that batches events before writing."]})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"3. An application reads device state from Cosmos DB. The read must reflect writes made by the same application session, but reads from other regions can be slightly stale. Which consistency level is most cost-effective?"}),(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Session consistency."})," It guarantees read-your-own-writes and monotonic reads within a single client session, costing the same as eventual consistency (1x RU for reads). Strong consistency would cost 2x RUs and is unnecessary since the requirement only mandates session-level consistency. Bounded staleness would also work but is more expensive than session consistency for single-region writes."]})]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:"4. When should you choose multi-region writes in Cosmos DB versus single-region writes with multi-region reads?"}),(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Choose multi-region writes when:"})," write latency from remote regions is unacceptable (devices need to write to the nearest region), or when write availability during a regional outage is required. ",(0,s.jsx)(t.strong,{children:"Choose single-region writes when:"})," write volume is manageable from one region, conflict resolution complexity is undesirable, consistency requirements are simpler, or cost is a primary concern (multi-write adds approximately 25% to RU costs). For IoT ingestion from globally distributed devices where low write latency is critical, multi-region writes are often justified despite the added cost and conflict resolution complexity."]})]}),"\n",(0,s.jsx)(t.h2,{id:"validation-lab",children:"Validation lab"}),"\n",(0,s.jsx)(t.p,{children:"This lab demonstrates that Cosmos DB architectural decisions -- partition key choice, consistency levels, and TTL -- produce measurable behavioral differences you can observe directly. You will not just create resources; you will see how design choices manifest as RU cost, query performance, and automatic data lifecycle management."}),"\n",(0,s.jsx)(t.h3,{id:"step-1-create-the-cosmos-db-account-and-container",children:"Step 1: create the Cosmos DB account and container"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"az group create \\\n  --name rg-az305-challenge18 \\\n  --location eastus\n"})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'COSMOS_ACCOUNT="cosmos-ch18-${RANDOM}"\n'})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'az cosmosdb create \\\n  --name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --locations regionName=eastus failoverPriority=0 \\\n  --capabilities EnableServerless \\\n  --default-consistency-level Session\n'})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'az cosmosdb sql database create \\\n  --account-name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --name SensorData\n'})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'az cosmosdb sql container create \\\n  --account-name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --database-name SensorData \\\n  --name Telemetry \\\n  --partition-key-path "/deviceId" \\\n  --ttl -1\n'})}),"\n",(0,s.jsxs)(t.p,{children:["Setting ",(0,s.jsx)(t.code,{children:"--ttl -1"})," enables TTL at the container level but requires each document to specify its own TTL value. This gives per-document control over expiration."]}),"\n",(0,s.jsx)(t.h3,{id:"step-2-insert-documents-with-different-partition-keys",children:"Step 2: insert documents with different partition keys"}),"\n",(0,s.jsx)(t.p,{children:"Retrieve the account endpoint and key:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'COSMOS_KEY=$(az cosmosdb keys list \\\n  --name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --query primaryMasterKey -o tsv)\n'})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'COSMOS_ENDPOINT=$(az cosmosdb show \\\n  --name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --query documentEndpoint -o tsv)\n'})}),"\n",(0,s.jsx)(t.p,{children:"Insert telemetry documents across multiple partitions:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'az cosmosdb sql c
1ontainer create \\\n  --account-name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --database-name SensorData \\\n  --name TelemetryTest \\\n  --partition-key-path "/deviceId" \\\n  --ttl -1\n'})}),"\n",(0,s.jsx)(t.p,{children:"Use the REST API or Data Explorer in the portal to insert these documents. Alternatively, use a short script:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"pip install azure-cosmos --quiet\n"})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"python3 -c \"\nfrom azure.cosmos import CosmosClient, PartitionKey\nimport os, time\n\nclient = CosmosClient(os.environ.get('COSMOS_ENDPOINT', '${COSMOS_ENDPOINT}'),\n                      os.environ.get('COSMOS_KEY', '${COSMOS_KEY}'))\ndb = client.get_database_client('SensorData')\ncontainer = db.get_container_client('Telemetry')\n\n# Insert documents across 3 different partitions\ndocs = [\n    {'id': 'reading-001', 'deviceId': 'device-A', 'temp': 72.1, 'facility': 'us-east'},\n    {'id': 'reading-002', 'deviceId': 'device-A', 'temp': 72.4, 'facility': 'us-east'},\n    {'id': 'reading-003', 'deviceId': 'device-A', 'temp': 71.9, 'facility': 'us-east'},\n    {'id': 'reading-004', 'deviceId': 'device-B', 'temp': 68.2, 'facility': 'eu-west'},\n    {'id': 'reading-005', 'deviceId': 'device-B', 'temp': 68.5, 'facility': 'eu-west'},\n    {'id': 'reading-006', 'deviceId': 'device-C', 'temp': 80.0, 'facility': 'ap-south'},\n]\n\nfor doc in docs:\n    response = container.create_item(body=doc)\n    print(f'Inserted {doc[\\\"id\\\"]} into partition {doc[\\\"deviceId\\\"]}')\n    # Show the RU charge for each write (Python SDK v4 pattern: last_response_headers stores headers from most recent operation)\n    print(f'  Write RU charge: {container.client_connection.last_response_headers[\\\"x-ms-request-charge\\\"]}')\n\"\n"})}),"\n",(0,s.jsx)(t.h3,{id:"step-3-compare-single-partition-vs-cross-partition-query-cost",children:"Step 3: compare single-partition vs cross-partition query cost"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"python3 -c \"\nfrom azure.cosmos import CosmosClient\nimport os\n\nclient = CosmosClient('${COSMOS_ENDPOINT}', '${COSMOS_KEY}')\ndb = client.get_database_client('SensorData')\ncontainer = db.get_container_client('Telemetry')\n\n# Single-partition query (targets device-A only)\nprint('=== Single-Partition Query (deviceId = device-A) ===')\nitem_list = list(container.query_items(\n    query='SELECT * FROM c WHERE c.deviceId = \\\"device-A\\\"',\n    partition_key='device-A'\n))\n# Python SDK v4 pattern: client_connection.last_response_headers captures headers from the most recent operation\nru_charge = container.client_connection.last_response_headers['x-ms-request-charge']\nprint(f'Results: {len(item_list)} documents')\nprint(f'RU cost: {ru_charge} RUs')\nprint()\n\n# Cross-partition query (scans ALL partitions)\nprint('=== Cross-Partition Query (all devices, filter by facility) ===')\nitem_list2 = list(container.query_items(\n    query='SELECT * FROM c WHERE c.facility = \\\"us-east\\\"',\n    enable_cross_partition_query=True\n))\n# Python SDK v4 pattern: client_connection.last_response_headers captures headers from the most recent operation\nru_charge2 = container.client_connection.last_response_headers['x-ms-request-charge']\nprint(f'Results: {len(item_list2)} documents')\nprint(f'RU cost: {ru_charge2} RUs')\n\"\n"})}),"\n",(0,s.jsx)(t.admonition,{title:"Architect Insight",type:"note",children:(0,s.jsx)(t.p,{children:'Observe that the cross-partition query costs significantly more RUs than the single-partition query, even when returning fewer or equal results. This is because Cosmos DB must fan out the query to every physical partition. On the AZ-305 exam, questions about "minimizing RU consumption" almost always hinge on whether the query is single-partition or cross-partition. Your partition key choice determines this at design time, not at query time.'})}),"\n",(0,s.jsx)(t.h3,{id:"step-4-change-consistency-level-and-observe-ru-impact",children:"Step 4: change consistency level and observe RU impact"}),"\n",(0,s.jsx)(t.p,{children:"Lower the account default consistency from Session to Eventual:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'az cosmosdb update \\\n  --name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --default-consistency-level Eventual\n'})}),"\n",(0,s.jsx)(t.p,{children:"Re-run the same single-partition query:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"python3 -c \"\nfrom azure.cosmos import CosmosClient\nimport os\n\nclient = CosmosClient('${COSMOS_ENDPOINT}', '${COSMOS_KEY}')\ndb = client.get_database_client('SensorData')\ncontainer = db.get_container_
1client('Telemetry')\n\nprint('=== Query with Eventual Consistency ===')\nitems = list(container.query_items(\n    query='SELECT * FROM c WHERE c.deviceId = \\\"device-A\\\"',\n    partition_key='device-A'\n))\nprint(f'Results: {len(items)} documents')\nprint(f'RU cost: {container.client_connection.last_response_headers[\\\"x-ms-request-charge\\\"]} RUs')\nprint()\nprint('Compare this RU cost to the Session consistency query above.')\nprint('Eventual consistency can reduce read costs because replicas do not')\nprint('need to confirm they have the latest write before responding.')\n\"\n"})}),"\n",(0,s.jsx)(t.admonition,{title:"Architect Insight",type:"note",children:(0,s.jsx)(t.p,{children:"Consistency levels are a performance lever, not just a correctness knob. Strong consistency costs 2x RUs for reads because it requires quorum confirmation. Session consistency (the default) costs 1x and guarantees read-your-own-writes. Eventual consistency may cost less in multi-region scenarios. On the exam, the correct answer depends on whether the scenario tolerates stale reads -- IoT dashboards often can, financial transactions cannot."})}),"\n",(0,s.jsx)(t.h3,{id:"step-5-test-ttl----automatic-document-expiration",children:"Step 5: test TTL -- automatic document expiration"}),"\n",(0,s.jsx)(t.p,{children:"Insert a document with a short TTL (30 seconds):"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"python3 -c \"\nfrom azure.cosmos import CosmosClient\nimport os, time\n\nclient = CosmosClient('${COSMOS_ENDPOINT}', '${COSMOS_KEY}')\ndb = client.get_database_client('SensorData')\ncontainer = db.get_container_client('Telemetry')\n\n# Insert with 30-second TTL\ndoc = {\n    'id': 'ephemeral-reading',\n    'deviceId': 'device-A',\n    'temp': 99.9,\n    'ttl': 30\n}\ncontainer.create_item(body=doc)\nprint('Inserted document with TTL=30 seconds')\n\n# Confirm it exists\nresult = container.read_item(item='ephemeral-reading', partition_key='device-A')\nprint(f'Document exists: {result[\\\"id\\\"]} (temp={result[\\\"temp\\\"]})')\n\nprint('Waiting 40 seconds for TTL expiration...')\ntime.sleep(40)\n\n# Try to read it again\ntry:\n    result = container.read_item(item='ephemeral-reading', partition_key='device-A')\n    print(f'Document still exists (TTL has not fired yet -- may take up to 60s)')\nexcept Exception as e:\n    print(f'Document GONE -- TTL expired it automatically')\n    print(f'Error: {e}')\n\"\n"})}),"\n",(0,s.jsx)(t.admonition,{title:"Architect Insight",type:"note",children:(0,s.jsx)(t.p,{children:'TTL eliminates the need for manual cleanup jobs, scheduled functions, or batch deletion scripts. For IoT telemetry with a 90-day retention policy, setting TTL to 7776000 seconds means data automatically disappears without any application logic. This reduces operational complexity AND cost (no compute resources running cleanup). The exam tests whether you know that TTL is the correct tool for "automatically expire data after N days" requirements.'})}),"\n",(0,s.jsx)(t.admonition,{title:"Design Validation",type:"tip",children:(0,s.jsx)(t.p,{children:"This lab proved three Cosmos DB architectural principles: (1) Partition key choice directly determines query cost -- single-partition queries are dramatically cheaper than cross-partition queries. (2) Consistency levels are a performance lever -- weaker consistency reduces RU consumption when the application tolerates stale reads. (3) TTL automates data lifecycle without application code -- documents disappear on schedule with zero operational overhead."})}),"\n",(0,s.jsx)(t.h2,{id:"cleanup",children:"Cleanup"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:'az cosmosdb delete \\\n  --name "$COSMOS_ACCOUNT" \\\n  --resource-group rg-az305-challenge18 \\\n  --yes\n'})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"az group delete \\\n  --name rg-az305-challenge18 \\\n  --yes --no-wait\n"})}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Next"}),": ",(0,s.jsx)(t.a,{href:"/docs/az-305/data-storage/challenge-19",children:"Challenge 19: Design an Unstructured Data Solution"})]})]})}function u(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},80892(e,t,n){n.d(t,{A:()=>
1o});var i=n(96540),s=n(74848);function r({text:e,checked:t,onToggle:n}){return(0,s.jsxs)("li",{onClick:n,style:{display:"flex",alignItems:"flex-start",gap:"0.6rem",padding:"0.5rem 0",cursor:"pointer",userSelect:"none",listStyle:"none"},children:[(0,s.jsx)("span",{style:{fontSize:"1.2rem",lineHeight:"1.4",flexShrink:0},children:t?"\u2705":"\u2b1c"}),(0,s.jsx)("span",{style:{lineHeight:"1.5",textDecoration:t?"line-through":"none",opacity:t?.7:1},children:e})]})}function o({items:e,storageKey:t}){const[n,o]=(0,i.useState)({});(0,i.useEffect)(()=>{try{const e=localStorage.getItem(`success-${t}`);e&&o(JSON.parse(e))}catch{}},[t]);const a=e.length,c=Object.values(n).filter(Boolean).length;return(0,s.jsxs)("div",{children:[c>0&&(0,s.jsxs)("div",{style:{fontSize:"0.85rem",marginBottom:"0.5rem",color:c===a?"#27ae60":"var(--ifm-color-emphasis-600)",fontWeight:c===a?600:400},children:[c,"/",a," completed ",c===a&&"- All done!"]}),(0,s.jsx)("ul",{style:{margin:0,padding:0},children:e.map((e,i)=>(0,s.jsx)(r,{text:e,checked:!!n[i],onToggle:()=>(e=>{const i={...n,[e]:!n[e]};o(i);try{localStorage.setItem(`success-${t}`,JSON.stringify(i))}catch{}})(i)},i))}),c>0&&(0,s.jsx)("button",{onClick:()=>{o({});try{localStorage.removeItem(`success-${t}`)}catch{}},style:{marginTop:"0.5rem",padding:"0.3rem 0.8rem",fontSize:"0.8rem",cursor:"pointer",borderRadius:"4px",border:"1px solid #ccc",background:"transparent"},children:"Reset"})]})}},28453(e,t,n){n.d(t,{R:()=>o,x:()=>a});var i=n(96540);const s={},r=i.createContext(s);function o(e){const t=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(r.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.