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

Side by Side 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 unified diff | Download patch
OLDNEW
1 /* 1 /*
2 Copyright 2016 The LUCI Authors. All rights reserved. 2 Copyright 2016 The LUCI Authors. All rights reserved.
3 Use of this source code is governed under the Apache License, Version 2.0 3 Use of this source code is governed under the Apache License, Version 2.0
4 that can be found in the LICENSE file. 4 that can be found in the LICENSE file.
5 */ 5 */
6 6
7 ///<reference path="../luci-sleep-promise/promise.ts" /> 7 ///<reference path="../luci-sleep-promise/promise.ts" />
8 8
9 namespace luci { 9 namespace luci {
10 /** 10 /**
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 * @param service the RPC service name to call. 66 * @param service the RPC service name to call.
67 * @param method the RPC method name to call. 67 * @param method the RPC method name to call.
68 * @param request optional request body content. 68 * @param request optional request body content.
69 * 69 *
70 * @returns A Promise that resolves to the RPC response, or errors with an 70 * @returns A Promise that resolves to the RPC response, or errors with an
71 * error, including HttpError or GrpcError on HTTP/gRPC failure 71 * error, including HttpError or GrpcError on HTTP/gRPC failure
72 * respectively. 72 * respectively.
73 */ 73 */
74 call<T, R>(service: string, method: string, request?: T): Promise<R> { 74 call<T, R>(service: string, method: string, request?: T): Promise<R> {
75 let transientRetry = new RetryIterator(this.transientRetry); 75 let transientRetry = new RetryIterator(this.transientRetry);
76 let doCall = (): Promise<R> => { 76 return transientRetry
77 // Configure the client for this request. 77 .do(
78 this.pc.service = service; 78 () => {
79 this.pc.method = method; 79 // Configure the client for this request.
80 this.pc.request = request; 80 this.pc.service = service;
81 this.pc.method = method;
82 this.pc.request = request;
81 83
82 // Execute the configured request. 84 // Execute the configured request.
83 let callPromise: Promise<R> = this.pc.call().completes; 85 return this.pc.call().completes;
84 return callPromise.then((resp: any) => resp.response) 86 },
85 .catch((err: Error) => { 87 (err: Error, delay: number) => {
86 // Is this a transient error? 88 // Is this a transient error?
87 if (isTransientError(err)) { 89 if (!isTransientError(err)) {
88 let delay = transientRetry.next(); 90 throw err;
89 if (delay >= 0) {
90 console.warn(
91 `Transient error calling ` +
92 `${service}.${method} with params:`,
93 request, `:`, err, `; retrying after ${delay}ms.`);
94 return luci.sleepPromise(delay).then(doCall);
95 } 91 }
96 } 92 console.warn(
97 93 `Transient error calling ` +
98 // Non-transient, throw the error. 94 `${service}.${method} with params:`,
99 throw err; 95 request, `:`, err, `; retrying after ${delay}ms.`);
100 }); 96 })
101 }; 97 .then((resp: any) => resp.response);
102 return doCall();
103 } 98 }
104 } 99 }
105 100
106 /** 101 /**
107 * gRPC Codes 102 * gRPC Codes
108 * 103 *
109 * Copied from "rpc-code.html" and, more directly, 104 * Copied from "rpc-code.html" and, more directly,
110 * https://github.com/grpc/grpc-go/blob/972dbd2/codes/codes.go#L43 105 * https://github.com/grpc/grpc-go/blob/972dbd2/codes/codes.go#L43
111 */ 106 */
112 export enum Code { 107 export enum Code {
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
209 204
210 /** 205 /**
211 * RetryIterator configuration class. 206 * RetryIterator configuration class.
212 * 207 *
213 * A user will define the retry parameters using a Retry instance, then create 208 * A user will define the retry parameters using a Retry instance, then create
214 * a RetryIterator with them. 209 * a RetryIterator with them.
215 */ 210 */
216 export type Retry = { 211 export type Retry = {
217 // The number of retries to perform before failing. If undefined, will retry 212 // The number of retries to perform before failing. If undefined, will retry
218 // indefinitely. 213 // indefinitely.
219 retries: number | undefined; 214 retries?: number;
220 215
221 // The amount of time to delay in between retry attempts, in milliseconds. 216 // The amount of time to delay in between retry attempts, in milliseconds.
222 // If undefined or < 0, no delay will be imposed. 217 // If undefined or < 0, no delay will be imposed.
223 delay: number; 218 delay: number;
224 // The maximum delay to apply, in milliseconds. If > 0 and delay scales past 219 // The maximum delay to apply, in milliseconds. If > 0 and delay scales past
225 // "maxDelay", it will be capped at "maxDelay". 220 // "maxDelay", it will be capped at "maxDelay".
226 maxDelay: number | undefined; 221 maxDelay?: number;
227 222
228 // delayScaling is the multiplier applied to "delay" in between retries. If 223 // delayScaling is the multiplier applied to "delay" in between retries. If
229 // undefined or <= 1, DEFAULT_DELAY_SCALING will be used. 224 // undefined or <= 1, DEFAULT_DELAY_SCALING will be used.
230 delayScaling?: number; 225 delayScaling?: number;
231 }; 226 };
232 227
228 /** RetryCallback is an optional callback type used in "do". */
229 export type RetryCallback = (err: Error, delay: number) => void;
230
231 /**
232 * Stopped is a sentinel error thrown by RetryIterator when it runs out of
233 * retries.
234 */
235 export const STOPPED = new Error('retry stopped');
236
233 /** 237 /**
234 * Generic exponential backoff retry delay generator. 238 * Generic exponential backoff retry delay generator.
235 * 239 *
236 * A RetryIterator is a specific configured instance of a Retry. Each call to 240 * A RetryIterator is a specific configured instance of a Retry. Each call to
237 * "next()" returns the next delay in the retry sequence. 241 * "next()" returns the next delay in the retry sequence.
238 */ 242 */
239 export class RetryIterator { 243 export class RetryIterator {
240 // Default scaling if no delay is specified. 244 // Default scaling if no delay is specified.
241 static readonly DEAFULT_DELAY_SCALING = 2; 245 static readonly DEAFULT_DELAY_SCALING = 2;
242 246
(...skipping 10 matching lines...) Expand all
253 this.delayScaling = (config.delayScaling || 0); 257 this.delayScaling = (config.delayScaling || 0);
254 if (this.delayScaling < 1) { 258 if (this.delayScaling < 1) {
255 this.delayScaling = RetryIterator.DEAFULT_DELAY_SCALING; 259 this.delayScaling = RetryIterator.DEAFULT_DELAY_SCALING;
256 } 260 }
257 } 261 }
258 262
259 /** 263 /**
260 * @returns the next delay, in milliseconds. If there are no more retries, 264 * @returns the next delay, in milliseconds. If there are no more retries,
261 * returns undefined. 265 * returns undefined.
262 */ 266 */
263 next(): number|undefined { 267 next(): number {
264 // Apply retries, if they have been enabled. 268 // Apply retries, if they have been enabled.
265 if (this.retries !== undefined) { 269 if (this.retries !== undefined) {
266 if (this.retries <= 0) { 270 if (this.retries <= 0) {
267 // No more retries remaining. 271 // No more retries remaining.
268 return undefined; 272 throw STOPPED;
269 } 273 }
270 this.retries--; 274 this.retries--;
271 } 275 }
272 276
273 let delay = this.delay; 277 let delay = this.delay;
274 this.delay *= this.delayScaling; 278 this.delay *= this.delayScaling;
275 if (this.maxDelay > 0 && delay > this.maxDelay) { 279 if (this.maxDelay > 0 && delay > this.maxDelay) {
276 this.delay = delay = this.maxDelay; 280 this.delay = delay = this.maxDelay;
277 } 281 }
278 return delay; 282 return delay;
279 } 283 }
284
285 /**
286 * Executes a Promise, retrying if the Promise raises an error.
287 *
288 * "do" iteratively tries to execute a Promise, generated by "gen". If that
289 * Promise raises an error, "do" will retry until it either runs out of
290 * retries, or the Promise does not return an error. Each retry, "do" will
291 * invoke "gen" again to generate a new Promise.
292 *
293 * An optional "onError" callback can be supplied. If it is, it will be
294 * invoked in between each retry. The callback may, itself, throw, in which
295 * case the retry loop will stop. This can be used for reporting and/or
296 * selective retries.
297 *
298 * @param gen Promise generator function for retries.
299 * @param onError optional callback to be invoked in between retries.
300 *
301 * @throws any the error generated by "gen"'s Promise, if out of retries,
302 * or the error raised by onError if it chooses to throw.
303 */
304 do
305 <T>(gen: () => Promise<T>, onError?: RetryCallback): Promise<T> {
306 let onErr = (err: Error): Promise<T> => {
307 let delay: number;
308 try {
309 delay = this.next();
310 } catch (e) {
311 if (e !== STOPPED) {
312 console.warn('Unexpected error generating next delay:', e);
313 }
314
315 // If we could not generate another retry delay, raise the initial
316 // Promise's error.
317 throw err;
318 }
319
320 if (onError) {
321 // Note: this may throw.
322 onError(err, delay);
323 }
324
325 return luci.sleepPromise(delay).then(() => {
326 return gen().catch(onErr);
327 });
328 };
329
330 return gen().catch(onErr);
331 }
280 } 332 }
281 } 333 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698