mirror of https://github.com/mongodb/mongo
127 lines
3.2 KiB
JavaScript
127 lines
3.2 KiB
JavaScript
/**
|
|
* Miscellaneous utilities which are commonly useful when dealing
|
|
* with success/failure, and retries.
|
|
*/
|
|
|
|
export function hasError(res) {
|
|
const hasExecutionStatsErrorCode = (function () {
|
|
if (!res.hasOwnProperty("executionStats")) {
|
|
return false;
|
|
}
|
|
|
|
if (!res.executionStats.executionStages.hasOwnProperty("shards")) {
|
|
return res.executionStats.hasOwnProperty("errorCode");
|
|
}
|
|
|
|
for (const shardExecutionStats of res.executionStats.executionStages.shards) {
|
|
if (shardExecutionStats.hasOwnProperty("errorCode")) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
})();
|
|
return (
|
|
res.ok !== 1 ||
|
|
res.writeErrors ||
|
|
(res.hasOwnProperty("nErrors") && res.nErrors != 0) ||
|
|
hasExecutionStatsErrorCode
|
|
);
|
|
}
|
|
|
|
export function hasWriteConcernError(res) {
|
|
return res.hasOwnProperty("writeConcernError");
|
|
}
|
|
|
|
export function isSuccess(res) {
|
|
return !(hasError(res) || hasWriteConcernError(res));
|
|
}
|
|
|
|
/**
|
|
* Wrapper for a status or an exception.
|
|
*
|
|
* Typical usage:
|
|
*
|
|
* let res = Result.wrap(() => mightReturnOrThrow());
|
|
* if (res.ok()) {...}
|
|
* return res.unwrap(); // Returns or throws.
|
|
*/
|
|
export class Result {
|
|
constructor(status, exception) {
|
|
// Status or exception must be present.
|
|
assert((status != null) != (exception != null));
|
|
this.status = status;
|
|
this.exception = exception;
|
|
}
|
|
static status(status) {
|
|
let res = new Result(status, null);
|
|
return res;
|
|
}
|
|
|
|
static exception(exception) {
|
|
let res = new Result(null, exception);
|
|
return res;
|
|
}
|
|
|
|
static wrap(callable) {
|
|
try {
|
|
return Result.status(callable());
|
|
} catch (e) {
|
|
return Result.exception(e);
|
|
}
|
|
}
|
|
|
|
ok() {
|
|
return this.status != null && isSuccess(this.status);
|
|
}
|
|
|
|
unwrap() {
|
|
if (this.exception != null) {
|
|
throw this.exception;
|
|
}
|
|
return this.status;
|
|
}
|
|
apply(callback) {
|
|
if (this.exception != null) {
|
|
return callback(this.exception);
|
|
}
|
|
return callback(this.status);
|
|
}
|
|
dispatch(onStatus, onException) {
|
|
if (this.exception != null) {
|
|
return onException(this.exception);
|
|
}
|
|
return onStatus(this.status);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility to simplify looping until X milliseconds elapse;
|
|
* a common pattern when retrying operations.
|
|
*
|
|
* let helper = new RetryTracker(3000);
|
|
* for (let _ of helper) {
|
|
* ... // E.g., try a command. Will loop until break or exceeding the time limit.
|
|
* }
|
|
* The timer starts from creation of the helper.
|
|
*/
|
|
export class RetryTracker {
|
|
constructor(timeout) {
|
|
this.retries = 0;
|
|
this.timeout = timeout;
|
|
this.startTime = Date.now();
|
|
this.timeoutExceeded = false;
|
|
}
|
|
|
|
*[Symbol.iterator]() {
|
|
while (true) {
|
|
let elapsed = Date.now() - this.startTime;
|
|
if (elapsed > this.timeout) {
|
|
this.timeoutExceeded = true;
|
|
break;
|
|
}
|
|
yield {retries: this.retries++, remainingTime: this.timeout - elapsed};
|
|
}
|
|
}
|
|
}
|