有经验与Postgres构建并发AWS Lambda函数的人吗?
我必须构建一个lambda cron,它将成千上万张发票提取到Postgres数据库中。我必须为每个发票同时调用提取Lambda函数。问题在于,因为它是并发的,所以摄取函数的每个实例都将创建与数据库的连接。这意味着,如果我要提取1000张发票,则每张发票都将调用一个lambda函数,该函数将创建1000个数据库连接。这将耗尽Postgres可以处理的最大连接数。调用的lambda函数的某些实例将返回错误,表明没有更多可用连接。
有什么技巧可以解决这个问题?
以下是我的代码片段:
ingestInvoiceList.js
var AWS = require('aws-sdk');
var sftp = require('ssh2-sftp-client');
var lambda = AWS.Lambda();
exports.handler = async (evenrt) => {
...
let folder_contents;
try {
// fetch list of Zip format invoices
folder_contents = await sftp.list(client_folder);
} catch (err) {
console.log(`[${client}]: ${err.toString()}`);
throw new Error(`[${client}]: ${err.toString()}`);
}
let invoiceCount = 0;
let funcName = 'ingestInvoice';
for (let item of folder_contents) {
if (item.type === '-') {
let payload = JSON.stringify({
invoice: item.name
});
let params = {
FunctionName: funcName,
Payload: payload,
InvocationType: 'Event'
};
//invo9ke ingest invoice concurrently
let result = await new Promise((resolve) => {
lambda.invoke(params, (err, data) => {
if (err) resolve(err);
else resolve(data);
});
});
console.log('result: ', result);
invoiceCount++;
}
}
...
}
ingestInvoice.js
var AWS = require('aws-sdk');
var sftp = require('ssh2-sftp-client');
var DBClient = require('db.js')l
var lambda = AWS.Lambda();
exports.handler = async (evenrt) => {
...
let invoice = event.invoice;
let client = 'client name';
let db = new DBClient();
try {
console.log(`[${client}]: Extracting documents from ${invoice}`);
try {
// get zip file from sftp server
await sftp.fastGet(invoice, '/tmp/tmp.zip', {});
} catch (err) {
throw err;
}
let zip;
try {
// extract the zip file...
zip = await new Promise((resolve, reject) => {
fs.readFile("/tmp/tmp.zip", async function (err, data) {
if (err) return reject(err);
let unzippedData;
try {
unzippedData = await JSZip.loadAsync(data);
} catch (err) {
return reject(err);
}
return resolve(unzippedData);
});
});
} catch (err) {
throw err;
}
let unibillRegEx = /unibill.+\.txt/g;
let files = [];
zip.forEach(async (path, entry) => {
if (unibillRegEx.exec(entry.name)) {
files['unibillObj'] = entry;
} else {
files['pdfObj'] = entry;
}
});
// await db.getClient().connect();
await db.setSchema(client);
console.log('Schema has been set.');
let unibillStr = await files.unibillObj.async('string');
console.log('ingesting ', files.unibillObj.name);
//Do ingestion queries here...
...
await uploadInvoiceDocsToS3(client, files);
} catch (err) {
console.error(err.stack);
throw err;
} finally {
try {
// console.log('Disconnecting from database...');
// await db.endClient();
console.log('Disconnecting from SFTP...');
await sftp.end();
} catch (err) {
console.log('ERROR: ' + err.toString());
throw err;
}
}
...
}
db.js
var { Pool } = require('pg');
module.exports = class DBClient {
constructor() {
this.pool = new Pool();
}
async setSchema(schema) {
await this.execQuery(`SET search_path TO ${schema}`);
}
async execQuery(sql) {
return await this.pool.query(sql);
}
}
任何答案将不胜感激,谢谢!
答案 0 :(得分:0)
我看到了两种处理方法。最终,这取决于您要处理该数据的速度。
这将允许您限制并发Lambda的运行数量(有关更多详细信息,请参见this link)。
答案 1 :(得分:0)
首先,您必须在处理程序外部初始化连接,因此,每次执行温暖的lambda时,它都不会打开新的连接:
AppbarLayout
如果是node-pg,则有一个跟踪所有空闲连接的软件包,如有必要,请终止它们,并在出现错误或const db = new DBClient();
exports.handler = async (event) => {
...
await db.query(...)
...
}
的情况下重试:
https://github.com/MatteoGioioso/serverless-pg
具有退避功能的任何其他自定义实现的重试机制也将起作用。
答案 2 :(得分:0)
如今在 AWS 上可以考虑解决此问题的一个很好的解决方案是 RDS Proxy,它充当 lambda 和数据库之间的透明代理:
<块引用>Amazon RDS 代理允许应用程序汇集和共享与数据库建立的连接,从而提高数据库效率、应用程序可扩展性和安全性。