@azure/cosmos
Version:
Microsoft Azure Cosmos DB Service Node.js SDK for NOSQL API
74 lines • 3.86 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { MetadataLookUpType } from "../CosmosDiagnostics.js";
import { getIdFromLink } from "../common/helper.js";
import { withMetadataDiagnostics } from "../utils/diagnostics.js";
import { createCompleteRoutingMap } from "./CollectionRoutingMapFactory.js";
import { hashPartitionKey, binarySearchOnPartitionKeyRanges } from "../utils/hashing/hash.js";
/** @hidden */
export class PartitionKeyRangeCache {
clientContext;
// Resolved, known-good routing maps. Only successful fetches are published here.
collectionRoutingMapByCollectionId;
// In-flight fetches, so concurrent lookups (cold or forceRefresh) dedupe to a single request.
pendingByCollectionId;
constructor(clientContext) {
this.clientContext = clientContext;
this.collectionRoutingMapByCollectionId = {};
this.pendingByCollectionId = {};
}
/**
* Finds or Instantiates the requested Collection Routing Map
* @param collectionLink - Requested collectionLink
* @hidden
*/
async onCollectionRoutingMap(collectionLink, diagnosticNode, forceRefresh = false) {
const collectionId = getIdFromLink(collectionLink);
const cached = this.collectionRoutingMapByCollectionId[collectionId];
if (cached !== undefined && !forceRefresh) {
return cached;
}
// Dedupe concurrent fetches (cold or forceRefresh) onto a single in-flight request. The map
// is published only on success, so a failed/in-flight fetch never poisons the cache or
// discards the last known-good map; on failure all waiters get the error and the next call
// retries. The prior good map keeps serving cache hits until the refresh resolves.
if (this.pendingByCollectionId[collectionId] === undefined) {
this.pendingByCollectionId[collectionId] = this.requestCollectionRoutingMap(collectionLink, diagnosticNode)
.then((map) => {
this.collectionRoutingMapByCollectionId[collectionId] = Promise.resolve(map);
return map;
})
.finally(() => {
delete this.pendingByCollectionId[collectionId];
});
}
return this.pendingByCollectionId[collectionId];
}
/**
* Given the query ranges and a collection, invokes the callback on the list of overlapping partition key ranges
* @hidden
*/
async getOverlappingRanges(collectionLink, queryRange, diagnosticNode, forceRefresh = false) {
const crm = await this.onCollectionRoutingMap(collectionLink, diagnosticNode, forceRefresh);
return crm.getOverlappingRanges(queryRange);
}
async requestCollectionRoutingMap(collectionLink, diagnosticNode) {
const { resources } = await withMetadataDiagnostics(async (metadataDiagnostics) => {
return this.clientContext
.queryPartitionKeyRanges(collectionLink)
.fetchAllInternal(metadataDiagnostics);
}, diagnosticNode, MetadataLookUpType.PartitionKeyRangeLookUp);
return createCompleteRoutingMap(resources.map((r) => [r, true]));
}
/**
* Given a partition key, returns the partition key range id
* @internal
*/
async getPartitionKeyRangeIdFromPartitionKey(collectionLink, partitionKey, partitionKeyDefinition, diagnosticNode) {
const hashedPartitionKey = hashPartitionKey(partitionKey, partitionKeyDefinition);
const partitionKeyRanges = (await this.onCollectionRoutingMap(collectionLink, diagnosticNode)).getOrderedParitionKeyRanges();
const partitionKeyRangeId = binarySearchOnPartitionKeyRanges(partitionKeyRanges, hashedPartitionKey);
return partitionKeyRangeId;
}
}
//# sourceMappingURL=partitionKeyRangeCache.js.map