@@ -7,6 +7,9 @@ const fs = require('fs')
77const path = require ( 'path' )
88require ( '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
1114const endpoint = process . env . AWS_ENDPOINT_URL ;
1215const url = new URL ( endpoint ) ;
@@ -21,10 +24,11 @@ const secrets = new AWS.SecretsManager({
2124} )
2225
2326exports . 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+
64117function getSecretValue ( secretId ) {
65118 return new Promise ( ( resolve , reject ) => {
66119 secrets . getSecretValue ( { SecretId : secretId } , ( err , data ) => {
0 commit comments