mirror of https://github.com/grpc/grpc-node.git
grpc-js: Some fixes for how idleness and reresolution are handled
This commit is contained in:
parent
607def892e
commit
d362ccb3f6
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@grpc/grpc-js",
|
"name": "@grpc/grpc-js",
|
||||||
"version": "0.6.8",
|
"version": "0.6.9",
|
||||||
"description": "gRPC Library for Node - pure JS implementation",
|
"description": "gRPC Library for Node - pure JS implementation",
|
||||||
"homepage": "https://grpc.io/",
|
"homepage": "https://grpc.io/",
|
||||||
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
||||||
|
|
|
@ -152,6 +152,12 @@ export class PickFirstLoadBalancer implements LoadBalancer {
|
||||||
this.pickSubchannel(subchannel);
|
this.pickSubchannel(subchannel);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
if (this.triedAllSubchannels && this.subchannelStateCounts[ConnectivityState.IDLE] === this.subchannels.length) {
|
||||||
|
/* If all of the subchannels are IDLE we should go back to a
|
||||||
|
* basic IDLE state where there is no subchannel list to avoid
|
||||||
|
* holding unused resources */
|
||||||
|
this.resetSubchannelList();
|
||||||
|
}
|
||||||
if (this.currentPick === null) {
|
if (this.currentPick === null) {
|
||||||
if (this.triedAllSubchannels) {
|
if (this.triedAllSubchannels) {
|
||||||
let newLBState: ConnectivityState;
|
let newLBState: ConnectivityState;
|
||||||
|
@ -190,18 +196,25 @@ export class PickFirstLoadBalancer implements LoadBalancer {
|
||||||
this.pickedSubchannelStateListener
|
this.pickedSubchannelStateListener
|
||||||
);
|
);
|
||||||
if (this.subchannels.length > 0) {
|
if (this.subchannels.length > 0) {
|
||||||
let newLBState: ConnectivityState;
|
if (this.triedAllSubchannels) {
|
||||||
if (this.subchannelStateCounts[ConnectivityState.CONNECTING] > 0) {
|
let newLBState: ConnectivityState;
|
||||||
newLBState = ConnectivityState.CONNECTING;
|
if (this.subchannelStateCounts[ConnectivityState.CONNECTING] > 0) {
|
||||||
} else if (this.subchannelStateCounts[ConnectivityState.TRANSIENT_FAILURE] > 0) {
|
newLBState = ConnectivityState.CONNECTING;
|
||||||
newLBState = ConnectivityState.TRANSIENT_FAILURE;
|
} else if (this.subchannelStateCounts[ConnectivityState.TRANSIENT_FAILURE] > 0) {
|
||||||
|
newLBState = ConnectivityState.TRANSIENT_FAILURE;
|
||||||
|
} else {
|
||||||
|
newLBState = ConnectivityState.IDLE;
|
||||||
|
}
|
||||||
|
if (newLBState === ConnectivityState.TRANSIENT_FAILURE) {
|
||||||
|
this.updateState(newLBState, new UnavailablePicker());
|
||||||
|
} else {
|
||||||
|
this.updateState(newLBState, new QueuePicker(this));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newLBState = ConnectivityState.IDLE;
|
this.updateState(
|
||||||
}
|
ConnectivityState.CONNECTING,
|
||||||
if (newLBState === ConnectivityState.TRANSIENT_FAILURE) {
|
new QueuePicker(this)
|
||||||
this.updateState(newLBState, new UnavailablePicker());
|
);
|
||||||
} else {
|
|
||||||
this.updateState(newLBState, new QueuePicker(this));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* We don't need to backoff here because this only happens if a
|
/* We don't need to backoff here because this only happens if a
|
||||||
|
|
|
@ -77,6 +77,8 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
*/
|
*/
|
||||||
private innerBalancerState: ConnectivityState = ConnectivityState.IDLE;
|
private innerBalancerState: ConnectivityState = ConnectivityState.IDLE;
|
||||||
|
|
||||||
|
private innerBalancerPicker: Picker = new UnavailablePicker();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent reported state of the pendingReplacementLoadBalancer.
|
* The most recent reported state of the pendingReplacementLoadBalancer.
|
||||||
* Starts at IDLE for type simplicity. This should get updated as soon as the
|
* Starts at IDLE for type simplicity. This should get updated as soon as the
|
||||||
|
@ -104,6 +106,12 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
*/
|
*/
|
||||||
private readonly backoffTimeout: BackoffTimeout;
|
private readonly backoffTimeout: BackoffTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether we should attempt to resolve again after the backoff
|
||||||
|
* timer runs out.
|
||||||
|
*/
|
||||||
|
private continueResolving = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class that behaves like a `LoadBalancer` and also handles name
|
* Wrapper class that behaves like a `LoadBalancer` and also handles name
|
||||||
* resolution internally.
|
* resolution internally.
|
||||||
|
@ -245,12 +253,22 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
},
|
},
|
||||||
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
|
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
|
||||||
this.innerBalancerState = connectivityState;
|
this.innerBalancerState = connectivityState;
|
||||||
|
if (connectivityState === ConnectivityState.IDLE) {
|
||||||
|
picker = new QueuePicker(this);
|
||||||
|
}
|
||||||
|
this.innerBalancerPicker = picker;
|
||||||
if (
|
if (
|
||||||
connectivityState !== ConnectivityState.READY &&
|
connectivityState !== ConnectivityState.READY &&
|
||||||
this.pendingReplacementLoadBalancer !== null
|
this.pendingReplacementLoadBalancer !== null
|
||||||
) {
|
) {
|
||||||
this.switchOverReplacementBalancer();
|
this.switchOverReplacementBalancer();
|
||||||
} else {
|
} else {
|
||||||
|
if (connectivityState === ConnectivityState.IDLE) {
|
||||||
|
if (this.innerLoadBalancer) {
|
||||||
|
this.innerLoadBalancer.destroy();
|
||||||
|
this.innerLoadBalancer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.updateState(connectivityState, picker);
|
this.updateState(connectivityState, picker);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -260,8 +278,10 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
* making resolve requests, so we shouldn't make another one here.
|
* making resolve requests, so we shouldn't make another one here.
|
||||||
* In that case, the backoff timer callback will call
|
* In that case, the backoff timer callback will call
|
||||||
* updateResolution */
|
* updateResolution */
|
||||||
if (!this.backoffTimeout.isRunning()) {
|
if (this.backoffTimeout.isRunning()) {
|
||||||
this.innerResolver.updateResolution();
|
this.continueResolving = true;
|
||||||
|
} else {
|
||||||
|
this.updateResolution();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -278,32 +298,54 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
|
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
|
||||||
|
if (connectivityState === ConnectivityState.IDLE) {
|
||||||
|
picker = new QueuePicker(this);
|
||||||
|
}
|
||||||
this.replacementBalancerState = connectivityState;
|
this.replacementBalancerState = connectivityState;
|
||||||
this.replacementBalancerPicker = picker;
|
this.replacementBalancerPicker = picker;
|
||||||
if (connectivityState === ConnectivityState.READY) {
|
if (connectivityState === ConnectivityState.READY) {
|
||||||
this.switchOverReplacementBalancer();
|
this.switchOverReplacementBalancer();
|
||||||
|
} else if (connectivityState === ConnectivityState.IDLE) {
|
||||||
|
if (this.pendingReplacementLoadBalancer) {
|
||||||
|
this.pendingReplacementLoadBalancer.destroy();
|
||||||
|
this.pendingReplacementLoadBalancer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
requestReresolution: () => {
|
requestReresolution: () => {
|
||||||
if (!this.backoffTimeout.isRunning()) {
|
/* If the backoffTimeout is running, we're still backing off from
|
||||||
/* If the backoffTimeout is running, we're still backing off from
|
* making resolve requests, so we shouldn't make another one here.
|
||||||
* making resolve requests, so we shouldn't make another one here.
|
* In that case, the backoff timer callback will call
|
||||||
* In that case, the backoff timer callback will call
|
* updateResolution */
|
||||||
* updateResolution */
|
if (this.backoffTimeout.isRunning()) {
|
||||||
this.innerResolver.updateResolution();
|
this.continueResolving = true;
|
||||||
|
} else {
|
||||||
|
this.updateResolution();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.backoffTimeout = new BackoffTimeout(() => {
|
this.backoffTimeout = new BackoffTimeout(() => {
|
||||||
if (this.innerLoadBalancer === null) {
|
if (this.continueResolving) {
|
||||||
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
|
this.updateResolution();
|
||||||
|
this.continueResolving = false;
|
||||||
} else {
|
} else {
|
||||||
this.innerResolver.updateResolution();
|
if (this.innerLoadBalancer === null) {
|
||||||
|
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
|
||||||
|
} else {
|
||||||
|
this.updateState(this.innerBalancerState, this.innerBalancerPicker);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateResolution() {
|
||||||
|
this.innerResolver.updateResolution();
|
||||||
|
if (this.innerLoadBalancer === null || this.innerBalancerState === ConnectivityState.IDLE) {
|
||||||
|
this.updateState(ConnectivityState.CONNECTING, new QueuePicker(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private updateState(connectivitystate: ConnectivityState, picker: Picker) {
|
private updateState(connectivitystate: ConnectivityState, picker: Picker) {
|
||||||
trace(this.target + ' ' + ConnectivityState[this.currentState] + ' -> ' + ConnectivityState[connectivitystate]);
|
trace(this.target + ' ' + ConnectivityState[this.currentState] + ' -> ' + ConnectivityState[connectivitystate]);
|
||||||
this.currentState = connectivitystate;
|
this.currentState = connectivitystate;
|
||||||
|
@ -323,6 +365,7 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
);
|
);
|
||||||
this.pendingReplacementLoadBalancer = null;
|
this.pendingReplacementLoadBalancer = null;
|
||||||
this.innerBalancerState = this.replacementBalancerState;
|
this.innerBalancerState = this.replacementBalancerState;
|
||||||
|
this.innerBalancerPicker = this.replacementBalancerPicker;
|
||||||
this.updateState(
|
this.updateState(
|
||||||
this.replacementBalancerState,
|
this.replacementBalancerState,
|
||||||
this.replacementBalancerPicker
|
this.replacementBalancerPicker
|
||||||
|
@ -330,7 +373,7 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleResolutionFailure(error: StatusObject) {
|
private handleResolutionFailure(error: StatusObject) {
|
||||||
if (this.innerLoadBalancer === null) {
|
if (this.innerLoadBalancer === null || this.innerBalancerState === ConnectivityState.IDLE) {
|
||||||
this.updateState(
|
this.updateState(
|
||||||
ConnectivityState.TRANSIENT_FAILURE,
|
ConnectivityState.TRANSIENT_FAILURE,
|
||||||
new UnavailablePicker(error)
|
new UnavailablePicker(error)
|
||||||
|
@ -344,7 +387,11 @@ export class ResolvingLoadBalancer implements LoadBalancer {
|
||||||
this.innerLoadBalancer.exitIdle();
|
this.innerLoadBalancer.exitIdle();
|
||||||
}
|
}
|
||||||
if (this.currentState === ConnectivityState.IDLE) {
|
if (this.currentState === ConnectivityState.IDLE) {
|
||||||
this.innerResolver.updateResolution();
|
if (this.backoffTimeout.isRunning()) {
|
||||||
|
this.continueResolving = true;
|
||||||
|
} else {
|
||||||
|
this.updateResolution();
|
||||||
|
}
|
||||||
this.updateState(
|
this.updateState(
|
||||||
ConnectivityState.CONNECTING,
|
ConnectivityState.CONNECTING,
|
||||||
new QueuePicker(this)
|
new QueuePicker(this)
|
||||||
|
|
Loading…
Reference in New Issue