refactor(sql): some changes to help with sql connection on random disconnect
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
import { returnFunc } from "../utils/return.js";
|
|
||||||
import { connected, pool } from "./prodSqlConnect.js";
|
|
||||||
import { validateEnv } from "../utils/envValidator.js";
|
import { validateEnv } from "../utils/envValidator.js";
|
||||||
|
import { returnFunc } from "../utils/return.js";
|
||||||
|
import {
|
||||||
|
closePool,
|
||||||
|
connected,
|
||||||
|
pool,
|
||||||
|
reconnecting,
|
||||||
|
reconnectToSql,
|
||||||
|
} from "./prodSqlConnect.js";
|
||||||
|
|
||||||
const env = validateEnv(process.env);
|
const env = validateEnv(process.env);
|
||||||
/**
|
/**
|
||||||
@@ -11,48 +17,65 @@ const env = validateEnv(process.env);
|
|||||||
* You must use test1 always as it will be changed via query
|
* You must use test1 always as it will be changed via query
|
||||||
*/
|
*/
|
||||||
export async function prodQuery(queryToRun: string, name: string) {
|
export async function prodQuery(queryToRun: string, name: string) {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
return returnFunc({
|
reconnectToSql();
|
||||||
success: false,
|
|
||||||
module: "prodSql",
|
|
||||||
subModule: "query",
|
|
||||||
level: "error",
|
|
||||||
message: `The sql ${env.PROD_PLANT_TOKEN} is not connected`,
|
|
||||||
notify: false,
|
|
||||||
data: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const query = queryToRun.replaceAll("test1", env.PROD_PLANT_TOKEN);
|
|
||||||
try {
|
|
||||||
const result = await pool.request().query(query);
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: `Query results for: ${name}`,
|
|
||||||
data: result.recordset,
|
|
||||||
};
|
|
||||||
} catch (error: any) {
|
|
||||||
console.log(error);
|
|
||||||
if (error.code === "ETIMEOUT") {
|
|
||||||
return returnFunc({
|
|
||||||
success: false,
|
|
||||||
module: "prodSql",
|
|
||||||
subModule: "query",
|
|
||||||
level: "error",
|
|
||||||
message: `${name} did not run due to a timeout.`,
|
|
||||||
notify: false,
|
|
||||||
data: [error],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.code === "EREQUEST") {
|
if (reconnecting) {
|
||||||
return returnFunc({
|
return returnFunc({
|
||||||
success: false,
|
success: false,
|
||||||
module: "prodSql",
|
module: "prodSql",
|
||||||
subModule: "query",
|
subModule: "query",
|
||||||
level: "error",
|
level: "error",
|
||||||
message: `${name} encountered an error ${error.originalError.info.message}`,
|
message: `The sql ${env.PROD_PLANT_TOKEN} is trying to reconnect already`,
|
||||||
data: [],
|
notify: false,
|
||||||
});
|
data: [],
|
||||||
}
|
});
|
||||||
}
|
} else {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
module: "prodSql",
|
||||||
|
subModule: "query",
|
||||||
|
level: "error",
|
||||||
|
message: `The sql ${env.PROD_PLANT_TOKEN} is not connected`,
|
||||||
|
notify: false,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = queryToRun.replaceAll("test1", env.PROD_PLANT_TOKEN);
|
||||||
|
try {
|
||||||
|
const result = await pool.request().query(query);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Query results for: ${name}`,
|
||||||
|
data: result.recordset,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(error);
|
||||||
|
if (error.code === "ETIMEOUT") {
|
||||||
|
closePool();
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
module: "prodSql",
|
||||||
|
subModule: "query",
|
||||||
|
level: "error",
|
||||||
|
message: `${name} did not run due to a timeout.`,
|
||||||
|
notify: false,
|
||||||
|
data: [error],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.code === "EREQUEST") {
|
||||||
|
closePool();
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
module: "prodSql",
|
||||||
|
subModule: "query",
|
||||||
|
level: "error",
|
||||||
|
message: `${name} encountered an error ${error.originalError.info.message}`,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,136 +1,134 @@
|
|||||||
import sql from "mssql";
|
import sql from "mssql";
|
||||||
import { checkHostnamePort } from "../utils/checkHostNamePort.js";
|
|
||||||
import { sqlConfig } from "./prodSqlConfig.js";
|
|
||||||
import { createLogger } from "../logger/logger.js";
|
import { createLogger } from "../logger/logger.js";
|
||||||
import { returnFunc } from "../utils/return.js";
|
import { checkHostnamePort } from "../utils/checkHostNamePort.js";
|
||||||
import { validateEnv } from "../utils/envValidator.js";
|
import { validateEnv } from "../utils/envValidator.js";
|
||||||
|
import { returnFunc } from "../utils/return.js";
|
||||||
|
import { sqlConfig } from "./prodSqlConfig.js";
|
||||||
|
|
||||||
const env = validateEnv(process.env);
|
const env = validateEnv(process.env);
|
||||||
|
|
||||||
export let pool: any;
|
export let pool: any;
|
||||||
export let connected: boolean = false;
|
export let connected: boolean = false;
|
||||||
let reconnecting = false;
|
export let reconnecting = false;
|
||||||
|
|
||||||
export const initializeProdPool = async () => {
|
export const initializeProdPool = async () => {
|
||||||
const log = createLogger({ module: "prodSql" });
|
const log = createLogger({ module: "prodSql" });
|
||||||
|
|
||||||
const serverUp = await checkHostnamePort(`${env.PROD_SERVER}:1433`);
|
const serverUp = await checkHostnamePort(`${env.PROD_SERVER}:1433`);
|
||||||
|
|
||||||
if (!serverUp) {
|
if (!serverUp) {
|
||||||
reconnectToSql();
|
reconnectToSql();
|
||||||
return returnFunc({
|
return returnFunc({
|
||||||
success: false,
|
success: false,
|
||||||
module: "prodSql",
|
module: "prodSql",
|
||||||
level: "fatal",
|
level: "fatal",
|
||||||
message: `The sql ${env.PROD_SERVER} is not reachable`,
|
message: `The sql ${env.PROD_SERVER} is not reachable`,
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// if you were restarting from the endpoint you get this lovely error
|
// if you were restarting from the endpoint you get this lovely error
|
||||||
if (connected) {
|
if (connected) {
|
||||||
return returnFunc({
|
return returnFunc({
|
||||||
success: false,
|
success: false,
|
||||||
module: "prodSql",
|
module: "prodSql",
|
||||||
level: "error",
|
level: "error",
|
||||||
message: `There is already a connection to ${env.PROD_PLANT_TOKEN}`,
|
message: `There is already a connection to ${env.PROD_PLANT_TOKEN}`,
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
pool = await sql.connect(sqlConfig);
|
pool = await sql.connect(sqlConfig);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Connected to ${sqlConfig?.server}, using DB: ${sqlConfig?.database}`
|
`Connected to ${sqlConfig?.server}, using DB: ${sqlConfig?.database}`,
|
||||||
);
|
);
|
||||||
connected = true;
|
connected = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.fatal(
|
log.fatal(
|
||||||
`${JSON.stringify(
|
`${JSON.stringify(error)}, "There was an error connecting to the pool."`,
|
||||||
error
|
);
|
||||||
)}, "There was an error connecting to the pool."`
|
reconnectToSql();
|
||||||
);
|
// throw new Error("There was an error closing the sql connection");
|
||||||
reconnectToSql();
|
}
|
||||||
// throw new Error("There was an error closing the sql connection");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const reconnectToSql = async () => {
|
export const reconnectToSql = async () => {
|
||||||
const log = createLogger({ module: "prodSql" });
|
const log = createLogger({ module: "prodSql" });
|
||||||
if (reconnecting) return;
|
if (reconnecting) return;
|
||||||
reconnecting = true;
|
reconnecting = true;
|
||||||
|
|
||||||
let delay = 2000; // start at 2s
|
let delay = 2000; // start at 2s
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = 10; // or limit by time, e.g. 2 min total
|
const maxAttempts = 10; // or limit by time, e.g. 2 min total
|
||||||
|
|
||||||
while (!connected && attempts < maxAttempts) {
|
while (!connected && attempts < maxAttempts) {
|
||||||
attempts++;
|
attempts++;
|
||||||
log.info(
|
log.info(
|
||||||
`Reconnect attempt ${attempts}/${maxAttempts} in ${
|
`Reconnect attempt ${attempts}/${maxAttempts} in ${delay / 1000}s...`,
|
||||||
delay / 1000
|
);
|
||||||
}s...`
|
|
||||||
);
|
|
||||||
|
|
||||||
await new Promise((res) => setTimeout(res, delay));
|
await new Promise((res) => setTimeout(res, delay));
|
||||||
|
|
||||||
const serverUp = await checkHostnamePort(`${env.PROD_SERVER}:1433`);
|
const serverUp = await checkHostnamePort(`${env.PROD_SERVER}:1433`);
|
||||||
|
|
||||||
if (!serverUp) {
|
if (!serverUp) {
|
||||||
delay = Math.min(delay * 2, 30000); // exponential backoff up to 30s
|
delay = Math.min(delay * 2, 30000); // exponential backoff up to 30s
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pool = sql.connect(sqlConfig);
|
pool = sql.connect(sqlConfig);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Connected to ${sqlConfig?.server}, and looking at ${sqlConfig?.database}`
|
`Connected to ${sqlConfig?.server}, and looking at ${sqlConfig?.database}`,
|
||||||
);
|
);
|
||||||
reconnecting = false;
|
reconnecting = false;
|
||||||
connected = true;
|
connected = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.fatal(
|
log.fatal(
|
||||||
`${JSON.stringify(
|
`${JSON.stringify(
|
||||||
error
|
error,
|
||||||
)}, "There was an error connecting to the pool."`
|
)}, "There was an error connecting to the pool."`,
|
||||||
);
|
);
|
||||||
delay = Math.min(delay * 2, 30000); // exponential backoff up to 30s
|
delay = Math.min(delay * 2, 30000); // exponential backoff up to 30s
|
||||||
// throw new Error("There was an error closing the sql connection");
|
// throw new Error("There was an error closing the sql connection");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
log.fatal(
|
log.fatal(
|
||||||
{ notify: true },
|
{ notify: true },
|
||||||
"Max reconnect attempts reached on the prodSql server. Stopping retries."
|
"Max reconnect attempts reached on the prodSql server. Stopping retries.",
|
||||||
);
|
);
|
||||||
reconnecting = false;
|
reconnecting = false;
|
||||||
// optional: exit process or alert someone here
|
// exit process or alert someone here
|
||||||
// process.exit(1);
|
// process.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const closePool = async () => {
|
export const closePool = async () => {
|
||||||
const log = createLogger({ module: "prodSql" });
|
const log = createLogger({ module: "prodSql" });
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
log.error("There is no connection a connection.");
|
log.error("There is no connection a connection.");
|
||||||
return { success: false, message: "There is already a connection." };
|
return { success: false, message: "There is already a connection." };
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await pool.close();
|
await pool.close();
|
||||||
log.info("Connection pool closed");
|
log.info("Connection pool closed");
|
||||||
connected = false;
|
connected = false;
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "The sql server connection has been closed",
|
message: "The sql server connection has been closed",
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.fatal(
|
connected = false;
|
||||||
{ notify: true },
|
log.info(
|
||||||
`${JSON.stringify(
|
//{ notify: true },
|
||||||
error
|
{ error: error },
|
||||||
)}, "There was an error closing the sql connection"`
|
`${JSON.stringify(
|
||||||
);
|
error,
|
||||||
}
|
)}, "There was an error closing the sql connection"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user