-
Notifications
You must be signed in to change notification settings - Fork 335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
handle failed api inits #1970
handle failed api inits #1970
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the initial connection fails will it ever be retried again?
const poolItem: ConnectionPoolItem<T> = { | ||
primary: primary, | ||
performanceScore: 100, | ||
failureCount: 0, | ||
endpoint: endpoint, | ||
backoffDelay: 0, | ||
rateLimited: false, | ||
failed: false, | ||
failed: initFailed, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to add initFailed
can't we use this?
.filter((index) => !this.pool[index].initFailed); | ||
|
||
if (initedIndices.length === 0) { | ||
throw new Error(`Initialization failed for all endpoints. Please add healthier endpoints.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem like the right place to throw this error. This isn't an init function
if ( | ||
network.chainId && | ||
network.chainId !== this.networkMeta.genesisHash | ||
) { | ||
const err = new Error( | ||
`Network chainId doesn't match expected genesisHash. Your SubQuery project is expecting to index data from "${ | ||
network.chainId ?? network.genesisHash | ||
}", however the endpoint that you are connecting to is different("${ | ||
this.networkMeta.genesisHash | ||
}). Please check that the RPC endpoint is actually for your desired network or update the genesisHash.`, | ||
); | ||
logger.error(err, err.message); | ||
throw err; | ||
} | ||
} else { | ||
const genesisHash = api.genesisHash.toString(); | ||
if (this.networkMeta.genesisHash !== genesisHash) { | ||
throw this.metadataMismatchError( | ||
'Genesis Hash', | ||
this.networkMeta.genesisHash, | ||
genesisHash, | ||
); | ||
} | ||
} | ||
} | ||
|
||
endpointToApiIndex[endpoint] = connection; | ||
endpointToApiIndex[endpoint] = connection; | ||
} catch (e) { | ||
logger.error(`failed to init ${endpoint}: ${e}`); | ||
endpointToApiIndex[endpoint] = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move some of this to node core? Its pretty much the same on all chains
I'm not sure how to handle this when there are multiple workers involved. the initialization may fail in a subset of workers and we should only allow using an endpoint when all of the workers are connected to it. |
Why would we only use an endpoint if all workers are connected? The shared state is the weighting not what endpoints can be used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking like a good improvement
): void { | ||
if (attempt < 5) { | ||
//eslint-disable-next-line @typescript-eslint/no-misused-promises | ||
setTimeout(async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth having a backoff function as we use the same concept a few places now. Also there needs to be a reference to this timeout
throw new Error(`Attempting to update connection that does not exist.`); | ||
} | ||
|
||
this.allApi[index] = api; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this is safe? What happens if the wrong index is provided?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.) the order of the api will be different for different workers, making the connection pool state invalid because only the index is used to reference api.
2.) one api might overwrite another
so, it's important that we get the index right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes its important to get the index right. Is using an int the right thing to key these by? If we use the endpoint it would be much safer
@@ -47,8 +48,9 @@ export class ApiService | |||
connectionPoolService: ConnectionPoolService<ApiPromiseConnection>, | |||
eventEmitter: EventEmitter2, | |||
private nodeConfig: NodeConfig, | |||
retryManager: RetryManager, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just meaning a util function. Adding another dependency ads a lot of unnecessary complexity.
Its already hard enough to write tests with ApiService because we need all the connectionPool stuff. We should be able to mock an api service without all that when we want a single endpoint. But thats another issue that shouldn't be addressed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! I think changing from index to endpoint was a good idea, it seems to mak code more readable
I tried this by adding |
}); | ||
|
||
try { | ||
const {fulfilledIndex, result} = (await raceFulfilled(connectionPromises)) as { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a cast needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to rule out undefined
Description
Avoid exiting process when one of the API initialization fails. Instead mark it is as unusable and continue initializing other APIs.
Fixes # (issue)
Type of change
Please delete options that are not relevant.
Checklist