Skip to content

Commit ea1613b

Browse files
authored
Merge pull request #324 from ml054/v5.2
sync with java: dee478bacb488851da66d3b849f93e84b339e00d
2 parents 3a0316e + 183f3ee commit ea1613b

File tree

13 files changed

+181
-35
lines changed

13 files changed

+181
-35
lines changed

src/Documents/Session/Tokens/GroupByKeyToken.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export class GroupByKeyToken extends QueryToken {
2323
}
2424

2525
writer
26-
.append(" as ")
27-
.append(this._projectedName);
26+
.append(" as '")
27+
.append(this._projectedName)
28+
.append("'");
2829
}
2930
}

src/Documents/Subscriptions/SubscriptionWorker.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ export class SubscriptionWorker<T extends object> implements IDisposable {
240240

241241
private _ensureParser(socket: Socket) {
242242
const keysTransformProfile = getTransformJsonKeysProfile(
243-
this._revisions
244-
? "SubscriptionRevisionsResponsePayload"
243+
this._revisions
244+
? "SubscriptionRevisionsResponsePayload"
245245
: "SubscriptionResponsePayload", this._store.conventions);
246246

247247
this._parser = stream.pipeline([
@@ -438,7 +438,7 @@ export class SubscriptionWorker<T extends object> implements IDisposable {
438438
}
439439

440440
const lastReceivedChangeVector = batch.initialize(incomingBatch);
441-
notifiedSubscriber = this._emitBatchAndWaitForProcessing(batch)
441+
notifiedSubscriber = this._emitBatchAndWaitForProcessing(batch)
442442
.catch((err) => {
443443
this._logger.error(err, "Subscription " + this._options.subscriptionName
444444
+ ". Subscriber threw an exception on document batch");
@@ -580,9 +580,9 @@ export class SubscriptionWorker<T extends object> implements IDisposable {
580580
return data.value;
581581
}
582582
}
583-
583+
584584
return new Promise<void>((resolve, reject) => {
585-
stream.once("readable", readableListener);
585+
stream.once("readable", readableListener);
586586
stream.once("error", errorHandler);
587587
stream.once("end", endHandler);
588588

@@ -592,7 +592,7 @@ export class SubscriptionWorker<T extends object> implements IDisposable {
592592
resolve();
593593
}
594594

595-
function errorHandler(err) {
595+
function errorHandler(err) {
596596
stream.removeListener("readable", readableListener);
597597
stream.removeListener("end", endHandler);
598598
reject(err);
@@ -646,7 +646,8 @@ export class SubscriptionWorker<T extends object> implements IDisposable {
646646
const curTopology = reqEx.getTopologyNodes();
647647
const nextNodeIndex = (this._forcedTopologyUpdateAttempts++) % curTopology.length;
648648
try {
649-
this._redirectNode = curTopology[nextNodeIndex];
649+
const indexAndNode = await reqEx.getRequestedNode(curTopology[nextNodeIndex].clusterTag, true);
650+
this._redirectNode = indexAndNode.currentNode;
650651

651652
this._logger.info("Subscription " + this._options.subscriptionName + ". Will modify redirect node from null to " + this._redirectNode.clusterTag);
652653
} catch (e) {
@@ -759,7 +760,7 @@ export class SubscriptionWorker<T extends object> implements IDisposable {
759760
public on(event: "connectionRetry",
760761
handler: (error?: Error) => void);
761762
public on(event: EventTypes,
762-
handler:
763+
handler:
763764
((batchOrError: SubscriptionBatch<T>, callback: EmptyCallback) => void)
764765
| ((error: Error) => void)) {
765766
this._emitter.on(event, handler);

src/Exceptions/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ export function getError(
4646
}
4747

4848
export type RavenErrorType = "RavenException"
49+
| "RavenTimeoutException"
4950
| "NotSupportedException"
51+
| "IndexCompactionInProgressException"
5052
| "InvalidOperationException"
5153
| "InvalidArgumentException"
5254
| "ErrorResponseException"
@@ -176,8 +178,8 @@ export class ExceptionDispatcher {
176178
return getError("ConcurrencyException", schema.error, inner);
177179
}
178180

179-
const error =
180-
schema.error + os.EOL
181+
const error =
182+
schema.error + os.EOL
181183
+ "The server at " + schema.url + " responded with status code: " + code;
182184

183185
const determinedType = this._getType(typeAsString) as RavenErrorType;
@@ -200,6 +202,8 @@ export class ExceptionDispatcher {
200202
const determinedType = this._getType(schema.type) as RavenErrorType;
201203
errorToThrow = getError(determinedType || "RavenException", schema.error);
202204
}
205+
206+
ExceptionDispatcher._fillException(errorToThrow, schema);
203207
} catch (errThrowing) {
204208
errorToThrow = getError("RavenException", errThrowing.message, errThrowing);
205209
} finally {
@@ -209,6 +213,12 @@ export class ExceptionDispatcher {
209213
throw errorToThrow;
210214
}
211215

216+
private static _fillException(exception: Error, json: any) {
217+
if (exception.name === "RavenTimeoutException") {
218+
(exception as any).failImmediately = !!json.FailImmediately;
219+
}
220+
}
221+
212222
private static _getConflictError(schema: ExceptionSchema, json: string) {
213223
if (schema.type.includes("DocumentConflictException")) {
214224
return getError("DocumentConflictException", schema.message, null, { json });

src/Http/NodeSelector.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ class NodeSelectorState {
1515
public fastestRecords: number[];
1616
public fastest: number;
1717
public speedTestMode = 1;
18+
public unlikelyEveryoneFaultedChoiceIndex: number;
1819

1920
constructor(topology: Topology) {
2021
this.topology = topology;
2122
this.nodes = topology.nodes;
2223
this.failures = ArrayUtil.range(topology.nodes.length, () => 0);
2324
this.fastestRecords = ArrayUtil.range(topology.nodes.length, () => 0);
25+
this.unlikelyEveryoneFaultedChoiceIndex = 0;
26+
}
27+
28+
public getNodeWhenEveryoneMarkedAsFaulted(): CurrentIndexAndNode {
29+
const index = this.unlikelyEveryoneFaultedChoiceIndex;
30+
this.unlikelyEveryoneFaultedChoiceIndex = (this.unlikelyEveryoneFaultedChoiceIndex + 1) % this.nodes.length;
31+
32+
33+
return new CurrentIndexAndNode(index, this.nodes[index]);
2434
}
2535
}
2636

@@ -91,18 +101,11 @@ export class NodeSelector {
91101

92102
public getRequestedNode(nodeTag: string): CurrentIndexAndNode {
93103
const state = this._state;
94-
95-
const stateFailures = state.failures;
96104
const serverNodes = state.nodes;
97-
const len = Math.min(serverNodes.length, stateFailures.length);
98105

99-
for (let i = 0; i < len; i++) {
106+
for (let i = 0; i < serverNodes.length; i++) {
100107
if (serverNodes[i].clusterTag === nodeTag) {
101-
if (stateFailures[i] === 0 && !StringUtil.isNullOrEmpty(serverNodes[i].url)) {
102-
return new CurrentIndexAndNode(i, serverNodes[i]);
103-
}
104-
105-
throwError("RequestedNodeUnavailableException", "Requested node " + nodeTag + " currently unavailable, please try again later.");
108+
return new CurrentIndexAndNode(i, serverNodes[i]);
106109
}
107110
}
108111

@@ -113,6 +116,10 @@ export class NodeSelector {
113116
throwError("RequestedNodeUnavailableException", "Could not find requested node " + nodeTag);
114117
}
115118

119+
public nodeIsAvailable(index: number) {
120+
return this._state.failures[index] === 0;
121+
}
122+
116123
public getPreferredNode(): CurrentIndexAndNode {
117124
const state = this._state;
118125
return NodeSelector.getPreferredNodeInternal(state);
@@ -124,7 +131,7 @@ export class NodeSelector {
124131
const len = Math.min(serverNodes.length, stateFailures.length);
125132

126133
for (let i = 0; i < len; i++) {
127-
if (stateFailures[i] === 0 && serverNodes[i].url) {
134+
if (stateFailures[i] === 0) {
128135
return new CurrentIndexAndNode(i, serverNodes[i]);
129136
}
130137
}
@@ -144,13 +151,13 @@ export class NodeSelector {
144151
}
145152

146153
private static _unlikelyEveryoneFaultedChoice(state: NodeSelectorState): CurrentIndexAndNode {
147-
// if there are all marked as failed, we'll chose the first
154+
// if there are all marked as failed, we'll choose the next (the one in CurrentNodeIndex)
148155
// one so the user will get an error (or recover :-) );
149156
if (state.nodes.length === 0) {
150157
throwError("DatabaseDoesNotExistException", "There are no nodes in the topology at all.");
151158
}
152159

153-
return new CurrentIndexAndNode(0, state.nodes[0]);
160+
return state.getNodeWhenEveryoneMarkedAsFaulted();
154161
}
155162

156163
public getFastestNode(): CurrentIndexAndNode {

src/Http/RequestExecutor.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ export class RequestExecutor implements IDisposable {
403403

404404
// this is just to get rid of unhandled rejection, we're handling it later on
405405
executor._firstTopologyUpdatePromise.catch(TypeUtil.NOOP);
406-
406+
407407
return executor;
408408
}
409409

@@ -1464,7 +1464,11 @@ export class RequestExecutor implements IDisposable {
14641464
command.failedNodes = new Map();
14651465
}
14661466

1467-
command.failedNodes.set(chosenNode, RequestExecutor._readExceptionFromServer(req, response, body, error));
1467+
const exception = RequestExecutor._readExceptionFromServer(req, response, body, error);
1468+
if (exception.name === "RavenTimeoutException" && (exception as any).failImmediately) {
1469+
throw exception;
1470+
}
1471+
command.failedNodes.set(chosenNode, exception);
14681472

14691473
if (nodeIndex === null) {
14701474
return false;
@@ -1817,10 +1821,16 @@ export class RequestExecutor implements IDisposable {
18171821
this._disposeAllFailedNodesTimers();
18181822
}
18191823

1820-
public async getRequestedNode(nodeTag: string): Promise<CurrentIndexAndNode> {
1824+
public async getRequestedNode(nodeTag: string, throwIfContainsFailures = false): Promise<CurrentIndexAndNode> {
18211825
await this._ensureNodeSelector();
18221826

1823-
return this._nodeSelector.getRequestedNode(nodeTag);
1827+
const currentIndexAndNode = this._nodeSelector.getRequestedNode(nodeTag);
1828+
1829+
if (throwIfContainsFailures && !this._nodeSelector.nodeIsAvailable(currentIndexAndNode.currentIndex)) {
1830+
throwError("RequestedNodeUnavailableException", "Requested node " + nodeTag + " currently unavailable, please try again later.");
1831+
}
1832+
1833+
return currentIndexAndNode;
18241834
}
18251835

18261836
public async getPreferredNode(): Promise<CurrentIndexAndNode> {

src/ServerWide/Operations/Certificates/CertificateMetadata.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ export interface CertificateMetadata {
66
securityClearance: SecurityClearance;
77
thumbprint: string;
88
notAfter: Date;
9+
notBefore: Date;
910
permissions?: Record<string, DatabaseAccess>;
1011
collectionSecondaryKeys?: string[];
1112
collectionPrimaryKey?: string;
1213
publicKeyPinningHash: string;
13-
}
14+
}

src/ServerWide/Operations/Certificates/GetCertificateMetadataOperation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ class GetCertificateMetadataCommand extends RavenCommand<CertificateMetadata> {
6565
const dateUtil = this._conventions.dateUtil;
6666

6767
const resultsMapped: CertificateMetadata[] = response.results.map(cert => {
68-
const { notAfter } = cert;
68+
const { notAfter, notBefore } = cert;
6969

7070
return {
7171
...cert,
72-
notAfter: dateUtil.parse(notAfter)
72+
notAfter: dateUtil.parse(notAfter),
73+
notBefore: dateUtil.parse(notBefore)
7374
}
7475
})
7576

src/ServerWide/Operations/Certificates/GetCertificateOperation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ class GetCertificateCommand extends RavenCommand<CertificateDefinition> {
6363
const results = await this._defaultPipeline(_ => body = _).process(bodyStream);
6464
const mapped = this._conventions.objectMapper.fromObjectLiteral<{ results: CertificateDefinition[] }>(results, {
6565
nestedTypes: {
66-
"results[].notAfter": "date"
66+
"results[].notAfter": "date",
67+
"results[].notBefore": "date"
6768
}
6869
}).results;
6970

src/ServerWide/Operations/Certificates/GetCertificatesMetadataOperation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,11 @@ class GetCertificatesMetadataCommand extends RavenCommand<CertificateMetadata[]>
6363
const results = await this._defaultPipeline(_ => body = _).process(bodyStream);
6464
this.result = this._conventions.objectMapper.fromObjectLiteral<{ results: CertificateMetadata[] }>(results, {
6565
nestedTypes: {
66-
"results[].notAfter": "date"
66+
"results[].notAfter": "date",
67+
"results[].notBefore": "date",
6768
}
6869
}).results;
6970

7071
return body;
7172
}
72-
}
73+
}

src/ServerWide/Operations/Certificates/GetCertificatesOperation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class GetCertificatesCommand extends RavenCommand<CertificateDefinition[]> {
6060
const results = await this._defaultPipeline(_ => body = _).process(bodyStream);
6161
this.result = this._conventions.objectMapper.fromObjectLiteral<{ results: CertificateDefinition[] }>(results, {
6262
nestedTypes: {
63-
"results[].notAfter": "date"
63+
"results[].notAfter": "date",
64+
"results[].notBefore": "date"
6465
}
6566
}).results;
6667
return body;

0 commit comments

Comments
 (0)