Производительность в тестах
В данной статье не пойдет речь решение проблем производительности в интеграционных тестах в Spring, с которыми я столкнулся. Будут предложены решения.
Интеграцинные тесты предполагают поднятие контекста приложения, поднятие базы, кафки и т.д. Testcontainers улучшил опыт работы с внешними зависимостями, есть даже возможность использовать нативный образ кафки. Самый лучший вариант - работать в рамках одного приложения. Но при этом нам нужно сохранять изоляцию тестов друг от друга, это относится как к изоляции в тестах с кафкой, так и в тестах, которые требуют разного состояния контекста. Спринг кеширует контексты (на основе чего?) и позволяет нам переиспользовать и не создавать новые. Но если наши тесты отличаются настройкой, то создание новых контекстов неминуемо.
Менеджмент контекстов
Не очевидные вещи про @DirtiesContext
TestPropertySource
Допустим вы написали тест, все хорошо. Вам нужно написать еще один тест, но с одним не большим отличием в плане контекста - с одним изменененным свойством, или с двуми или … в целом количество свойств ни на что не влияет. Вы написали тест и использовали TestPropertySource
и все хорошо. Потом еще раз и еще и еще. И в какой-то момент вы ловите первую ошибку по нехватке памяти. Почему такое произошло? Потому что создаются новые контексты и не чистятся - смотрите баги. Есть механизм лимита не кеши, но … В общем анализ свойств может показать, что контексты оформленные для отдельных тестовых сценариев не переиспользуются - разовые. Давайте из удалять через @DirtyContext.
DynamicTestPropertySource
В случае если есть возможность
Статья про cloud config, @RefreshScope - https://habr.com/ru/companies/otus/articles/590761/.
Memory leaks
Memory leak in Spring
Ресурсоемкие методы
Cегодня профилировал тесты и нашел два ресурсоемких метода:
- Связан с этой проблемой, знакомой нам уже, - https://stackoverflow.com/questions/76704872/spring-boot-3-high-cpu-usage-on-basicauth NoOpPasswordEncoder for spring auth
- Какой-то новый и мне не знакомый
java.lang.invoke.VarHandleByteArrayAsInts$ArrayHandle.index
org.postgresql.shaded.com.ongres.scram.common.util.CryptoUtil.hi
org.postgresql.shaded.com.ongres.scram.common.ScramMechanisms.saltedPassword
На сколько я понял это часть драйвера PostgreSQL JDBC для защиты паролей в процессе аутентификации. Отключить можно указав переменную для контейнера
private final static PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(DockerImageName.parse("postgres:14"))
.withDatabaseName("application")
.withEnv("POSTGRES_HOST_AUTH_METHOD", "trust") <----- ТУТ
.withCommand("postgres -c max_connections=300")
.waitingFor(Wait.forListeningPort());
и
private void execute(PGSimpleDataSource dataSource) throws SQLException {
for (int i = 0; i < 100; i++) {
try (Connection connection = dataSource.getConnection()) {
connection.prepareStatement("select 1;").execute();
}
}
}
и можно уже не передавать пароль
PGSimpleDataSource dataSource = new PGSimpleDataSource();
dataSource.setUser(container.getUsername());
dataSource.setUrl(container.getJdbcUrl());
execute(dataSource);
Синтетический бенчмарк показывает прирост в 20% при использовании trust
.
Benchmark Mode Cnt Score Error Units
PostgresqlContainerAuthModeBenchmark.methodDefault ss 20 2,202 ± 0,049 s/op
PostgresqlContainerAuthModeBenchmark.methodTrust ss 7 1,552 ± 0,100 s/op
Я еще погонял CI с включенным и выключенным, в целом картина на CI повторяет ту же тенденцию.