Skip to content

Commit 2221311

Browse files
committed
fix: retry DB connection in RDS init lambda
1 parent 69d99ad commit 2221311

1 file changed

Lines changed: 56 additions & 3 deletions

File tree

demos/rds-init-fn-code/index.js

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const fs = require('fs')
77
const path = require('path')
88
require('dotenv').config();
99

10+
const DB_CONNECT_RETRY_MAX_ATTEMPTS = Number.parseInt(process.env.DB_CONNECT_RETRY_MAX_ATTEMPTS || '24', 10)
11+
const DB_CONNECT_RETRY_DELAY_MS = Number.parseInt(process.env.DB_CONNECT_RETRY_DELAY_MS || '5000', 10)
12+
1013
// the env AWS_ENDPOINT_URL is automatically injected and available
1114
const endpoint = process.env.AWS_ENDPOINT_URL;
1215
const url = new URL(endpoint);
@@ -21,10 +24,11 @@ const secrets = new AWS.SecretsManager({
2124
})
2225

2326
exports.handler = async (e) => {
27+
let connection
2428
try {
2529
const { config } = e.params
2630
const { password, username, dbname, port } = await getSecretValue(config.credsSecretName)
27-
const connection = mysql.createConnection({
31+
connection = await createConnectionWithRetry({
2832
host: hostname,
2933
user: username,
3034
database: dbname,
@@ -33,8 +37,6 @@ exports.handler = async (e) => {
3337
multipleStatements: true
3438
})
3539

36-
connection.connect()
37-
3840
const sqlScript = fs.readFileSync(path.join(__dirname, 'script.sql')).toString()
3941
const res = await query(connection, sqlScript)
4042

@@ -48,6 +50,10 @@ exports.handler = async (e) => {
4850
err,
4951
message: err.message
5052
}
53+
} finally {
54+
if (connection) {
55+
await closeConnection(connection)
56+
}
5157
}
5258
}
5359

@@ -61,6 +67,53 @@ function query (connection, sql) {
6167
})
6268
}
6369

70+
async function createConnectionWithRetry (connectionConfig) {
71+
let lastError
72+
for (let attempt = 1; attempt <= DB_CONNECT_RETRY_MAX_ATTEMPTS; attempt += 1) {
73+
const connection = mysql.createConnection(connectionConfig)
74+
try {
75+
await connect(connection)
76+
return connection
77+
} catch (error) {
78+
connection.destroy()
79+
lastError = error
80+
if (!shouldRetryConnectionError(error) || attempt === DB_CONNECT_RETRY_MAX_ATTEMPTS) {
81+
break
82+
}
83+
84+
const retryInSeconds = DB_CONNECT_RETRY_DELAY_MS / 1000
85+
console.log(`Database connection attempt ${attempt}/${DB_CONNECT_RETRY_MAX_ATTEMPTS} failed with '${error.code || error.message}'. Retrying in ${retryInSeconds}s...`)
86+
await sleep(DB_CONNECT_RETRY_DELAY_MS)
87+
}
88+
}
89+
90+
throw lastError
91+
}
92+
93+
function connect (connection) {
94+
return new Promise((resolve, reject) => {
95+
connection.connect((error) => {
96+
if (error) return reject(error)
97+
98+
return resolve()
99+
})
100+
})
101+
}
102+
103+
function shouldRetryConnectionError (error) {
104+
return ['ECONNREFUSED', 'ETIMEDOUT', 'EHOSTUNREACH', 'ENOTFOUND', 'PROTOCOL_CONNECTION_LOST'].includes(error?.code)
105+
}
106+
107+
function sleep (delayMs) {
108+
return new Promise((resolve) => setTimeout(resolve, delayMs))
109+
}
110+
111+
function closeConnection (connection) {
112+
return new Promise((resolve) => {
113+
connection.end(() => resolve())
114+
})
115+
}
116+
64117
function getSecretValue (secretId) {
65118
return new Promise((resolve, reject) => {
66119
secrets.getSecretValue({ SecretId: secretId }, (err, data) => {

0 commit comments

Comments
 (0)