Preferred locations are now properly honored + dev docs improvements (#181)

* initial fixes

* Add tests to make sure multi-region scenarios work

* Update dev docs to document the test config

* variable names are hard

* Add timeout setting
This commit is contained in:
Christopher Anderson 2018-11-06 21:50:13 -08:00 коммит произвёл GitHub
Родитель 71e8cd3bd3
Коммит 5c11c442c6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 191 добавлений и 95 удалений

2
.vscode/launch.json поставляемый
Просмотреть файл

@ -9,7 +9,7 @@
"request": "launch",
"name": "Mocha Tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": ["-u", "tdd", "--colors", "${workspaceFolder}/lib/test/**/*.js", "-g", ".*SessionContainer.*"],
"args": ["-u", "tdd", "--colors", "${workspaceFolder}/lib/test/**/*.js", "-g", ".*Location Cache.*"],
"internalConsoleOptions": "openOnSessionStart",
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/lib/**"],

22
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"problemMatcher": [
"$tsc",
"$tslint5"
]
},
{
"type": "npm",
"script": "compile",
"problemMatcher": [
"$tsc"
]
}
]
}

Просмотреть файл

@ -7,29 +7,29 @@ Database Service](https://azure.microsoft.com/en-us/services/cosmos-db/). This p
```js
// JavaScript
const cosmos = require('@azure/cosmos');
const cosmos = require("@azure/cosmos");
const CosmosClient = cosmos.CosmosClient;
const endpoint = "[hostendpoint]"; // Add your endpoint
const masterKey = "[database account masterkey]"; // Add the masterkey of the endpoint
const endpoint = "[hostendpoint]"; // Add your endpoint
const masterKey = "[database account masterkey]"; // Add the masterkey of the endpoint
const client = new CosmosClient({ endpoint, auth: { masterKey } });
const databaseDefinition = { id: 'sample database' };
const collectionDefinition = { id: 'sample collection' };
const documentDefinition = { id: 'hello world doc', content: 'Hello World!' };
const databaseDefinition = { id: "sample database" };
const collectionDefinition = { id: "sample collection" };
const documentDefinition = { id: "hello world doc", content: "Hello World!" };
async function helloCosmos() {
const { database: db } = await client.databases.create(databaseDefinition);
console.log('created db');
console.log("created db");
const { container } = await db.containers.create(collectionDefinition);
console.log('created collection');
console.log("created collection");
const { body } = await container.items.create(documentDefinition);
console.log('Created item with content: ', body.content);
console.log("Created item with content: ", body.content);
await db.delete();
console.log('Deleted database');
console.log("Deleted database");
}
helloCosmos().catch(err => {
@ -48,70 +48,14 @@ helloCosmos().catch(err => {
- [Partitioning](https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-partition-data)
- [API Documentation](https://docs.microsoft.com/en-us/javascript/api/%40azure/cosmos/?view=azure-node-latest)
## Installation
### Prerequisites
Install Node.js 6 or above and npm
[https://docs.npmjs.com/getting-started/installing-node](https://docs.npmjs.com/getting-started/installing-node)
The SDK is not supported in Node v4 or below. Those Node.js versions are out of support and not recommended for production. Our support will only cover maintained versions of Node.js.
To use the SDK, first [create an account](https://docs.microsoft.com/en-us/azure/cosmos-db/create-sql-api-nodejs) and follow [tutorial](https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-nodejs-application).
#### Note:
When connecting to the [emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator) from the SDK, SSL verification is disabled.
Follow these instructions to run the tests locally.
### Install
```bash
npm install @azure/cosmos
```
## Tests
### Prerequisites
1. Clone Azure/azure-cosmos-js repository
```bash
git clone https://github.com/azure/azure-cosmos-js.git
```
2. Install Node.js 6 or above and npm
[https://docs.npmjs.com/getting-started/installing-node](https://docs.npmjs.com/getting-started/installing-node)
3. [Cosmos DB emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator)
- Note: requires a windows machine or ability to run Windows container
4. Install dependencies
```bash
npm i # alias for npm install
```
5. Build the source
```bash
npm run build # compiles the typescript source, runs linting, creates webpack, creates docs
```
### Running the tests
```bash
npm run test # runs all tests
```
## Need Help?
Tweet us with #CosmosDB and we'll respond on Twitter. Be sure to check out the Microsoft Azure [Developer Forums on MSDN](https://social.msdn.microsoft.com/forums/azure/en-US/home?forum=AzureDocument) or the [Developer Forums on Stack Overflow](https://stackoverflow.com/questions/tagged/azure-cosmosdb) if you have trouble with the provided code.
## Contribute Code or Provide Feedback
If you would like to become an active contributor to this project please follow the instructions provided in [Azure Projects Contribution Guidelines](http://azure.github.io/guidelines.html).
For our rules and guidelines on contributing, please see [Microsoft's contributor guide].(https://docs.microsoft.com/en-us/contribute/).
For information on how build and test this repo, please see [./dev.md](./dev.md).
If you encounter any bugs with the library please file an issue in the [Issues](https://github.com/Azure/azure-cosmos-js/issues) section of the project.

73
dev.md
Просмотреть файл

@ -1,17 +1,61 @@
# Dev docs
Info on how to build the SDK and run the samples
```bash
# Info on how to build the SDK and run the samples
npm i # install dependencies and tools
npm run build # builds the project
npm run test # runs the tests
# see below prereqs, more commands, and config options
```
## Pre-reqs
- [Node v6 or above](https://nodejs.org/en/)
- Recommend using Node 8 LTS
- Recommend using a Node version manager ([nvm-windows](https://github.com/coreybutler/nvm-windows/releases), [nvm (mac/linux)](https://github.com/creationix/nvm/), [n (mac/linux)](https://github.com/tj/n))
- Recommend using Node 8 LTS
- Recommend using a Node version manager ([nvm-windows](https://github.com/coreybutler/nvm-windows/releases), [nvm (mac/linux)](https://github.com/creationix/nvm/), [n (mac/linux)](https://github.com/tj/n))
- npm (comes with Node)(all tooling is done via npm scripts)
- All OS's should be supported (but only tested on Windows so far)(Requires a Cosmos Emulator running on a Windows container on your machine or local network)
- All OS's should be supported (emulator only runs on windows)
- (Recommended) [VS Code](https://code.visualstudio.com/)
- Cosmos DB (Azure or Local Emulator) (emulator only works on Windows, right now, so mac/linux needs a cloud instance)
## Available commands
```
Lifecycle scripts included in @azure/cosmos:
test
mocha -r ./src/test/common/setup.ts ./lib/test/ --recursive --timeout 100000 -i -g .*ignore.js
available via `npm run-script`:
clean
rimraf lib
lint
tslint --project tsconfig.json
format
prettier --write --config .prettierrc.json "src/**/*.ts"
check-format
prettier --list-different --config .prettierrc.json "src/**/*.ts"
compile
echo Using TypeScript && tsc --version && tsc --pretty
compile-prod
echo Using TypeScript && tsc --version && tsc -p tsconfig.prod.json --pretty
docs
typedoc --excludePrivate --exclude "**/test/**" --mode file --out ./lib/docs ./src
pack
webpack -d
pack-prod
webpack -p
build
npm run clean && npm run check-format && npm run lint && npm run compile && npm run docs && npm run pack
build-prod
npm run clean && npm run check-format && npm run lint && npm run compile-prod && npm run docs && npm run pack-prod
test-ts
mocha -r ts-node/register -r ./src/test/common/setup.ts ./src/test/**/*.spec.ts --recursive --timeout 100000 -i -g .*ignore.js
test-browser
karma start ./karma.config.js --single-run
```
## Building the SDK
1. Install dependencies `npm i`
@ -19,12 +63,25 @@ Info on how to build the SDK and run the samples
## Testing the SDK
Only a subset of tests are working at the moment (due to API changes, theoretically 😉).
1. Build the SDK (see above)
2. Run all tests `npm run test`
You can also run the tests via VS Code. There should already be a launch.json for launching the mocha tests. You can modify the `-g` setting to run a specific test. (aka change `.*` to `validate database CRUD` or whatever your test cases are called)
The above assumes you have the local emulator installed. If you need to use a remote endpoint, check out the `ACCOUNT_HOST` and `ACCOUNT_KEY` below.
### Test config
Extra environment variables you can use:
- `MOCHA_TIMEOUT`: time in milliseconds before timeout (default is different per test, mostly 10-20 seconds). Useful to set to 999999 during debugging.
- `ACCOUNT_HOST`: account endpoint for testing (default is the emulator running on localhost:8081
- `ACCOUNT_KEY`: masterkey for testing (default is the emulators default key)
- `TESTS_MULTIREGION`: enables tests that require a multi-region write enabled database account with at least two regions.
## VS Code
You can also run the tests via VS Code. There should already be a launch.json for launching the mocha tests. You can modify the `-g` setting to run a specific test. (aka change `.*` to `.*validate database CRUD.*` or whatever your test cases are called)
You can also build via the configured tasks (`build` does a full build, and `compile` just does a typescript compile with no linting, formatting, etc.)
# Samples
@ -32,4 +89,4 @@ Build the SDK and make sure the tests run before you try any samples (they depen
- [TodoApp](./samples/TodoApp)
We recommend using [VS code's multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) for testing the samples, especially if you're using the samples to test the SDK. There is a `launch.json` for the samples thave have been updated and multi-root workspaces will show all `launch.json`s.
We recommend using [VS code's multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) for testing the samples, especially if you're using the samples to test the SDK. There is a `launch.json` for the samples thave have been updated and multi-root workspaces will show all `launch.json`s.

30
package-lock.json сгенерированный
Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "@azure/cosmos",
"version": "2.0.3",
"version": "2.0.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -3370,12 +3370,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -3390,17 +3392,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -3517,7 +3522,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -3529,6 +3535,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -3543,6 +3550,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -3550,12 +3558,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@ -3574,6 +3584,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -3654,7 +3665,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -3666,6 +3678,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -3787,6 +3800,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",

Просмотреть файл

@ -121,7 +121,7 @@ export class LocationCache {
if (currentInfo.orderedWriteLocations.length > 0) {
locationIndex = Math.min(locationIndex % 2, currentInfo.orderedWriteLocations.length - 1);
const writeLocation = currentInfo.orderedWriteLocations[locationIndex];
return currentInfo.availableWriteEndpointByLocation.get(writeLocation);
return currentInfo.availableWriteEndpointByLocation.get(LocationCache.normalizeLocationName(writeLocation));
} else {
return this.defaultEndpoint;
}
@ -138,7 +138,9 @@ export class LocationCache {
let canRefreshInBackground = true;
const currentInfo = this.locationInfo;
const mostPreferredLocation: string = currentInfo.preferredLocations ? currentInfo.preferredLocations[0] : null;
const mostPreferredLocation: string = LocationCache.normalizeLocationName(
currentInfo.preferredLocations ? currentInfo.preferredLocations[0] : null
);
if (this.options.connectionPolicy.EnableEndpointDiscovery) {
// Refresh if client opts-in to use multiple write locations, but it's not enabled on the server.
@ -301,7 +303,7 @@ export class LocationCache {
const unavailableEndpoints: string[] = [];
if (this.options.connectionPolicy.PreferredLocations) {
for (const location of this.options.connectionPolicy.PreferredLocations) {
const endpoint = endpointsByLocation.get(location);
const endpoint = endpointsByLocation.get(LocationCache.normalizeLocationName(location));
if (endpoint) {
if (this.isEndpointUnavailable(endpoint, expectedAvailableOperation)) {
unavailableEndpoints.push(endpoint);
@ -317,8 +319,9 @@ export class LocationCache {
}
} else {
for (const location of orderedLocations) {
if (endpointsByLocation.has(location)) {
endpoints.push(endpointsByLocation.get(location));
const normalizedLocationName = LocationCache.normalizeLocationName(location);
if (endpointsByLocation.has(normalizedLocationName)) {
endpoints.push(endpointsByLocation.get(normalizedLocationName));
}
}
}
@ -341,9 +344,9 @@ export class LocationCache {
if (!location) {
continue;
}
const fixedUpLocation = location.name.toLowerCase().replace(/ /g, "");
endpointsByLocation.set(fixedUpLocation, location.databaseAccountEndpoint);
orderedLocations.push(fixedUpLocation);
const normalizedLocationName = LocationCache.normalizeLocationName(location.name);
endpointsByLocation.set(normalizedLocationName, location.databaseAccountEndpoint);
orderedLocations.push(normalizedLocationName);
}
return { endpointsByLocation, orderedLocations };
}
@ -351,4 +354,8 @@ export class LocationCache {
private canUpdateCache(timestamp: Date): boolean {
return new Date(Date.now() - Constants.DefaultUnavailableLocationExpirationTimeMS) > timestamp;
}
private static normalizeLocationName(location: string): string {
return location ? location.toLowerCase().replace(/ /g, "") : null;
}
}

Просмотреть файл

@ -0,0 +1,52 @@
import assert from "assert";
import { CosmosClient } from "../../CosmosClient";
import { ConnectionPolicy, DatabaseAccount } from "../../documents";
import { endpoint, masterKey } from "../common/_testConfig";
// This test requires a multi-region write enabled account with at least two regions.
(process.env.TESTS_MULTIREGION ? describe : describe.skip)("Multi-region tests", function() {
this.timeout(process.env.MOCHA_TIMEOUT || "30000");
let preferredLocations: string[] = [];
let dbAccount: DatabaseAccount;
before(async function() {
const client = new CosmosClient({ endpoint, auth: { masterKey } });
({ body: dbAccount } = await client.getDatabaseAccount());
// We reverse the order of the preferred locations list to make sure
// we don't just follow the order we got back from the server
preferredLocations = dbAccount.readableLocations.map(v => v.name).reverse();
assert(
preferredLocations.length > 1,
"Not a multi-region account. Please add a region before running this test again."
);
});
it("Preferred locations should be honored for readEndpoint", async function() {
const connectionPolicy = new ConnectionPolicy();
connectionPolicy.PreferredLocations = preferredLocations;
const client = new CosmosClient({ endpoint, auth: { masterKey }, connectionPolicy });
const currentReadEndpoint = await client.getReadEndpoint();
assert(
currentReadEndpoint.includes(preferredLocations[0].toLowerCase().replace(/ /g, "")),
"The readendpoint should be the first preferred location"
);
});
it("Preferred locations should be honored for writeEndpoint", async function() {
assert(
dbAccount.enableMultipleWritableLocations,
"MultipleWriteableLocations must be set on your database account for this test to work"
);
const connectionPolicy = new ConnectionPolicy();
connectionPolicy.PreferredLocations = preferredLocations;
connectionPolicy.UseMultipleWriteLocations = true;
const client = new CosmosClient({ endpoint, auth: { masterKey }, connectionPolicy });
const currentWriteEndpoint = await client.getWriteEndpoint();
assert(
currentWriteEndpoint.includes(preferredLocations[0].toLowerCase().replace(/ /g, "")),
"The writeendpoint should be the first preferred location"
);
});
});

Просмотреть файл

@ -6,7 +6,7 @@ import * as assert from "assert";
import { Constants, ResourceType } from "../../common";
const scenarios: Scenario[] = [];
const regions = ["westus", "eastus", "eastus2", "southcentralus", "seasia"];
const regions = ["westus", "East US", "eastus2", "south Centralus", "sEasIa"];
interface Scenario {
defaultEndpoint?: string;