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:
Родитель
71e8cd3bd3
Коммит
5c11c442c6
|
@ -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/**"],
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
82
README.md
82
README.md
|
@ -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
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.
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче