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

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

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

3 ответов

8 просмотров

При выполнении запроса к БД, 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 не создаст ли это больше проблем чем решает

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

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

Всем привет, написал код ниже, но он выдает сегфолт, в чем причина? #include <stdio.h> #include <stdlib.h> #include <string.h> struct product { char *name; float price; };...
buzz базз
70
Здравствуйте. Задача состоит в том, чтобы сделать real-time чат в мобильном приложении. После передачи сообщения пользователем через веб-сокеты, для основного и долговременног...
🐾
5
Всем доброго дня, ребят подскажите пожалуйста, если в курсе по ассемблеру используется MASM32, могу ли я использовать FASM? В чем явная разница и будет ли у меня все работать?
Botsman
17
Хотел бы спросить у знающих, правильную ли я выбрал книгу для начала изучения ассемблера Юрова В.И ? Или есть более лучшие книги для начала обучения?
Botsman
25
Книга Юрова В.И пойдёт для обучения?
Botsman
24
$params = [ 'formid' => 'feedbackForm', 'formTpl' => '@CODE: <form class="form-validate" data-id="ajax_form"> <fieldset class="margin-bottom-md"> ...
Pathologic
1
Люди добрые, помогите с идеями, потому что свои закончились. У клиента падает софтина в момент инициализации модуля OtlEventMonitor на RegisterWindowMessage('Gp/OtlTaskEvents/...
Михаил Усков
7
Тут просто дело в том, что я не могу сейчас дать такие подробности из за того что рассчитать это всё нереально. Этого проекта который я хочу сделать ещё даже не существует) И ...
🐾
8
> Примечательно, что новый владелец удаляет из GitHub любые жалобы, указывающие на подозрительную активность или смену владельца, и, видимо, рассчитывает на то, что пользовате...
Alex Sherbakov
2
GridView fully ignored first parent(SizedBox), and take width from second parent(Container). How can I constrain GridView by first parent? Widget build(BuildContext context) {...
Hamster
1
Карта сайта