feat(xen-api): automatically retry ro calls on ECONNRESET (#5674)

See xoa-support#3266
This commit is contained in:
Julien Fontanet 2021-03-16 17:31:18 +01:00 committed by GitHub
parent 2e0e1d2aac
commit 853905e52f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 7 deletions

View File

@ -13,6 +13,7 @@
- [Pool] Fix `an error has occurred` when using the "Disconnect" button from the pool page [#5669](https://github.com/vatesfr/xen-orchestra/issues/5669) (PR [#5671](https://github.com/vatesfr/xen-orchestra/pull/5671))
- [Configuration] Automatically connect enabled servers after import [#5660](https://github.com/vatesfr/xen-orchestra/issues/5660) (PR [#5672](https://github.com/vatesfr/xen-orchestra/pull/5672))
- Work-around some `ECONNRESET` errors when connecting to XEN-API (PR [#5674](https://github.com/vatesfr/xen-orchestra/pull/5674))
### Packages to release
@ -31,5 +32,6 @@
>
> In case of conflict, the highest (lowest in previous list) `$version` wins.
- xen-api minor
- xo-server patch
- xo-web patch

View File

@ -86,6 +86,13 @@ export class Xapi extends EventEmitter {
this._readOnly = Boolean(opts.readOnly)
this._RecordsByType = { __proto__: null }
this._roCallRetryOptions = {
delay: 1e3,
tries: 10,
...opts.roCallRetryOptions,
when: { code: 'ECONNRESET' },
}
this._auth = opts.auth
const url = parseUrl(opts.url)
if (this._auth === undefined) {
@ -231,7 +238,9 @@ export class Xapi extends EventEmitter {
// this should be used for instantaneous calls, otherwise use `callAsync`
call(method, ...args) {
return this._readOnly && !isReadOnlyCall(method, args)
return isReadOnlyCall(method, args)
? this._roCall(method, args)
: this._readOnly
? Promise.reject(new Error(`cannot call ${method}() in read only mode`))
: this._sessionCall(method, args)
}
@ -261,15 +270,15 @@ export class Xapi extends EventEmitter {
// ===========================================================================
async getAllRecords(type) {
return map(await this._sessionCall(`${type}.get_all_records`), (record, ref) => this._wrapRecord(type, ref, record))
return map(await this._roCall(`${type}.get_all_records`), (record, ref) => this._wrapRecord(type, ref, record))
}
async getRecord(type, ref) {
return this._wrapRecord(type, ref, await this._sessionCall(`${type}.get_record`, [ref]))
return this._wrapRecord(type, ref, await this._roCall(`${type}.get_record`, [ref]))
}
async getRecordByUuid(type, uuid) {
return this.getRecord(type, await this._sessionCall(`${type}.get_by_uuid`, [uuid]))
return this.getRecord(type, await this._roCall(`${type}.get_by_uuid`, [uuid]))
}
getRecords(type, refs) {
@ -277,7 +286,7 @@ export class Xapi extends EventEmitter {
}
getField(type, ref, field) {
return this._sessionCall(`${type}.get_${field}`, [ref])
return this._roCall(`${type}.get_${field}`, [ref])
}
setField(type, ref, field, value) {
@ -849,7 +858,7 @@ export class Xapi extends EventEmitter {
types.map(async type => {
try {
const toRemove = toRemoveByType[type]
const records = await this._sessionCall(`${type}.get_all_records`)
const records = await this._roCall(`${type}.get_all_records`)
const refs = getKeys(records)
refs.forEach(ref => {
toRemove.delete(ref)
@ -900,6 +909,11 @@ export class Xapi extends EventEmitter {
}
}
// read-only call, automatically retry in case of connection issues
_roCall(method, args) {
return pRetry(() => this._sessionCall(method, args), this._roCallRetryOptions)
}
_watchEvents = coalesceCalls(this._watchEvents)
async _watchEvents() {
// eslint-disable-next-line no-labels
@ -1015,7 +1029,7 @@ export class Xapi extends EventEmitter {
try {
await this._connected
this._processEvents(await this._sessionCall('event.next', undefined, EVENT_TIMEOUT * 1e3))
this._processEvents(await this._roCall('event.next', undefined, EVENT_TIMEOUT * 1e3))
} catch (error) {
if (error?.code === 'EVENTS_LOST') {
await ignoreErrors.call(this._sessionCall('event.unregister', [types]))