Skip to content

Commit

Permalink
[Cosmos] Remove binary-search-bounds (#5417)
Browse files Browse the repository at this point in the history
  • Loading branch information
southpolesteve authored Oct 8, 2019
1 parent f028c79 commit 560b14f
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 156 deletions.
1 change: 0 additions & 1 deletion sdk/cosmosdb/cosmos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
"@azure/abort-controller": "1.0.0-preview.2",
"@azure/cosmos-sign": "^1.0.2",
"@types/debug": "^4.1.4",
"binary-search-bounds": "^2.0.3",
"crypto-hash": "^1.1.0",
"debug": "^4.1.1",
"fast-json-stable-stringify": "^2.0.0",
Expand Down
6 changes: 1 addition & 5 deletions sdk/cosmosdb/cosmos/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ export default [
"debug",
"@azure/abort-controller",
"node-fetch",
"atob",
"binary-search-bounds",
"priorityqueuejs",
"semaphore",
"crypto-hash",
Expand All @@ -25,7 +23,6 @@ export default [
globals: {
"universal-user-agent": "universalUserAgent",
"@azure/cosmos-sign": "cosmosSign",
"binary-search-bounds": "bs",
"crypto-hash": "cryptoHash",
"fast-json-stable-stringify": "stableStringify",
"uuid/v4": "uuid",
Expand All @@ -34,8 +31,7 @@ export default [
tslib: "tslib_1",
debug: "debugLib",
priorityqueuejs: "PriorityQueue",
semaphore: "semaphore",
atob: "atob"
semaphore: "semaphore"
}
},
plugins: [resolve()]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as bs from "binary-search-bounds";
import PriorityQueue from "priorityqueuejs";
import semaphore from "semaphore";
import { ClientContext } from "../ClientContext";
Expand All @@ -9,7 +8,7 @@ import { StatusCodes, SubStatusCodes } from "../common/statusCodes";
import { FeedOptions, Response } from "../request";
import { PartitionedQueryExecutionInfo } from "../request/ErrorResponse";
import { QueryRange } from "../routing/QueryRange";
import { PARITIONKEYRANGE, SmartRoutingMapProvider } from "../routing/smartRoutingMapProvider";
import { SmartRoutingMapProvider } from "../routing/smartRoutingMapProvider";
import { CosmosHeaders } from "./CosmosHeaders";
import { DocumentProducer } from "./documentProducer";
import { ExecutionContext } from "./ExecutionContext";
Expand Down Expand Up @@ -120,27 +119,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont
const targetPartitionQueryExecutionContextList: DocumentProducer[] = [];

if (this.requestContinuation) {
// Need to create the first documentProducer with the suppliedCompositeContinuationToken
try {
const suppliedCompositeContinuationToken = JSON.parse(this.requestContinuation);
filteredPartitionKeyRanges = this.getPartitionKeyRangesForContinuation(
suppliedCompositeContinuationToken,
targetPartitionRanges
);
if (filteredPartitionKeyRanges.length > 0) {
targetPartitionQueryExecutionContextList.push(
this._createTargetPartitionQueryExecutionContext(
filteredPartitionKeyRanges[0],
suppliedCompositeContinuationToken.token
)
);
// Slicing the first element off, since we already made a documentProducer for it
filteredPartitionKeyRanges = filteredPartitionKeyRanges.slice(1);
}
} catch (e) {
this.err = e;
this.sem.leave();
}
throw new Error("Continuation tokens are not yet supported for cross partition queries");
} else {
filteredPartitionKeyRanges = targetPartitionRanges;
}
Expand Down Expand Up @@ -195,37 +174,6 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont
dp1: DocumentProducer,
dp2: DocumentProducer
): number;
// TODO: any TODO: any
public getPartitionKeyRangesForContinuation(
suppliedCompositeContinuationToken: any,
partitionKeyRanges: any
) {
const startRange: any = {}; // TODO: any
startRange[PARITIONKEYRANGE.MinInclusive] = suppliedCompositeContinuationToken.range.min;
startRange[PARITIONKEYRANGE.MaxExclusive] = suppliedCompositeContinuationToken.range.max;

const vbCompareFunction = (x: any, y: any) => {
// TODO: any
if (x[PARITIONKEYRANGE.MinInclusive] > y[PARITIONKEYRANGE.MinInclusive]) {
return 1;
}
if (x[PARITIONKEYRANGE.MinInclusive] < y[PARITIONKEYRANGE.MinInclusive]) {
return -1;
}

return 0;
};

const minIndex = bs.le(partitionKeyRanges, startRange, vbCompareFunction);
// that's an error

if (minIndex > 0) {
throw new Error("BadRequestException: InvalidContinuationToken");
}

// return slice of the partition key ranges
return partitionKeyRanges.slice(minIndex, partitionKeyRanges.length - minIndex);
}

private _decrementInitiationLock() {
// decrements waitingForInternalExecutionContexts
Expand All @@ -252,7 +200,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont
private async _onTargetPartitionRanges() {
// invokes the callback when the target partition ranges are ready
const parsedRanges = this.partitionedQueryExecutionInfo.queryRanges;
const queryRanges = parsedRanges.map((item: any) => QueryRange.parseFromDict(item)); // TODO: any
const queryRanges = parsedRanges.map((item) => QueryRange.parseFromDict(item));
return this.routingProvider.getOverlappingRanges(this.collectionLink, queryRanges);
}

Expand Down
9 changes: 9 additions & 0 deletions sdk/cosmosdb/cosmos/src/routing/QueryRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ export class QueryRange {
return false;
}

public isFullRange() {
return (
this.min === Constants.EffectiveParitionKeyConstants.MinimumInclusiveEffectivePartitionKey &&
this.max === Constants.EffectiveParitionKeyConstants.MaximumExclusiveEffectivePartitionKey &&
this.isMinInclusive === true &&
this.isMaxInclusive === false
);
}

public isEmpty() {
return !(this.isMinInclusive && this.isMaxInclusive) && this.min === this.max;
}
Expand Down
107 changes: 37 additions & 70 deletions sdk/cosmosdb/cosmos/src/routing/inMemoryCollectionRoutingMap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as bs from "binary-search-bounds"; // TODO: missing types
import { Constants } from "../common";
import { QueryRange } from "./QueryRange";

Expand Down Expand Up @@ -32,98 +31,66 @@ export class InMemoryCollectionRoutingMap {
return this.orderedPartitionKeyRanges;
}

public getRangeByEffectivePartitionKey(effectivePartitionKeyValue: string) {
if (
Constants.EffectiveParitionKeyConstants.MinimumInclusiveEffectivePartitionKey ===
effectivePartitionKeyValue
) {
return this.orderedPartitionKeyRanges[0];
}

if (
Constants.EffectiveParitionKeyConstants.MaximumExclusiveEffectivePartitionKey ===
effectivePartitionKeyValue
) {
return undefined;
}

const sortedLow = this.orderedRanges.map((r) => {
return { v: r.min, b: !r.isMinInclusive };
});

const index = bs.le(
sortedLow,
{ v: effectivePartitionKeyValue, b: true },
InMemoryCollectionRoutingMap._vbCompareFunction
);
// that's an error
if (index < 0) {
throw new Error(
"error in collection routing map, queried partition key is less than the start range."
);
}

return this.orderedPartitionKeyRanges[index];
}

private static _vbCompareFunction(x: any, y: any) {
// TODO: What is x & y? A bs type?
if (x.v > y.v) {
return 1;
}
if (x.v < y.v) {
return -1;
}
if (x.b > y.b) {
return 1;
}
if (x.b < y.b) {
return -1;
}
return 0;
}

public getOverlappingRanges(providedQueryRanges: QueryRange | QueryRange[]) {
// TODO This code has all kinds of smells. Multiple iterations and sorts just to grab overlapping ranges
// stfaul attempted to bring it down to one for-loop and failed
const pqr: QueryRange[] = Array.isArray(providedQueryRanges)
? providedQueryRanges
: [providedQueryRanges];
const minToPartitionRange: any = {}; // TODO: any
const sortedLow = this.orderedRanges.map((r) => {
return { v: r.min, b: !r.isMinInclusive };
});
const sortedHigh = this.orderedRanges.map((r) => {
return { v: r.max, b: r.isMaxInclusive };
});

// this for loop doesn't invoke any async callback
for (const queryRange of pqr) {
if (queryRange.isEmpty()) {
continue;
}
const minIndex = bs.le(
sortedLow,
{ v: queryRange.min, b: !queryRange.isMinInclusive },
InMemoryCollectionRoutingMap._vbCompareFunction
);

if (queryRange.isFullRange()) {
return this.orderedPartitionKeyRanges;
}

const minIndex = this.orderedRanges.findIndex((range) => {
if (queryRange.min > range.min && queryRange.min < range.max) {
return true;
}
if (queryRange.min === range.min) {
return true;
}
if (queryRange.min === range.max) {
return true;
}
});

if (minIndex < 0) {
throw new Error(
"error in collection routing map, queried value is less than the start range."
);
}

const maxIndex = bs.ge(
sortedHigh,
{ v: queryRange.max, b: queryRange.isMaxInclusive },
InMemoryCollectionRoutingMap._vbCompareFunction
);
if (maxIndex > sortedHigh.length) {
// Start at the end and work backwards
let maxIndex: number;
for (let i = this.orderedRanges.length - 1; i >= 0; i--) {
const range = this.orderedRanges[i];
if (queryRange.max > range.min && queryRange.max < range.max) {
maxIndex = i;
break;
}
if (queryRange.max === range.min) {
maxIndex = i;
break;
}
if (queryRange.max === range.max) {
maxIndex = i;
break;
}
}

if (maxIndex > this.orderedRanges.length) {
throw new Error(
"error in collection routing map, queried value is greater than the end range."
);
}

// the for loop doesn't invoke any async callback
for (let j = minIndex; j < maxIndex + 1; j++) {
if (queryRange.overlaps(this.orderedRanges[j])) {
minToPartitionRange[
Expand Down
6 changes: 3 additions & 3 deletions sdk/cosmosdb/cosmos/src/routing/partitionKeyRangeCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export class PartitionKeyRangeCache {
/**
* Given the query ranges and a collection, invokes the callback on the list of overlapping partition key ranges
* @param collectionLink
* @param queryRanges
* @param queryRange
* @ignore
*/
public async getOverlappingRanges(collectionLink: string, queryRanges: QueryRange) {
public async getOverlappingRanges(collectionLink: string, queryRange: QueryRange) {
const crm = await this.onCollectionRoutingMap(collectionLink);
return crm.getOverlappingRanges(queryRanges);
return crm.getOverlappingRanges(queryRange);
}

private async requestCollectionRoutingMap(collectionLink: string) {
Expand Down
6 changes: 0 additions & 6 deletions sdk/cosmosdb/cosmos/src/typings/binary-search-bounds.d.ts

This file was deleted.

5 changes: 3 additions & 2 deletions sdk/cosmosdb/cosmos/test/integration/crossPartition.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ describe("Cross Partition", function() {
documentDefinitions.length,
"actual results length doesn't match with expected results length."
);
if (expectedOrderIds)
{assert.deepStrictEqual(actualResults.map((doc) => doc.id || doc), expectedOrderIds);}
if (expectedOrderIds) {
assert.deepStrictEqual(actualResults.map((doc) => doc.id || doc), expectedOrderIds);
}
};

const validateFetchAll = async function(
Expand Down
14 changes: 0 additions & 14 deletions sdk/cosmosdb/cosmos/test/unit/inMemoryCollectionRoutingMap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,6 @@ describe("InMemoryCollectionRoutingMap Tests", function() {
assert.equal(3, collectionRoutingMap.orderedPartitionInfo[3]);
});

it("validate getRangeByEffectivePartitionKey", function() {
assert.equal("0", collectionRoutingMap.getRangeByEffectivePartitionKey("").id);
assert.equal("0", collectionRoutingMap.getRangeByEffectivePartitionKey("0000000000").id);
assert.equal("1", collectionRoutingMap.getRangeByEffectivePartitionKey("0000000030").id);
assert.equal("1", collectionRoutingMap.getRangeByEffectivePartitionKey("0000000031").id);
assert.equal("3", collectionRoutingMap.getRangeByEffectivePartitionKey("0000000071").id);
});

// // TODO: bad practice to test implementation details
// it("validate getRangeByPartitionKeyRangeId", function () {
// assert.equal("0", collectionRoutingMap.getRangeByPartitionKeyRangeId(0).id);
// assert.equal("1", collectionRoutingMap.getRangeByPartitionKeyRangeId(1).id);
// });

it("validate getOverlappingRanges", function() {
const completeRange = new QueryRange("", "FF", true, false);

Expand Down

0 comments on commit 560b14f

Please sign in to comment.