170 похожих чатов

На проекте архитектор кинул "актуальную статью", как выкачать через Spring

JPA данные, превышающие память микросервиса (минимальный фетч сайз + запись в файл). Мне кажется это очень плохо для практики, есть другие мнения? Сама статья: https://medium.com/predictly-on-tech/spring-data-jpa-batching-using-streams-af456ea611fc

3 ответов

16 просмотров

При выполнении запроса к БД, Postgres по умолчанию выполняет запрос целиком и кеширует результат в виде набора всех доступных строк (https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java#L117). Далее, Hibernate обходит этот набор строк для формирования списка (https://github.com/hibernate/hibernate-orm/blob/5.5/hibernate-core/src/main/java/org/hibernate/loader/Loader.java#L1043) и последующей трансформации в список объектов, получаемых из репозитория. Полезные ссылки: https://jdbc.postgresql.org/documentation/query/#getting-results-based-on-a-cursor Для того, чтобы изменить это поведение нужно выполнить несколько условий: 1. Метод репозитория должен возвращать не массив/список, а Stream; 2. Чтобы при формировании запроса ответ целиком не выгружался в память, стоит указать размер буфера, используемого для хранения результатов из БД. 3. Так как запрос у нас теперь ленивый (Stream будет получать данные из БД по мере необходимости, например для формирования результирующего json), то на время всего запроса нужно держать открытой транзакцию; Для ознакомления можно обратиться к https://github.com/hibernate/hibernate-orm/blob/5.5/hibernate-core/src/main/java/org/hibernate/internal/ScrollableResultsImpl.java ; Таким образом запрос становится вида: interface CurrentEntityRepository extends JpaRepository<UUID, CurrentEntity> { @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.FETCH_SIZE, value = "1000")) @Query("select c from CurrentEntity c") Stream<CurrentEntity> findAllEntities(); } Так как для работы с открытым стримом из БД нужно держать открытой транзакцию, возникает вопрос как правильно сформировать ответ. Мы не сможем повесить на метод контроллера @Transactional поскольку маршалинг происходит после завершения метода контроллера и закрытия транзакции (для json'a - тут https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java#L440). На данный момент можно выбрать минимум 2 подхода - явный и неявный. Явный подход заключается в явном открытии транзакции для получения стрима и явном закрытии после окончания формирования ответа сервиса. В таком случае контроллер принимает вид: @GetMapping("/api/v1/entities") @Transactional(readOnly = true) void findAllEntities(HttpServletResponse httpServletResponse) throws IOException { httpServletResponse.setStatus(HttpServletResponse.SC_OK); try (var data = currentEntityService.findAllEntities()) { var writer = new BufferedWriter(new OutputStreamWriter(httpServletResponse.getOutputStream())); objectMapper.writerFor(Iterator.class).writeValue(writer, data.iterator()); } } Неявный подход - в делегировании логики работы с транзакциями фильтру виду : class StreamApiTransactionalFilter extends OncePerRequestFilter { private final PlatformTransactionManager platformTransactionManager; public StreamApiTransactionalFilter(PlatformTransactionManager platformTransactionManager) { this.platformTransactionManager = platformTransactionManager; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Проверка, что API нужно нужно открывать транзакцию заранее TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager); transactionTemplate.setReadOnly(true); transactionTemplate.executeWithoutResult(x -> { try { filterChain.doFilter(request, response); } catch (IOException | ServletException e) { throw new RuntimeException(e); } }); } }

а что сделвать хотят?

мы делали свой маппер качающий 2гб из базы в память тк вся логика шла на торговле ассетами из памяти (биржа) так что бывает все что угодно если требует задача ) но надо хорошо подумать 1 есть ли у микросервиса диск 2 нужен ли он ему (или лучше memcache/redis) 3 не создаст ли это больше проблем чем решает

Похожие вопросы

Обсуждают сегодня

Господа, а что сейчас вообще с рынком труда на делфи происходит? Какова ситуация?
Rꙮman Yankꙮvsky
29
А вообще, что может смущать в самой Julia - бы сказал, что нет единого стандартного подхода по многим моментам, поэтому многое выглядит как "хаки" и произвол. Короче говоря, с...
Viktor G.
2
30500 за редактор? )
Владимир
47
а через ESC-код ?
Alexey Kulakov
29
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
14
Добрый день! Скажите пожалуйста, а какие программы вы бы рекомендовали написать для того, чтобы научиться управлять памятью? Можно написать динамический массив, можно связный ...
Филипп
7
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Ребят в СИ можно реализовать ООП?
Николай
33
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Карта сайта