AI-200 · Domain 2: Develop AI solutions by using Azure data management services · 33% of the exam
Task 2.1: Develop AI solutions by using Azure Cosmos DB for NoSQL
The Python SDK and queries, indexing policies, consistency levels and request units, storing embeddings and running vector search, and the change feed processor.
Study it
Cosmos DB for NoSQL with the Python SDK: containers, partition keys and queries
The change feed processor: leases, start time and estimating lag
Lesson coming
Sample questions
Try each one before opening the answer. Every option is explained, with the Microsoft documentation page that proves it.
Question 1 · choose 1
A container in Azure Cosmos DB for NoSQL will hold about two million product embeddings of 1,536 dimensions each. Similarity queries search across the whole container, and the team wants the lowest latency and RU charge per query while keeping recall high. Which vector index type should the indexing policy use for the embedding path?
Aflat
BquantizedFlat
CdiskANN
DNo vector index, relying on VectorDistance over the stored vectors
Show the answer and why
Aflat
Incorrect
A flat index supports at most 505 dimensions, so it can't index 1,536-dimension vectors at all.
BquantizedFlat
Incorrect
quantizedFlat is still a brute-force search; Microsoft suggests it when the vectors in scope number 50,000 or fewer.
CdiskANN
Correct
DiskANN is generally the most performant index type when a search is scoped to more than 50,000 vectors, with high recall at low RU cost.
DNo vector index, relying on VectorDistance over the stored vectors
Incorrect
Without a vector index every search is a full scan, with much higher latency and RU charges.
flat (up to 505 dimensions) gives exact results, quantizedFlat compresses vectors but still scans them, and diskANN builds an approximate nearest neighbor index that suits large vector sets. The vector index must be on a path that the container's vector policy defines.
You created a Cosmos DB for NoSQL container with a vector embedding policy for /contentVector and a quantizedFlat vector index. The indexing policy includes the root path /* and excludes only /_etag/?. Inserting documents costs noticeably more RUs and takes longer than expected. Which change to the indexing policy addresses this?
ASet indexingMode to lazy
BSet indexingMode to none
CAdd /contentVector/* to excludedPaths
DReplace the root path /* with /contentVector/? in includedPaths
Show the answer and why
ASet indexingMode to lazy
Incorrect
New containers can't select lazy indexing, and lazy indexing can return inconsistent or incomplete query results.
BSet indexingMode to none
Incorrect
None disables indexing for the container, so filtered queries would lose their indexes.
CAdd /contentVector/* to excludedPaths
Correct
Leaving the vector path under the regular range index makes every insert index each vector element; excluding it lowers insert RU charges and latency.
DReplace the root path /* with /contentVector/? in includedPaths
Incorrect
That would index only the vector path, the one path that shouldn't be in the range index, and drop every other property from it.
Vector properties are handled by the vector index, not by the range index on /*. Adding the vector path to excludedPaths keeps the range index from indexing every array element of every embedding.
A retrieval step runs SELECT c.id, c.text, VectorDistance(c.embedding, @q) AS score FROM c ORDER BY VectorDistance(c.embedding, @q). The application keeps only the five best matches, yet each call consumes many RUs. What should you change in the query?
APass true as the third argument of VectorDistance
BReplace ORDER BY with a WHERE clause on the score alias
CAdd ORDER BY c._ts DESC as a second sort key
DAdd TOP 5 to the SELECT clause
Show the answer and why
APass true as the third argument of VectorDistance
Incorrect
True forces a brute-force calculation that ignores the vector index, which raises the cost.
BReplace ORDER BY with a WHERE clause on the score alias
Incorrect
Similarity search ranks results with ORDER BY VectorDistance; filtering on a computed score doesn't limit the scan.
CAdd ORDER BY c._ts DESC as a second sort key
Incorrect
A second sort key doesn't limit the result count and isn't part of a vector search.
DAdd TOP 5 to the SELECT clause
Correct
Microsoft advises always using TOP N in vector queries; without it the search tries to return many more results and costs more RUs.
VectorDistance returns a similarity score that the query orders by. Limiting the result with TOP N keeps the search from returning, and charging for, more matches than the application needs.
A Cosmos DB account uses Strong as its default consistency. A chat app reads conversation history right after writing each turn and must always see its own latest writes, but it doesn't need every user to see every other user's writes at once. Reads are the largest share of the RU bill. Which default consistency level meets the requirement at the lowest read cost?
ABounded staleness
BEventual
CSession
DStrong, with autoscale throughput on the container
Show the answer and why
ABounded staleness
Incorrect
Bounded staleness reads still cost about twice as many RUs as reads at the weaker levels.
BEventual
Incorrect
Eventual is cheap but gives no read-your-writes guarantee, so a turn could be missing right after it was written.
CSession
Correct
Session guarantees read-your-writes within a client session and avoids the doubled read charge of the two strongest levels.
DStrong, with autoscale throughput on the container
Incorrect
Autoscale changes how throughput is provisioned, not the RU charge of each strong read.
Strong and bounded staleness reads cost roughly twice the RUs of the other levels. Session consistency uses a session token so a client always reads its own writes, which is what a single user's chat needs. Recreate SDK clients after changing the account default.
A Python service must react to new and updated items in a Cosmos DB for NoSQL container, resume from where it stopped after a restart, and spread the work across several instances. Which two approaches work for a Python solution? (Choose TWO.)
ABuild a change feed processor with the Python SDK's builder and a lease container
BRead in all versions and deletes mode on an account that uses periodic backup
CRead the change feed with the pull model, persisting continuation tokens and splitting the work by FeedRange
DSet a time to live on the container so that changed items are pushed to the service
EUse an Azure Functions Cosmos DB trigger with a lease container
Show the answer and why
ABuild a change feed processor with the Python SDK's builder and a lease container
Incorrect
The change feed processor library is available only in the .NET and Java SDKs.
BRead in all versions and deletes mode on an account that uses periodic backup
Incorrect
All versions and deletes mode requires continuous backup, and it isn't needed to see creates and updates.
CRead the change feed with the pull model, persisting continuation tokens and splitting the work by FeedRange
Correct
The pull model is the documented option for Python; continuation tokens bookmark progress and FeedRanges parallelize consumers.
DSet a time to live on the container so that changed items are pushed to the service
Incorrect
TTL deletes expired items; it doesn't deliver changes, and latest version mode doesn't show deletes at all.
EUse an Azure Functions Cosmos DB trigger with a lease container
Correct
The trigger runs on the change feed and keeps its progress in a lease container, and it supports Python functions.
Inserts and updates are captured in latest version mode. In .NET and Java the change feed processor manages leases for you; Python uses the pull model with manually persisted continuation tokens, or an Azure Functions trigger that handles leases.
A Python service looks up a user profile by its id and partition key on every request by running SELECT * FROM c WHERE c.id = @id with the partition key set. Profiles are about 1 KB. How do you make each lookup as cheap as possible?
AKeep the query and add a composite index on id and the partition key
BKeep the query and run it with enable_cross_partition_query set to true
CChange the account's default consistency to Strong
DCall read_item with the item ID and partition key instead of running a query
Show the answer and why
AKeep the query and add a composite index on id and the partition key
Incorrect
Even with an index, a query that filters on id and partition key isn't treated as a point read.
BKeep the query and run it with enable_cross_partition_query set to true
Incorrect
Fanning out across partitions adds cost; the partition key is already known.
CChange the account's default consistency to Strong
Incorrect
Strong consistency roughly doubles the RU charge of every read.
DCall read_item with the item ID and partition key instead of running a query
Correct
A point read of a 1 KB item costs about 1 RU, the cheapest way to read in Cosmos DB.
Point reads, key/value lookups on an item's ID and partition key, are the most efficient reads, and their cost depends mainly on item size. In the API for NoSQL they're made through the SDK or REST API, not through a query.
A query returns documents for one category sorted with ORDER BY c.category ASC, c.updatedAt DESC, and it fails until the indexing policy supports the sort. The policy currently has only the default range indexes. Which composite index do you add?
A(updatedAt DESC, category ASC)
B(category ASC, updatedAt ASC)
CNo composite index; add an included range path for /updatedAt/?
D(category ASC, updatedAt DESC)
Show the answer and why
A(updatedAt DESC, category ASC)
Incorrect
The path sequence must match the ORDER BY clause; reversing it leaves the query unsupported.
B(category ASC, updatedAt ASC)
Incorrect
A composite index supports the same directions, or the opposite direction on every path, but not a mix like this one.
CNo composite index; add an included range path for /updatedAt/?
Incorrect
ORDER BY on two or more properties always needs a composite index; range indexes alone don't serve it.
D(category ASC, updatedAt DESC)
Correct
The composite paths match the ORDER BY sequence and directions, so the index supports the query.
Composite indexes are required for ORDER BY on more than one property. The order of the paths and their directions must match the clause (or be the exact opposite on all paths). Adding the index triggers an online index transformation.
A FastAPI service calls Cosmos DB from async endpoints with the synchronous azure.cosmos.CosmosClient. Under load, request latency spikes and the event loop stalls. What's the recommended fix?
ACreate a new synchronous CosmosClient inside each request handler and close it after use
BSwitch to azure.cosmos.aio.CosmosClient, created once and closed with async with
CRaise the container's provisioned throughput
DSet preferred_locations to the nearest region
Show the answer and why
ACreate a new synchronous CosmosClient inside each request handler and close it after use
Incorrect
The SDK should be one client instance reused for the app's life; new clients per request add overhead and still block.
BSwitch to azure.cosmos.aio.CosmosClient, created once and closed with async with
Correct
The async client is the one to use in async frameworks; the sync client blocks the event loop.
CRaise the container's provisioned throughput
Incorrect
More RU/s doesn't stop blocking I/O calls from stalling the event loop.
DSet preferred_locations to the nearest region
Incorrect
Preferred locations improve latency and failover, but the blocking calls remain.
Don't use the sync CosmosClient inside an async event loop. The async client lives in azure.cosmos.aio, needs aiohttp installed explicitly, and must be closed, preferably with async with.
In a Cosmos DB container that stores chat sessions, only items marked as temporary drafts should be deleted automatically, 24 hours after their last change; every other item must stay until the app deletes it. How do you configure time to live?
ALeave the container's TTL unset and give draft items a ttl of 86400
BSet the container's default TTL to -1 and give draft items a ttl of 86400
CSet the container's default TTL to 86400 and give other items a ttl of null
DRun a nightly query that deletes drafts older than 24 hours
Show the answer and why
ALeave the container's TTL unset and give draft items a ttl of 86400
Incorrect
If the container has no TTL, an item's ttl property has no effect, so drafts would never expire.
BSet the container's default TTL to -1 and give draft items a ttl of 86400
Correct
With -1 on the container nothing expires by default, and items with their own ttl expire that many seconds after their last change.
CSet the container's default TTL to 86400 and give other items a ttl of null
Incorrect
Setting an item's ttl to null isn't supported; non-expiring items would need -1 instead.
DRun a nightly query that deletes drafts older than 24 hours
Incorrect
This works, but it spends RUs on a query and deletes that TTL performs for you as a background task.
DefaultTimeToLive on the container enables TTL: missing or null turns it off, -1 means items don't expire unless they set their own ttl, and n expires items n seconds after their last modification. Expired items disappear from queries at once and are purged in the background.
When a user saves a conversation, your Python code must create a conversation item and update a summary item so that either both changes are saved or neither is. Which design lets you do this with a transactional batch in Cosmos DB for NoSQL?
APut the items in different containers that share a database
BGive the items different partition keys and set Strong consistency
CStore both items under the same partition key value
DWrite the items in parallel and retry whichever write fails
Show the answer and why
APut the items in different containers that share a database
Incorrect
A batch runs against one container and one partition key.
BGive the items different partition keys and set Strong consistency
Incorrect
Consistency levels govern reads; they don't make writes across partition keys atomic.
CStore both items under the same partition key value
Correct
A transactional batch groups point operations that share the same partition key and commits or rolls them back together.
DWrite the items in parallel and retry whichever write fails
Incorrect
Retrying doesn't make the two writes atomic; one can still succeed while the other fails.
Cosmos DB offers ACID transactions with snapshot isolation within one logical partition. Transactional batch, available in the SDKs, is the code-based way to use them; stored procedures are the JavaScript alternative.
Cosmos DB metrics for a container show about 3 percent of requests ending in 429, and users report no latency problems. Another container shows 20 percent 429s while its overall RU usage looks low. Which two statements are correct? (Choose TWO.)
AThe first container likely needs no action; 1 to 5 percent 429s with acceptable latency means its RU/s are fully used
BThe application sees every 429 in the metrics as an error
CDoubling the RU/s of the second container is the first step
DOn an autoscale container, a 429 always means it has already reached its maximum RU/s
EFor the second container, check Normalized RU Consumption by PartitionKeyRangeID for a hot partition
Show the answer and why
AThe first container likely needs no action; 1 to 5 percent 429s with acceptable latency means its RU/s are fully used
Correct
Microsoft treats this range as healthy when partitions are evenly distributed and latency is fine.
BThe application sees every 429 in the metrics as an error
Incorrect
The SDKs retry 429s automatically, typically up to nine times, so many never reach the application.
CDoubling the RU/s of the second container is the first step
Incorrect
Microsoft recommends finding the root cause, such as a hot partition, before changing RU/s.
DOn an autoscale container, a 429 always means it has already reached its maximum RU/s
Incorrect
Autoscale containers can return 429s even when they haven't scaled to the maximum RU/s.
EFor the second container, check Normalized RU Consumption by PartitionKeyRangeID for a hot partition
Correct
One physical partition at 100 percent while others are low points to a hot partition despite low overall usage.
A small share of 429s shows a workload using its provisioned throughput. A large share with low overall usage usually means a hot partition: one logical key range drawing most requests to one physical partition, which has only its share of the RU/s.
In a test container with a diskANN vector index, you load 300 embeddings. Vector queries work but cost far more RUs than the team expected from the documentation. Why?
AdiskANN indexes always cost more than full scans for the same query
BBelow 1,000 indexed vectors, Cosmos DB runs a full scan instead of using the index
CThe vector search feature takes 15 minutes to enable, and queries until then are full scans
DThe container uses shared database throughput
Show the answer and why
AdiskANN indexes always cost more than full scans for the same query
Incorrect
The index type isn't the cause; with this few vectors the query doesn't use the index at all.
BBelow 1,000 indexed vectors, Cosmos DB runs a full scan instead of using the index
Correct
quantizedFlat and diskANN need at least 1,000 vectors for accurate quantization; with fewer, a full scan with higher RU charges runs.
CThe vector search feature takes 15 minutes to enable, and queries until then are full scans
Incorrect
A query can't use vector search before the feature is enabled at all; the cost here comes from the small vector count.
DThe container uses shared database throughput
Incorrect
Vector indexing isn't supported on shared-throughput accounts at all, so this container couldn't have a vector index.
Quantization-based indexes need enough data. With fewer than 1,000 vectors, the engine falls back to scanning, so test RU costs with a realistically sized data set.