Description
🐛 Bug Report
requests sent after 6 requests that failed due to a timeout with a weightedConnectionPool that has 2 upstreams will fail with NoLivingConnectionsError
even if the upstreams is running again and not timing out anymore.
This is mostly due to the wrong assumption mentioned here:
https://github.com/elastic/elastic-transport-js/blob/main/src/pool/WeightedConnectionPool.ts#L54
if the GCD is 1 and both weights reached 1 after the subtraction we would need about 800 loops for current weight to reach 1 to be able to choose one of the weights
To Reproduce
Steps to reproduce the behavior:
1- clone my branch #54 which has a new test that should reproduce the bug
2- npm run build && node_modules/tap/bin/run.js test/unit/transport.test.ts
Paste your code here:
test('upstreams are down for the first 7 requests', async t => {
const pool = new WeightedConnectionPool({ Connection: MockConnectionTimeoutForThefirstSevenRequests })
pool.addConnection('https://localhost:9200')
pool.addConnection('https://localhost:9201')
const transport = new Transport({ connectionPool: pool, maxRetries: 0 })
for(let i=0;i<20;i++){
try {
await transport.request({
method: 'GET',
path: '/hello'
})
} catch (err: any) {
console.log(err.name)
t.equal(err.name, 'TimeoutError')
}
}
})
export class MockConnectionTimeoutForThefirstSevenRequests extends BaseConnection {
requestCount = -1
async request (params: ConnectionRequestParams, options: ConnectionRequestOptions): Promise<ConnectionRequestResponse>
async request (params: ConnectionRequestParams, options: ConnectionRequestOptionsAsStream): Promise<ConnectionRequestResponseAsStream>
async request (params: ConnectionRequestParams, options: any): Promise<any> {
return new Promise((resolve, reject) => {
this.requestCount++;
if( this.requestCount<=6){
process.nextTick(reject, new TimeoutError('Request timed out'))
}
const body = JSON.stringify({ hello: 'world' })
const statusCode = setStatusCode(params.path)
const headers = {
'content-type': 'application/json;utf=8',
date: new Date().toISOString(),
connection: 'keep-alive',
'content-length': '17',
'x-elastic-product': 'Elasticsearch'
}
process.nextTick(resolve, { body, statusCode, headers })
})
}
}
Expected behavior
we are able to use the pool after the upstreams are up again
Your Environment
- node version: v16.0.0
- master branch of this repo
- os: Mac