AWS lambda中的pg-promise

时间:2017-07-04 01:46:48

标签: node.js postgresql aws-lambda pg-promise

我在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完成时关闭连接。

我们面临的问题是:

  1. 我们如何发布连接? AWS lambdas重用容器。这意味着它将调用已经初始化的相同节点代码,并且如果在先前运行后很快发生对lambda的调用,则重新运行相同的函数。 这意味着db对象对于lambda的下一次调用将是相同的。在我们的第一个lambda完成后,如果我们调用pgp.end(),文档说连接池关闭。它还说我们不能在之后的同一个过程中使用pgp库。但是这个库将被用作db对象仍然存在并将在后续运行中使用。

  2. 我们如何重新尝试获取新连接? AWS lambda构成的另一个问题是,当您在VPC中运行lambda并且Postgres实例也在VPC内运行时,postgres数据库的DNS需要时间来解析。因此,如果您尝试连接,可能会收到ENOTFOUND错误。 AWS的建议是重试获取连接。使用pg-promise我们如何重试获取连接?

  3. 我想实现它的方式是:

    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();
     }
    
    }
    

2 个答案:

答案 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示例可能对您有用。它显示了如何在连接池之外维护单个全局连接,以及如何始终保持活动状态。

但这更像是最后的手段。我相信只需使用自动连接池就足够了。