123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- "use strict";
- /*
- * Copyright 2022 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.RetryingCall = exports.MessageBufferTracker = exports.RetryThrottler = void 0;
- const constants_1 = require("./constants");
- const deadline_1 = require("./deadline");
- const metadata_1 = require("./metadata");
- const logging = require("./logging");
- const TRACER_NAME = 'retrying_call';
- class RetryThrottler {
- constructor(maxTokens, tokenRatio, previousRetryThrottler) {
- this.maxTokens = maxTokens;
- this.tokenRatio = tokenRatio;
- if (previousRetryThrottler) {
- /* When carrying over tokens from a previous config, rescale them to the
- * new max value */
- this.tokens =
- previousRetryThrottler.tokens *
- (maxTokens / previousRetryThrottler.maxTokens);
- }
- else {
- this.tokens = maxTokens;
- }
- }
- addCallSucceeded() {
- this.tokens = Math.max(this.tokens + this.tokenRatio, this.maxTokens);
- }
- addCallFailed() {
- this.tokens = Math.min(this.tokens - 1, 0);
- }
- canRetryCall() {
- return this.tokens > this.maxTokens / 2;
- }
- }
- exports.RetryThrottler = RetryThrottler;
- class MessageBufferTracker {
- constructor(totalLimit, limitPerCall) {
- this.totalLimit = totalLimit;
- this.limitPerCall = limitPerCall;
- this.totalAllocated = 0;
- this.allocatedPerCall = new Map();
- }
- allocate(size, callId) {
- var _a;
- const currentPerCall = (_a = this.allocatedPerCall.get(callId)) !== null && _a !== void 0 ? _a : 0;
- if (this.limitPerCall - currentPerCall < size ||
- this.totalLimit - this.totalAllocated < size) {
- return false;
- }
- this.allocatedPerCall.set(callId, currentPerCall + size);
- this.totalAllocated += size;
- return true;
- }
- free(size, callId) {
- var _a;
- if (this.totalAllocated < size) {
- throw new Error(`Invalid buffer allocation state: call ${callId} freed ${size} > total allocated ${this.totalAllocated}`);
- }
- this.totalAllocated -= size;
- const currentPerCall = (_a = this.allocatedPerCall.get(callId)) !== null && _a !== void 0 ? _a : 0;
- if (currentPerCall < size) {
- throw new Error(`Invalid buffer allocation state: call ${callId} freed ${size} > allocated for call ${currentPerCall}`);
- }
- this.allocatedPerCall.set(callId, currentPerCall - size);
- }
- freeAll(callId) {
- var _a;
- const currentPerCall = (_a = this.allocatedPerCall.get(callId)) !== null && _a !== void 0 ? _a : 0;
- if (this.totalAllocated < currentPerCall) {
- throw new Error(`Invalid buffer allocation state: call ${callId} allocated ${currentPerCall} > total allocated ${this.totalAllocated}`);
- }
- this.totalAllocated -= currentPerCall;
- this.allocatedPerCall.delete(callId);
- }
- }
- exports.MessageBufferTracker = MessageBufferTracker;
- const PREVIONS_RPC_ATTEMPTS_METADATA_KEY = 'grpc-previous-rpc-attempts';
- const DEFAULT_MAX_ATTEMPTS_LIMIT = 5;
- class RetryingCall {
- constructor(channel, callConfig, methodName, host, credentials, deadline, callNumber, bufferTracker, retryThrottler) {
- var _a;
- this.channel = channel;
- this.callConfig = callConfig;
- this.methodName = methodName;
- this.host = host;
- this.credentials = credentials;
- this.deadline = deadline;
- this.callNumber = callNumber;
- this.bufferTracker = bufferTracker;
- this.retryThrottler = retryThrottler;
- this.listener = null;
- this.initialMetadata = null;
- this.underlyingCalls = [];
- this.writeBuffer = [];
- /**
- * The offset of message indices in the writeBuffer. For example, if
- * writeBufferOffset is 10, message 10 is in writeBuffer[0] and message 15
- * is in writeBuffer[5].
- */
- this.writeBufferOffset = 0;
- /**
- * Tracks whether a read has been started, so that we know whether to start
- * reads on new child calls. This only matters for the first read, because
- * once a message comes in the child call becomes committed and there will
- * be no new child calls.
- */
- this.readStarted = false;
- this.transparentRetryUsed = false;
- /**
- * Number of attempts so far
- */
- this.attempts = 0;
- this.hedgingTimer = null;
- this.committedCallIndex = null;
- this.initialRetryBackoffSec = 0;
- this.nextRetryBackoffSec = 0;
- const maxAttemptsLimit = (_a = channel.getOptions()['grpc-node.retry_max_attempts_limit']) !== null && _a !== void 0 ? _a : DEFAULT_MAX_ATTEMPTS_LIMIT;
- if (callConfig.methodConfig.retryPolicy) {
- this.state = 'RETRY';
- const retryPolicy = callConfig.methodConfig.retryPolicy;
- this.nextRetryBackoffSec = this.initialRetryBackoffSec = Number(retryPolicy.initialBackoff.substring(0, retryPolicy.initialBackoff.length - 1));
- this.maxAttempts = Math.min(retryPolicy.maxAttempts, maxAttemptsLimit);
- }
- else if (callConfig.methodConfig.hedgingPolicy) {
- this.state = 'HEDGING';
- this.maxAttempts = Math.min(callConfig.methodConfig.hedgingPolicy.maxAttempts, maxAttemptsLimit);
- }
- else {
- this.state = 'TRANSPARENT_ONLY';
- this.maxAttempts = 1;
- }
- this.startTime = new Date();
- }
- getDeadlineInfo() {
- if (this.underlyingCalls.length === 0) {
- return [];
- }
- const deadlineInfo = [];
- const latestCall = this.underlyingCalls[this.underlyingCalls.length - 1];
- if (this.underlyingCalls.length > 1) {
- deadlineInfo.push(`previous attempts: ${this.underlyingCalls.length - 1}`);
- }
- if (latestCall.startTime > this.startTime) {
- deadlineInfo.push(`time to current attempt start: ${(0, deadline_1.formatDateDifference)(this.startTime, latestCall.startTime)}`);
- }
- deadlineInfo.push(...latestCall.call.getDeadlineInfo());
- return deadlineInfo;
- }
- getCallNumber() {
- return this.callNumber;
- }
- trace(text) {
- logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, '[' + this.callNumber + '] ' + text);
- }
- reportStatus(statusObject) {
- this.trace('ended with status: code=' +
- statusObject.code +
- ' details="' +
- statusObject.details +
- '" start time=' +
- this.startTime.toISOString());
- this.bufferTracker.freeAll(this.callNumber);
- this.writeBufferOffset = this.writeBufferOffset + this.writeBuffer.length;
- this.writeBuffer = [];
- process.nextTick(() => {
- var _a;
- // Explicitly construct status object to remove progress field
- (_a = this.listener) === null || _a === void 0 ? void 0 : _a.onReceiveStatus({
- code: statusObject.code,
- details: statusObject.details,
- metadata: statusObject.metadata,
- });
- });
- }
- cancelWithStatus(status, details) {
- this.trace('cancelWithStatus code: ' + status + ' details: "' + details + '"');
- this.reportStatus({ code: status, details, metadata: new metadata_1.Metadata() });
- for (const { call } of this.underlyingCalls) {
- call.cancelWithStatus(status, details);
- }
- }
- getPeer() {
- if (this.committedCallIndex !== null) {
- return this.underlyingCalls[this.committedCallIndex].call.getPeer();
- }
- else {
- return 'unknown';
- }
- }
- getBufferEntry(messageIndex) {
- var _a;
- return ((_a = this.writeBuffer[messageIndex - this.writeBufferOffset]) !== null && _a !== void 0 ? _a : {
- entryType: 'FREED',
- allocated: false,
- });
- }
- getNextBufferIndex() {
- return this.writeBufferOffset + this.writeBuffer.length;
- }
- clearSentMessages() {
- if (this.state !== 'COMMITTED') {
- return;
- }
- const earliestNeededMessageIndex = this.underlyingCalls[this.committedCallIndex].nextMessageToSend;
- for (let messageIndex = this.writeBufferOffset; messageIndex < earliestNeededMessageIndex; messageIndex++) {
- const bufferEntry = this.getBufferEntry(messageIndex);
- if (bufferEntry.allocated) {
- this.bufferTracker.free(bufferEntry.message.message.length, this.callNumber);
- }
- }
- this.writeBuffer = this.writeBuffer.slice(earliestNeededMessageIndex - this.writeBufferOffset);
- this.writeBufferOffset = earliestNeededMessageIndex;
- }
- commitCall(index) {
- if (this.state === 'COMMITTED') {
- return;
- }
- if (this.underlyingCalls[index].state === 'COMPLETED') {
- return;
- }
- this.trace('Committing call [' +
- this.underlyingCalls[index].call.getCallNumber() +
- '] at index ' +
- index);
- this.state = 'COMMITTED';
- this.committedCallIndex = index;
- for (let i = 0; i < this.underlyingCalls.length; i++) {
- if (i === index) {
- continue;
- }
- if (this.underlyingCalls[i].state === 'COMPLETED') {
- continue;
- }
- this.underlyingCalls[i].state = 'COMPLETED';
- this.underlyingCalls[i].call.cancelWithStatus(constants_1.Status.CANCELLED, 'Discarded in favor of other hedged attempt');
- }
- this.clearSentMessages();
- }
- commitCallWithMostMessages() {
- if (this.state === 'COMMITTED') {
- return;
- }
- let mostMessages = -1;
- let callWithMostMessages = -1;
- for (const [index, childCall] of this.underlyingCalls.entries()) {
- if (childCall.state === 'ACTIVE' &&
- childCall.nextMessageToSend > mostMessages) {
- mostMessages = childCall.nextMessageToSend;
- callWithMostMessages = index;
- }
- }
- if (callWithMostMessages === -1) {
- /* There are no active calls, disable retries to force the next call that
- * is started to be committed. */
- this.state = 'TRANSPARENT_ONLY';
- }
- else {
- this.commitCall(callWithMostMessages);
- }
- }
- isStatusCodeInList(list, code) {
- return list.some(value => {
- var _a;
- return value === code ||
- value.toString().toLowerCase() === ((_a = constants_1.Status[code]) === null || _a === void 0 ? void 0 : _a.toLowerCase());
- });
- }
- getNextRetryBackoffMs() {
- var _a;
- const retryPolicy = (_a = this.callConfig) === null || _a === void 0 ? void 0 : _a.methodConfig.retryPolicy;
- if (!retryPolicy) {
- return 0;
- }
- const nextBackoffMs = Math.random() * this.nextRetryBackoffSec * 1000;
- const maxBackoffSec = Number(retryPolicy.maxBackoff.substring(0, retryPolicy.maxBackoff.length - 1));
- this.nextRetryBackoffSec = Math.min(this.nextRetryBackoffSec * retryPolicy.backoffMultiplier, maxBackoffSec);
- return nextBackoffMs;
- }
- maybeRetryCall(pushback, callback) {
- if (this.state !== 'RETRY') {
- callback(false);
- return;
- }
- if (this.attempts >= this.maxAttempts) {
- callback(false);
- return;
- }
- let retryDelayMs;
- if (pushback === null) {
- retryDelayMs = this.getNextRetryBackoffMs();
- }
- else if (pushback < 0) {
- this.state = 'TRANSPARENT_ONLY';
- callback(false);
- return;
- }
- else {
- retryDelayMs = pushback;
- this.nextRetryBackoffSec = this.initialRetryBackoffSec;
- }
- setTimeout(() => {
- var _a, _b;
- if (this.state !== 'RETRY') {
- callback(false);
- return;
- }
- if ((_b = (_a = this.retryThrottler) === null || _a === void 0 ? void 0 : _a.canRetryCall()) !== null && _b !== void 0 ? _b : true) {
- callback(true);
- this.attempts += 1;
- this.startNewAttempt();
- }
- }, retryDelayMs);
- }
- countActiveCalls() {
- let count = 0;
- for (const call of this.underlyingCalls) {
- if ((call === null || call === void 0 ? void 0 : call.state) === 'ACTIVE') {
- count += 1;
- }
- }
- return count;
- }
- handleProcessedStatus(status, callIndex, pushback) {
- var _a, _b, _c;
- switch (this.state) {
- case 'COMMITTED':
- case 'TRANSPARENT_ONLY':
- this.commitCall(callIndex);
- this.reportStatus(status);
- break;
- case 'HEDGING':
- if (this.isStatusCodeInList((_a = this.callConfig.methodConfig.hedgingPolicy.nonFatalStatusCodes) !== null && _a !== void 0 ? _a : [], status.code)) {
- (_b = this.retryThrottler) === null || _b === void 0 ? void 0 : _b.addCallFailed();
- let delayMs;
- if (pushback === null) {
- delayMs = 0;
- }
- else if (pushback < 0) {
- this.state = 'TRANSPARENT_ONLY';
- this.commitCall(callIndex);
- this.reportStatus(status);
- return;
- }
- else {
- delayMs = pushback;
- }
- setTimeout(() => {
- this.maybeStartHedgingAttempt();
- // If after trying to start a call there are no active calls, this was the last one
- if (this.countActiveCalls() === 0) {
- this.commitCall(callIndex);
- this.reportStatus(status);
- }
- }, delayMs);
- }
- else {
- this.commitCall(callIndex);
- this.reportStatus(status);
- }
- break;
- case 'RETRY':
- if (this.isStatusCodeInList(this.callConfig.methodConfig.retryPolicy.retryableStatusCodes, status.code)) {
- (_c = this.retryThrottler) === null || _c === void 0 ? void 0 : _c.addCallFailed();
- this.maybeRetryCall(pushback, retried => {
- if (!retried) {
- this.commitCall(callIndex);
- this.reportStatus(status);
- }
- });
- }
- else {
- this.commitCall(callIndex);
- this.reportStatus(status);
- }
- break;
- }
- }
- getPushback(metadata) {
- const mdValue = metadata.get('grpc-retry-pushback-ms');
- if (mdValue.length === 0) {
- return null;
- }
- try {
- return parseInt(mdValue[0]);
- }
- catch (e) {
- return -1;
- }
- }
- handleChildStatus(status, callIndex) {
- var _a;
- if (this.underlyingCalls[callIndex].state === 'COMPLETED') {
- return;
- }
- this.trace('state=' +
- this.state +
- ' handling status with progress ' +
- status.progress +
- ' from child [' +
- this.underlyingCalls[callIndex].call.getCallNumber() +
- '] in state ' +
- this.underlyingCalls[callIndex].state);
- this.underlyingCalls[callIndex].state = 'COMPLETED';
- if (status.code === constants_1.Status.OK) {
- (_a = this.retryThrottler) === null || _a === void 0 ? void 0 : _a.addCallSucceeded();
- this.commitCall(callIndex);
- this.reportStatus(status);
- return;
- }
- if (this.state === 'COMMITTED') {
- this.reportStatus(status);
- return;
- }
- const pushback = this.getPushback(status.metadata);
- switch (status.progress) {
- case 'NOT_STARTED':
- // RPC never leaves the client, always safe to retry
- this.startNewAttempt();
- break;
- case 'REFUSED':
- // RPC reaches the server library, but not the server application logic
- if (this.transparentRetryUsed) {
- this.handleProcessedStatus(status, callIndex, pushback);
- }
- else {
- this.transparentRetryUsed = true;
- this.startNewAttempt();
- }
- break;
- case 'DROP':
- this.commitCall(callIndex);
- this.reportStatus(status);
- break;
- case 'PROCESSED':
- this.handleProcessedStatus(status, callIndex, pushback);
- break;
- }
- }
- maybeStartHedgingAttempt() {
- if (this.state !== 'HEDGING') {
- return;
- }
- if (!this.callConfig.methodConfig.hedgingPolicy) {
- return;
- }
- if (this.attempts >= this.maxAttempts) {
- return;
- }
- this.attempts += 1;
- this.startNewAttempt();
- this.maybeStartHedgingTimer();
- }
- maybeStartHedgingTimer() {
- var _a, _b, _c;
- if (this.hedgingTimer) {
- clearTimeout(this.hedgingTimer);
- }
- if (this.state !== 'HEDGING') {
- return;
- }
- if (!this.callConfig.methodConfig.hedgingPolicy) {
- return;
- }
- const hedgingPolicy = this.callConfig.methodConfig.hedgingPolicy;
- if (this.attempts >= this.maxAttempts) {
- return;
- }
- const hedgingDelayString = (_a = hedgingPolicy.hedgingDelay) !== null && _a !== void 0 ? _a : '0s';
- const hedgingDelaySec = Number(hedgingDelayString.substring(0, hedgingDelayString.length - 1));
- this.hedgingTimer = setTimeout(() => {
- this.maybeStartHedgingAttempt();
- }, hedgingDelaySec * 1000);
- (_c = (_b = this.hedgingTimer).unref) === null || _c === void 0 ? void 0 : _c.call(_b);
- }
- startNewAttempt() {
- const child = this.channel.createLoadBalancingCall(this.callConfig, this.methodName, this.host, this.credentials, this.deadline);
- this.trace('Created child call [' +
- child.getCallNumber() +
- '] for attempt ' +
- this.attempts);
- const index = this.underlyingCalls.length;
- this.underlyingCalls.push({
- state: 'ACTIVE',
- call: child,
- nextMessageToSend: 0,
- startTime: new Date()
- });
- const previousAttempts = this.attempts - 1;
- const initialMetadata = this.initialMetadata.clone();
- if (previousAttempts > 0) {
- initialMetadata.set(PREVIONS_RPC_ATTEMPTS_METADATA_KEY, `${previousAttempts}`);
- }
- let receivedMetadata = false;
- child.start(initialMetadata, {
- onReceiveMetadata: metadata => {
- this.trace('Received metadata from child [' + child.getCallNumber() + ']');
- this.commitCall(index);
- receivedMetadata = true;
- if (previousAttempts > 0) {
- metadata.set(PREVIONS_RPC_ATTEMPTS_METADATA_KEY, `${previousAttempts}`);
- }
- if (this.underlyingCalls[index].state === 'ACTIVE') {
- this.listener.onReceiveMetadata(metadata);
- }
- },
- onReceiveMessage: message => {
- this.trace('Received message from child [' + child.getCallNumber() + ']');
- this.commitCall(index);
- if (this.underlyingCalls[index].state === 'ACTIVE') {
- this.listener.onReceiveMessage(message);
- }
- },
- onReceiveStatus: status => {
- this.trace('Received status from child [' + child.getCallNumber() + ']');
- if (!receivedMetadata && previousAttempts > 0) {
- status.metadata.set(PREVIONS_RPC_ATTEMPTS_METADATA_KEY, `${previousAttempts}`);
- }
- this.handleChildStatus(status, index);
- },
- });
- this.sendNextChildMessage(index);
- if (this.readStarted) {
- child.startRead();
- }
- }
- start(metadata, listener) {
- this.trace('start called');
- this.listener = listener;
- this.initialMetadata = metadata;
- this.attempts += 1;
- this.startNewAttempt();
- this.maybeStartHedgingTimer();
- }
- handleChildWriteCompleted(childIndex) {
- var _a, _b;
- const childCall = this.underlyingCalls[childIndex];
- const messageIndex = childCall.nextMessageToSend;
- (_b = (_a = this.getBufferEntry(messageIndex)).callback) === null || _b === void 0 ? void 0 : _b.call(_a);
- this.clearSentMessages();
- childCall.nextMessageToSend += 1;
- this.sendNextChildMessage(childIndex);
- }
- sendNextChildMessage(childIndex) {
- const childCall = this.underlyingCalls[childIndex];
- if (childCall.state === 'COMPLETED') {
- return;
- }
- if (this.getBufferEntry(childCall.nextMessageToSend)) {
- const bufferEntry = this.getBufferEntry(childCall.nextMessageToSend);
- switch (bufferEntry.entryType) {
- case 'MESSAGE':
- childCall.call.sendMessageWithContext({
- callback: error => {
- // Ignore error
- this.handleChildWriteCompleted(childIndex);
- },
- }, bufferEntry.message.message);
- break;
- case 'HALF_CLOSE':
- childCall.nextMessageToSend += 1;
- childCall.call.halfClose();
- break;
- case 'FREED':
- // Should not be possible
- break;
- }
- }
- }
- sendMessageWithContext(context, message) {
- var _a;
- this.trace('write() called with message of length ' + message.length);
- const writeObj = {
- message,
- flags: context.flags,
- };
- const messageIndex = this.getNextBufferIndex();
- const bufferEntry = {
- entryType: 'MESSAGE',
- message: writeObj,
- allocated: this.bufferTracker.allocate(message.length, this.callNumber),
- };
- this.writeBuffer.push(bufferEntry);
- if (bufferEntry.allocated) {
- (_a = context.callback) === null || _a === void 0 ? void 0 : _a.call(context);
- for (const [callIndex, call] of this.underlyingCalls.entries()) {
- if (call.state === 'ACTIVE' &&
- call.nextMessageToSend === messageIndex) {
- call.call.sendMessageWithContext({
- callback: error => {
- // Ignore error
- this.handleChildWriteCompleted(callIndex);
- },
- }, message);
- }
- }
- }
- else {
- this.commitCallWithMostMessages();
- // commitCallWithMostMessages can fail if we are between ping attempts
- if (this.committedCallIndex === null) {
- return;
- }
- const call = this.underlyingCalls[this.committedCallIndex];
- bufferEntry.callback = context.callback;
- if (call.state === 'ACTIVE' && call.nextMessageToSend === messageIndex) {
- call.call.sendMessageWithContext({
- callback: error => {
- // Ignore error
- this.handleChildWriteCompleted(this.committedCallIndex);
- },
- }, message);
- }
- }
- }
- startRead() {
- this.trace('startRead called');
- this.readStarted = true;
- for (const underlyingCall of this.underlyingCalls) {
- if ((underlyingCall === null || underlyingCall === void 0 ? void 0 : underlyingCall.state) === 'ACTIVE') {
- underlyingCall.call.startRead();
- }
- }
- }
- halfClose() {
- this.trace('halfClose called');
- const halfCloseIndex = this.getNextBufferIndex();
- this.writeBuffer.push({
- entryType: 'HALF_CLOSE',
- allocated: false,
- });
- for (const call of this.underlyingCalls) {
- if ((call === null || call === void 0 ? void 0 : call.state) === 'ACTIVE' &&
- call.nextMessageToSend === halfCloseIndex) {
- call.nextMessageToSend += 1;
- call.call.halfClose();
- }
- }
- }
- setCredentials(newCredentials) {
- throw new Error('Method not implemented.');
- }
- getMethod() {
- return this.methodName;
- }
- getHost() {
- return this.host;
- }
- }
- exports.RetryingCall = RetryingCall;
- //# sourceMappingURL=retrying-call.js.map
|