Skip to content

Commit

Permalink
[Cosmos] Priority level request option (#26393)
Browse files Browse the repository at this point in the history
### Packages impacted by this PR
@azure/cosmos

### Issues associated with this PR


### Describe the problem that is addressed by this PR
This PR adds PriorityLevel to Request options and passes it as header to
the backend. This header is used to set priority to requests and
throttle low priority requests before high priority requests are
throttled.

### What are the possible designs available to address the problem? If
there are more than one possible design, why was the one in this PR
chosen?


### Are there test cases added in this PR? _(If not, why?)_
Yes

### Provide a list of related PRs _(if any)_
Corresponding [.Net
PR](Azure/azure-cosmos-dotnet-v3#3672)
Corresponding [Java
PR](Azure/azure-sdk-for-java#34121)

### Command used to generate this PR:**_(Applicable only to SDK release
request PRs)_

### Checklists
- [x] Added impacted package name to the issue description
- [ ] Does this PR needs any fixes in the SDK Generator?** _(If so,
create an Issue in the
[Autorest/typescript](https://github.com/Azure/autorest.typescript)
repository and link it here)_
- [ ] Added a changelog (if necessary)
  • Loading branch information
balalakshmin authored Sep 5, 2023
1 parent d539002 commit 2dfe32b
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 0 deletions.
8 changes: 8 additions & 0 deletions sdk/cosmosdb/cosmos/review/cosmos.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ export const Constants: {
BatchContinueOnError: string;
DedicatedGatewayPerRequestCacheStaleness: string;
ForceRefresh: string;
PriorityLevel: string;
};
WritableLocations: string;
ReadableLocations: string;
Expand Down Expand Up @@ -1497,6 +1498,12 @@ export enum PluginOn {
// @public
export type PrimitivePartitionKeyValue = string | number | boolean | NullPartitionKeyType | NonePartitionKeyType;

// @public
export enum PriorityLevel {
High = "High",
Low = "Low"
}

// @public (undocumented)
export interface QueryInfo {
// (undocumented)
Expand Down Expand Up @@ -2021,6 +2028,7 @@ export interface SharedOptions {
abortSignal?: AbortSignal_2;
initialHeaders?: CosmosHeaders;
maxIntegratedCacheStalenessInMs?: number;
priorityLevel?: PriorityLevel;
sessionToken?: string;
}

Expand Down
3 changes: 3 additions & 0 deletions sdk/cosmosdb/cosmos/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ export const Constants = {

// Cache Refresh header
ForceRefresh: "x-ms-force-refresh",

// Priority Based throttling header
PriorityLevel: "x-ms-cosmos-priority-level",
},

// GlobalDB related constants
Expand Down
20 changes: 20 additions & 0 deletions sdk/cosmosdb/cosmos/src/documents/PriorityLevel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/**
* Represents Priority Level associated with each Azure Cosmos DB client requests.<br>
* The Low priority requests are always throttled before any High priority requests.
*
* By default all requests are considered as High priority requests.
*
* See https://aka.ms/CosmosDB/PriorityBasedExecution for more detailed documentation on Priority based throttling.
*/
export enum PriorityLevel {
/**
* High Priority requests are throttled after Low priority requests.
*/
High = "High",
/**
* Low Priority requests are throttled before High priority requests.
*/
Low = "Low",
}
1 change: 1 addition & 0 deletions sdk/cosmosdb/cosmos/src/documents/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export * from "./PartitionKeyDefinitionVersion";
export * from "./PartitionKeyKind";
export * from "./PartitionKeyDefinition";
export * from "./PermissionMode";
export * from "./PriorityLevel";
export * from "./TriggerOperation";
export * from "./TriggerType";
export * from "./UserDefinedFunctionType";
Expand Down
1 change: 1 addition & 0 deletions sdk/cosmosdb/cosmos/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
PartitionKeyDefinition,
PartitionKeyDefinitionVersion,
PermissionMode,
PriorityLevel,
TriggerOperation,
TriggerType,
UserDefinedFunctionType,
Expand Down
9 changes: 9 additions & 0 deletions sdk/cosmosdb/cosmos/src/request/SharedOptions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/// <reference lib="dom" />
import { PriorityLevel } from "../documents/PriorityLevel";
import { CosmosHeaders } from "../index";
import { AbortSignal } from "node-abort-controller";

Expand Down Expand Up @@ -33,4 +34,12 @@ export interface SharedOptions {
* <p>Cache Staleness is supported in milliseconds granularity. Anything smaller than milliseconds will be ignored.</p>
*/
maxIntegratedCacheStalenessInMs?: number;

/**
* Priority Level (Low/High) for each request.
* Low priority requests are always throttled before any high priority requests.
*
* <p>Default value is null. By default all requests are of High priority</p>
*/
priorityLevel?: PriorityLevel;
}
4 changes: 4 additions & 0 deletions sdk/cosmosdb/cosmos/src/request/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ export async function getHeaders({
headers[Constants.HttpHeaders.ConsistencyLevel] = options.consistencyLevel;
}

if (options.priorityLevel) {
headers[Constants.HttpHeaders.PriorityLevel] = options.priorityLevel;
}

if (options.maxIntegratedCacheStalenessInMs && resourceType === ResourceType.item) {
if (typeof options.maxIntegratedCacheStalenessInMs === "number") {
headers[Constants.HttpHeaders.DedicatedGatewayPerRequestCacheStaleness] =
Expand Down
108 changes: 108 additions & 0 deletions sdk/cosmosdb/cosmos/test/public/functional/item/item.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
PartitionKeyDefinitionVersion,
PartitionKeyKind,
} from "../../../../src/documents";
import { PriorityLevel } from "../../../../src/documents/PriorityLevel";
import { getCurrentTimestampInMs } from "../../../../src/utils/time";

/**
Expand Down Expand Up @@ -915,3 +916,110 @@ describe("patch operations", function () {
});
});
});

describe("Item CRUD with priority", function (this: Suite) {
this.timeout(process.env.MOCHA_TIMEOUT || 10000);
beforeEach(async function () {
await removeAllDatabases();
});
const documentCRUDTest = async function (isUpsertTest: boolean): Promise<void> {
// create database
const database = await getTestDatabase("sample 中文 database");
// create container
const { resource: containerdef } = await database.containers.create(
{ id: "sample container" },
{ priorityLevel: PriorityLevel.Low }
);
const container: Container = database.container(containerdef.id);

// read items
const { resources: items } = await container.items
.readAll({ priorityLevel: PriorityLevel.Low })
.fetchAll();
assert(Array.isArray(items), "Value should be an array");

// create an item
const beforeCreateDocumentsCount = items.length;
const itemDefinition: TestItem = {
name: "sample document",
foo: "bar",
key: "value",
replace: "new property",
};

const { resource: document } = await createOrUpsertItem(
container,
itemDefinition,
{ priorityLevel: PriorityLevel.Low },
isUpsertTest
);
assert.equal(document.name, itemDefinition.name);
assert(document.id !== undefined);
// read documents after creation
const { resources: documents2 } = await container.items
.readAll({ priorityLevel: PriorityLevel.Low })
.fetchAll();
assert.equal(
documents2.length,
beforeCreateDocumentsCount + 1,
"create should increase the number of documents"
);
// query documents
const querySpec = {
query: "SELECT * FROM root r WHERE r.id=@id",
parameters: [
{
name: "@id",
value: document.id,
},
],
};
const { resources: results } = await container.items
.query(querySpec, { priorityLevel: PriorityLevel.Low })
.fetchAll();
assert(results.length > 0, "number of results for the query should be > 0");

// replace document
document.name = "replaced document";
document.foo = "not bar";
const { resource: replacedDocument } = await replaceOrUpsertItem(
container,
document,
{ priorityLevel: PriorityLevel.Low },
isUpsertTest
);
assert.equal(
replacedDocument.name,
"replaced document",
"document name property should change"
);
assert.equal(replacedDocument.foo, "not bar", "property should have changed");
assert.equal(document.id, replacedDocument.id, "document id should stay the same");
// read document
const response2 = await container
.item(replacedDocument.id, undefined)
.read<TestItem>({ priorityLevel: PriorityLevel.Low });
const document2 = response2.resource;
assert.equal(replacedDocument.id, document2.id);
assert.equal(typeof response2.requestCharge, "number");
// delete document
await container
.item(replacedDocument.id, undefined)
.delete({ priorityLevel: PriorityLevel.Low });

// read documents after deletion
const response = await container
.item(replacedDocument.id, undefined)
.read({ priorityLevel: PriorityLevel.Low });
assert.equal(response.statusCode, 404, "response should return error code 404");
assert.equal(response.resource, undefined);
};

it("Should do document CRUD operations successfully", async function () {
await documentCRUDTest(false);
});

it("Should do document CRUD operations successfully with upsert", async function () {
await documentCRUDTest(true);
});
});

0 comments on commit 2dfe32b

Please sign in to comment.