#java #postgresql #aws-lambda #mybatis #pam
Вопрос:
Я пытаюсь подключиться к экземпляру RDS Aurora Postgres с помощью IAM auth и MyBatis-Guice для запуска некоторых операторов вставки. Изначально настройка работает нормально. Однако через некоторое время вызов lambda завершается ошибкой с FATAL: PAM authentication failed for user "xxx"
. Обычно это происходит при одновременных вызовах. Но затем следующие вызовы завершаются успешно.
Это мой метод ДАО:
@Transactional(executorType=ExecutorType.BATCH, isolationType=Isolation.READ_COMMITTED)
public void insertEntities(List<Entity> entities) {
for(Entity entity: entities) {
myMapper.insert(entity);
}
}
Эта ошибка не должна возникать, поскольку токен IAM и Лямбда имеют ограничение по времени 15 минут.
Кроме того, просматривая исходный код, я вижу, что dataSourceProvider привязан к источнику данных в одноэлементной области в MyBatisModule. Я предполагаю, что какой-то параллельный getAuthToken()
вызов лямбды изменяет ожидаемый токен для роли postgres, и когда вызывается любая другая «теплая» параллельная лямбда (вместе с лямбдой, которая изменила пароль) и открывает новый управляемый сеанс с источником данных, настроенным на более старый токен, он получает сообщение об PAM authentication
ошибке.
Как я мог бы смягчить это?
Ответ №1:
Я устанавливал пароль каждый раз, когда вызывалась лямбда — функция. Я внес несколько изменений в свой метод DAO и использовал SqlSession вместо картографов. Это делается для того, чтобы избежать кэширования SqlSession, которое выполняет базовый SqlSessionManager при использовании методов сопоставления непосредственно в mybatis-guice @Transactional
. Используемый здесь SqlSessionFactory находится в одноэлементной области и вводится в экземпляр DAO.
public void insertEntities(List<Entity> entites) {
try(SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, TransactionIsolationLevel.READ_COMMITTED) {
try {
MyMapper myMapper = session.getMapper(MyMapper.class);
// do something
session.commit();
} catch(Exception e) {
session.rollback();
throw e;
}
}
}
И при каждом вызове я устанавливаю пароль в качестве маркера аутентификации IAM в источнике данных.
MyDataSource dataSource = (MyDataSource) sqlSessionFactory.getConfiguration().getEnvironment().getDataSource();
dataSource.setPassword(generator.getAuthToken(request));
Теперь всякий раз, когда вызывается функция Lambda, источник данных будет иметь новый токен аутентификации и сможет подключаться к базе данных.