mirror of https://github.com/mongodb/mongo
107 lines
2.7 KiB
JavaScript
107 lines
2.7 KiB
JavaScript
/**
|
|
* Miscellaneous utilities which are commonly useful when dealing
|
|
* with success/failure, and retries.
|
|
*/
|
|
|
|
export function hasError(res) {
|
|
return res.ok !== 1 || res.writeErrors || (res.hasOwnProperty("nErrors") && res.nErrors != 0);
|
|
}
|
|
|
|
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 iterations or Y milliseconds elapses;
|
|
* a common pattern when retrying operations.
|
|
*
|
|
* let helper = new RetryTracker(3, 3000);
|
|
* for (let _ of helper) {
|
|
* ... // E.g., try a command. Will loop until break or exceeding either limit.
|
|
* }
|
|
* The timer starts from creation of the helper.
|
|
*/
|
|
export class RetryTracker {
|
|
constructor(retryCount, timeout) {
|
|
this.retryCount = retryCount;
|
|
this.timeout = timeout;
|
|
this.startTime = Date.now();
|
|
}
|
|
|
|
* [Symbol.iterator]() {
|
|
if (this.retryCount <= 0) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < this.retryCount; ++i) {
|
|
let elapsed = Date.now() - this.startTime;
|
|
if (elapsed > this.timeout) {
|
|
break;
|
|
}
|
|
yield {remaining: this.retryCount - i - 1, remainingTime: this.timeout - elapsed};
|
|
}
|
|
}
|
|
}
|