Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Unified Diff: web/inc/rpc/client.ts

Issue 2717043002: Add LogDog log stream fetcher code. (Closed)
Patch Set: operations, comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: web/inc/rpc/client.ts
diff --git a/web/inc/rpc/client.ts b/web/inc/rpc/client.ts
index 9d3928ff54b2de3008a4d84a11a3adcb5b94f042..3829aa87a07b730d592e706e02bacbda6cd88c18 100644
--- a/web/inc/rpc/client.ts
+++ b/web/inc/rpc/client.ts
@@ -73,33 +73,28 @@ namespace luci {
*/
call<T, R>(service: string, method: string, request?: T): Promise<R> {
let transientRetry = new RetryIterator(this.transientRetry);
- let doCall = (): Promise<R> => {
- // Configure the client for this request.
- this.pc.service = service;
- this.pc.method = method;
- this.pc.request = request;
-
- // Execute the configured request.
- let callPromise: Promise<R> = this.pc.call().completes;
- return callPromise.then((resp: any) => resp.response)
- .catch((err: Error) => {
- // Is this a transient error?
- if (isTransientError(err)) {
- let delay = transientRetry.next();
- if (delay >= 0) {
- console.warn(
- `Transient error calling ` +
- `${service}.${method} with params:`,
- request, `:`, err, `; retrying after ${delay}ms.`);
- return luci.sleepPromise(delay).then(doCall);
- }
- }
+ return transientRetry
+ .do(
+ () => {
+ // Configure the client for this request.
+ this.pc.service = service;
+ this.pc.method = method;
+ this.pc.request = request;
- // Non-transient, throw the error.
- throw err;
- });
- };
- return doCall();
+ // Execute the configured request.
+ return this.pc.call().completes;
+ },
+ (err: Error, delay: number) => {
+ // Is this a transient error?
+ if (!isTransientError(err)) {
+ throw err;
+ }
+ console.warn(
+ `Transient error calling ` +
+ `${service}.${method} with params:`,
+ request, `:`, err, `; retrying after ${delay}ms.`);
+ })
+ .then((resp: any) => resp.response);
}
}
@@ -216,20 +211,29 @@ namespace luci {
export type Retry = {
// The number of retries to perform before failing. If undefined, will retry
// indefinitely.
- retries: number | undefined;
+ retries?: number;
// The amount of time to delay in between retry attempts, in milliseconds.
// If undefined or < 0, no delay will be imposed.
delay: number;
// The maximum delay to apply, in milliseconds. If > 0 and delay scales past
// "maxDelay", it will be capped at "maxDelay".
- maxDelay: number | undefined;
+ maxDelay?: number;
// delayScaling is the multiplier applied to "delay" in between retries. If
// undefined or <= 1, DEFAULT_DELAY_SCALING will be used.
delayScaling?: number;
};
+ /** RetryCallback is an optional callback type used in "do". */
+ export type RetryCallback = (err: Error, delay: number) => void;
+
+ /**
+ * Stopped is a sentinel error thrown by RetryIterator when it runs out of
+ * retries.
+ */
+ export const STOPPED = new Error('retry stopped');
+
/**
* Generic exponential backoff retry delay generator.
*
@@ -260,12 +264,12 @@ namespace luci {
* @returns the next delay, in milliseconds. If there are no more retries,
* returns undefined.
*/
- next(): number|undefined {
+ next(): number {
// Apply retries, if they have been enabled.
if (this.retries !== undefined) {
if (this.retries <= 0) {
// No more retries remaining.
- return undefined;
+ throw STOPPED;
}
this.retries--;
}
@@ -277,5 +281,53 @@ namespace luci {
}
return delay;
}
+
+ /**
+ * Executes a Promise, retrying if the Promise raises an error.
+ *
+ * "do" iteratively tries to execute a Promise, generated by "gen". If that
+ * Promise raises an error, "do" will retry until it either runs out of
+ * retries, or the Promise does not return an error. Each retry, "do" will
+ * invoke "gen" again to generate a new Promise.
+ *
+ * An optional "onError" callback can be supplied. If it is, it will be
+ * invoked in between each retry. The callback may, itself, throw, in which
+ * case the retry loop will stop. This can be used for reporting and/or
+ * selective retries.
+ *
+ * @param gen Promise generator function for retries.
+ * @param onError optional callback to be invoked in between retries.
+ *
+ * @throws any the error generated by "gen"'s Promise, if out of retries,
+ * or the error raised by onError if it chooses to throw.
+ */
+ do
+ <T>(gen: () => Promise<T>, onError?: RetryCallback): Promise<T> {
+ let onErr = (err: Error): Promise<T> => {
+ let delay: number;
+ try {
+ delay = this.next();
+ } catch (e) {
+ if (e !== STOPPED) {
+ console.warn('Unexpected error generating next delay:', e);
+ }
+
+ // If we could not generate another retry delay, raise the initial
+ // Promise's error.
+ throw err;
+ }
+
+ if (onError) {
+ // Note: this may throw.
+ onError(err, delay);
+ }
+
+ return luci.sleepPromise(delay).then(() => {
+ return gen().catch(onErr);
+ });
+ };
+
+ return gen().catch(onErr);
+ }
}
}

Powered by Google App Engine
This is Rietveld 408576698