我在AWS lambda中使用pg-promise时遇到了很多问题。我想了解如何解决这些问题。
库建议您创建一个Database对象实例,然后从模块中导出它。应该只创建一个对象实例。 类似的东西:
const db = pgp({
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
database: process.env.DATABASE_NAME,
user: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
poolSize: 0,
poolIdleTimeout: 10,
});
module.exports = db;
我知道这只是一个对象而且没有在这里创建连接。当您在此db对象上运行任何内容时,将会延迟创建连接,例如db.query()。
由于我们将池大小设置为0,因此只会创建一个连接。这就是我们需要的每个lambda函数的开头,我们需要创建一个连接,然后在lambda完成时关闭连接。
我们面临的问题是:
我们如何发布连接? AWS lambdas重用容器。这意味着它将调用已经初始化的相同节点代码,并且如果在先前运行后很快发生对lambda的调用,则重新运行相同的函数。 这意味着db对象对于lambda的下一次调用将是相同的。在我们的第一个lambda完成后,如果我们调用pgp.end(),文档说连接池关闭。它还说我们不能在之后的同一个过程中使用pgp库。但是这个库将被用作db对象仍然存在并将在后续运行中使用。
我们如何重新尝试获取新连接? AWS lambda构成的另一个问题是,当您在VPC中运行lambda并且Postgres实例也在VPC内运行时,postgres数据库的DNS需要时间来解析。因此,如果您尝试连接,可能会收到ENOTFOUND错误。 AWS的建议是重试获取连接。使用pg-promise我们如何重试获取连接?
我想实现它的方式是:
module.exports.handler = (event, context, callback) => {
let connection;
try {
connection = /*gets connection and retries if it failed the first time*/
// run db queries and transactions.. etc.
callback(null, result);
} finally {
connection.close();
}
}
答案 0 :(得分:3)
这就是我们的目标。
jist在每个lambda开始之前创建一个新连接,然后在从lambda返回之前将其关闭
// your lambda entry point
module.exports.handler = (event, context, callback) =>
getConnection(async (connection) => {
let result;
try {
// work with your connection
} catch (error) {
}
callback(null, result);
})
// db connection
const getConnection = async (callback) => {
const dbConnection = new DBConnection();
try {
const connection = await dbConnection.create();
await callback(connection);
} finally {
dbConnection.close();
}
};
const MAX_RETRY = 3;
const options = {
// global event notification;
error: (error, e) => {
if (e.cn) {
// A connection-related error;
//
// Connections are reported back with the password hashed,
// for safe errors logging, without exposing passwords.
logger.error('CN:', e.cn);
logger.error('EVENT:', error.message || error);
}
},
};
const pgp = require('pg-promise')(options);
const connectionParams = {
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
database: process.env.DATABASE_NAME,
user: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
poolSize: 0,
poolIdleTimeout: 10,
};
const db = pgp(connectionParams);
class DBConnection {
async create() {
let retry = 0;
while (retry < MAX_RETRY) {
try {
logger.debug(`Acquiring a new DB connection Attempt: ${retry}/${MAX_RETRY}`);
this.connection = await db.connect({ direct: true });
break;
} catch (error) {
logger.error(`Error occurred while getting DB connection ${error}. Retrying ${retry}/${MAX_RETRY}`);
retry += 1;
}
}
if (!this.connection) {
throw Error(`Unable to obtain DB connection after ${MAX_RETRY} retries`);
}
return this.connection;
}
close() {
if (this.connection) {
logger.debug('Closing DB Connection');
this.connection.done();
}
}
}
答案 1 :(得分:1)
我们如何释放连接?
你没有。连接自动与连接池通信。一旦查询结束执行,连接就会返回到池中,以供下一个请求它的查询使用。
一次执行多个查询时,您应该使用任务。请参阅Chaining Queries。
我们如何重新尝试获取新连接?
根据连接需求和最大池大小,连接池会在必要时自动创建新的物理连接。当连接断开时,会自动重新创建。
如果您在AWS lambda中拥有单一连接,那么最好的模式可能是创建和维护单个全局连接吗?
如果是这种情况,Robust Listeners示例可能对您有用。它显示了如何在连接池之外维护单个全局连接,以及如何始终保持活动状态。
但这更像是最后的手段。我相信只需使用自动连接池就足够了。