共享PostgreSQL连接的AWS Node.js 2.x Lambda第二个无返回值求助
项目结构
project-root/ │ ├── src/ │ ├── lambda1/ │ │ ├── index.ts │ │ ├── Service.ts │ │ ├── package.json │ │ └── tsconfig.json │ │ │ ├── lambda2/ │ │ ├── index.ts │ │ ├── Service.ts // 注:原描述中为Service.json,推测应为Service.ts │ │ ├── package.json │ │ └── tsconfig.json │ │ │ └── shared/ │ ├── dbconnect.ts │ ├── constants.ts │ ├── tsconfig.json ├── package.json
问题现象
部署两个Lambda后,第一个Lambda可正常返回数据,但第二个Lambda既无返回值也不抛出错误,日志如下:
INIT_START Runtime Version: nodejs:20.v15 Runtime Version ARN: arn:aws:lambda:us-east-2::runtime:0856f5d760b75612d4aa6b5feda50bafc1ce3ba07c036cbd0abc585dc28fb870
START RequestId: 85af3fa6-2044-4664-9d67-9890be0a088a Version: $LATEST
2024-02-16T10:59:14.484Z 85af3fa6-2044-4664-9d67-9890be0a088a INFO Inside GetLegalDashBoardData
2024-02-16T10:59:14.523Z 85af3fa6-2044-4664-9d67-9890be0a088a INFO Inside Service connectionString
2024-02-16T10:59:14.524Z 85af3fa6-2044-4664-9d67-9890be0a088a INFO Inside ConnectToDatabase
2024-02-16T10:59:14.726Z 85af3fa6-2044-4664-9d67-9890be0a088a INFO Inside ConnectToDatabase before return
2024-02-16T10:59:14.726Z 85af3fa6-2044-4664-9d67-9890be0a088a INFO After ConnectToDatabase
END RequestId: 85af3fa6-2044-4664-9d67-9890be0a088a
REPORT RequestId: 85af3fa6-2044-4664-9d67-9890be0a088a Duration: 525.35 ms Billed Duration: 526 ms Memory Size: 128 MB Max Memory Used: 76 MB Init Duration: 191.40 ms
关键代码分析
dbconnect.ts 原代码
import { IDatabase, IMain } from 'pg-promise'; import pgpromise from 'pg-promise'; async function ConnectToDatabase(user: string, password: string, host: string, port: number, database: string): Promise<IDatabase<any>> { try { console.log("Inside ConnectToDatabase"); const pgp = pgpromise(); const connectionOptions = { host: host, port: port, database: database, user: user, password: password, }; const dbClient = pgp(connectionOptions) console.log("Inside ConnectToDatabase before return"); return dbClient; } catch (error) { console.error('Error connecting to the database:', error); throw error; } } export async function ExecuteFunction(query: string, connectionParams: any) { try { const dbCreds = JSON.parse(connectionParams); const plsqlQuery: any = JSON.parse(query) const dbInstance = await ConnectToDatabase(dbCreds.user, dbCreds.password, dbCreds.host, dbCreds.port, dbCreds.database); console.log(`After ConnectToDatabase`) const result = dbInstance.func(plsqlQuery.funcName, plsqlQuery.params); return result; } catch (error) { console.error('ExecuteFunction Error in Fetching Records From the database:', error); throw error; } }
Service.ts 原代码
import { config } from '../../../shared/config/whub_config'; import { ExecuteFunction } from '../../../shared/data_access/whub_data_service'; export async function GetLegalDashBoardData() { console.log("Inside GetLegalDashBoardData") try { const dbCred: any = GetHubDBCredentials(); const query: any = { funcName: `${config.WHUB_LEGAL_SCHEMA}.${config.LEGAL_GET_DASHBOARD_DATA}`, params:"test" } console.log(`Inside Service connectionString`) const result = await ExecuteFunction(JSON.stringify(query),JSON.stringify(dbCred)); return result; } catch (error) { console.error('Error executing query', error); console.log('Error executing query', JSON.stringify(error)); } }
问题根源
- 未等待数据库函数执行:
ExecuteFunction中调用dbInstance.func()时未添加await,该方法返回Promise,但代码直接返回未解析的Promise,导致Lambda在数据库查询完成前就因事件循环为空而终止,无返回结果。 - 不必要的异步包装:
ConnectToDatabase函数被标记为async,但内部没有异步操作(pg-promise实例化是同步的,连接是懒加载),多余的async会导致返回Promise,但实际不需要。 - 重复创建pgp实例:每次调用
ConnectToDatabase都新建pgp实例,可能导致资源泄漏或连接问题。
修复方案
1. 修改dbconnect.ts,添加await并优化实例创建
import { IDatabase } from 'pg-promise'; import pgpromise from 'pg-promise'; // 全局单例pgp实例,避免重复创建 const pgp = pgpromise(); // 移除不必要的async,同步返回数据库实例 function ConnectToDatabase(user: string, password: string, host: string, port: number, database: string): IDatabase<any> { try { console.log("Inside ConnectToDatabase"); const connectionOptions = { host, port, database, user, password, }; const dbClient = pgp(connectionOptions); console.log("Inside ConnectToDatabase before return"); return dbClient; } catch (error) { console.error('Error connecting to the database:', error); throw error; } } export async function ExecuteFunction(query: string, connectionParams: string) { try { const dbCreds = JSON.parse(connectionParams); const plsqlQuery = JSON.parse(query) as { funcName: string; params: any }; const dbInstance = ConnectToDatabase(dbCreds.user, dbCreds.password, dbCreds.host, dbCreds.port, dbCreds.database); console.log(`After ConnectToDatabase`); // 关键:await数据库函数执行,确保等待查询完成 const result = await dbInstance.func(plsqlQuery.funcName, plsqlQuery.params); return result; } catch (error) { console.error('ExecuteFunction Error in Fetching Records From the database:', error); throw error; } }
2. 确保Lambda Handler正确等待结果
检查lambda2的index.ts,确保handler正确await GetLegalDashBoardData并返回结果:
import { GetLegalDashBoardData } from './Service'; export const handler = async () => { try { const data = await GetLegalDashBoardData(); return { statusCode: 200, body: JSON.stringify(data) }; } catch (error) { console.error('Handler failed:', error); return { statusCode: 500, body: JSON.stringify({ message: 'Failed to retrieve dashboard data' }) }; } };
3. 补充异常处理(可选)
在GetLegalDashBoardData中,若catch块未抛出错误,会返回undefined,建议在catch中重新抛出错误或返回明确错误对象,确保Handler能捕获并处理:
export async function GetLegalDashBoardData() { console.log("Inside GetLegalDashBoardData") try { const dbCred: any = GetHubDBCredentials(); const query: any = { funcName: `${config.WHUB_LEGAL_SCHEMA}.${config.LEGAL_GET_DASHBOARD_DATA}`, params:"test" } console.log(`Inside Service connectionString`) const result = await ExecuteFunction(JSON.stringify(query),JSON.stringify(dbCred)); return result; } catch (error) { console.error('Error executing query', error); // 重新抛出错误,让Handler处理 throw error; } }
额外检查项
- 确认第二个Lambda的VPC、安全组配置与第一个一致,确保能访问数据库
- 检查数据库函数
${config.WHUB_LEGAL_SCHEMA}.${config.LEGAL_GET_DASHBOARD_DATA}是否存在且权限正确 - 验证
GetHubDBCredentials返回的数据库凭证是否正确
内容的提问来源于stack exchange,提问作者balajivaishnav

