UNPKG

@azure/cosmos

Version:
74 lines 3.86 kB
// 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