retry certain queries (deadlocks)

This commit is contained in:
Erik 2023-02-10 23:57:34 +02:00
parent 0a2d0818e7
commit ed2bbaa6b1
Signed by: Navy.gif
GPG Key ID: 2532FBBB61C65A68

View File

@ -3,6 +3,8 @@ const mysql = require('mysql');
const { inspect } = require('node:util');
const { Util } = require('../../util');
const SAFE_TO_RETRY = [ 'ER_LOCK_DEADLOCK' ];
class MariaDB {
constructor (server, config) {
@ -39,7 +41,6 @@ class MariaDB {
this.pool = mysql.createPoolCluster(this.config.options.cluster);
for (const creds of this._credentials)
this.pool.add({ ...this.config.options.client, ...creds });
} else {
this.pool = mysql.createPool({ ...this.config.options.client, ...this._credentials[0] });
}
@ -106,30 +107,56 @@ class MariaDB {
}
query (query, values) {
getConnection () {
return new Promise((resolve, reject) => {
if (!this.ready)
return reject(new Error('MariaDB not ready'));
let batch = false;
if (values && typeof values.some === 'function')
batch = values.some(val => val instanceof Array);
this.logger.debug(`Incoming query (batch: ${batch})\n${query}\n${inspect(values)}`);
// If connected to a cluster, pick one of the node connection pools
// By default the config is set to use round robin for picking a pool when connected to a cluster
const pool = this._cluster ? this.pool.of('*') : this.pool;
return pool.query(query, values, (err, results, fields) => {
this.pool.getConnection((err, conn) => {
if (err)
return reject(err);
if (results)
return resolve(results);
resolve(fields);
resolve(conn);
});
});
}
/**
* Retry certain queries that are safe to retry, e.g. deadlock
*
* @private
* */
async #_query (query, values, attempts = 0) {
const connection = await this.getConnection();
try {
const result = await new Promise((resolve, reject) => {
const q = connection.query(query, values, (err, results, fields) => {
if (err)
reject(err);
else if (results)
resolve(results);
else
resolve(fields);
connection.release();
});
this.logger.debug(`Constructed query: ${q.sql}`);
});
return Promise.resolve(result);
} catch (err) {
// Retry safe errors
if (SAFE_TO_RETRY.includes(err.code) && attempts < 5) //
return this.#_query(query, values, ++attempts);
return Promise.reject(err);
}
}
async query (query, values) {
if (!this.ready)
return Promise.reject(new Error('MariaDB not ready'));
let batch = false;
if (values && typeof values.some === 'function')
batch = values.some(val => val instanceof Array);
this.logger.debug(`Incoming query (batch: ${batch})\n${query}\n${inspect(values)}`);
return this.#_query(query, values);
}