Привет, это снова Егор. И да - это снова статья про Фидонет. Так уж сложилось, что именно эта тематика оказался слишком уж популярным на моём Хабре, и неразрывно связана со мной, хотя я всячески пытался "сбежать" от данной тематики.

Однако я решил совместить приятное с полезным - рассказать про недавние мои поправки в NodehistJ, которые значительно ускорит индексацию диффов нодлистов сети Фидонет, а также значительно снизит потребление ОЗУ, за счёт одного из лучших вещей, которая есть в Spring Data JDBC - live streaming данных с СУБД без использования неэффективной пагинации. Данная технология позволяет быстро перебирать нодлисты за считанные минуты, и ускорить индексацию даже на слабых серверах, за счёт прямого перебора данных из СУБД, в live-режиме, без использования неэффективной пагинации, но с использованием самых обычного Java Streams.

Что нужно для этого

Для того, чтобы использовать данную технологию, необходимо создать в репозитории метод с кастомным запросом, и с типом Stream<T>. Вот пример:

@Query("""
        SELECT * FROM nodelist_entry nl
        JOIN node_entry n
        ON nl.id = n.nodelist_entry_id
        ORDER BY nodelist_year DESC, nodelist_name DESC
        """)
Stream<NodelistEntry> findAllAsStreamWithSort();

А дальше просто обращаемся к этому методу, и работаем со Streams как обычно:

nodeListEntries
    .gather(Gatherers.windowSliding(2))
    .map(window -> processDiffBetweenNodelists(window.get(0), window.get(1)))
    .flatMap(list -> list.stream())
    .forEach(nodeHistoryEntryRepository::save);

Я использую .gather() для создания "скользящего окна", но оно появилось в LTS-версиях Java только с 25 ветки. Мне пришлось мигрировать из за этого NodehistJ на новую версию Java.

Зачем мы берём целый нодлист вместо того, чтобы брать конкретные записи с него?

Это позволяет уменьшить количество запросов к нодлисту, но при этом жертвуя небольшим количеством ОЗУ. Благо в памяти мы храним только 2 нодлиста, по сути, и даже нодлисты за 1996 год не так сильно сожрёт ОЗУ. Но если мы не будем их хранить, мы замедлим работу уже с СУБД, что тоже неприятно.

Зачем я всё заново индексирую?

Вы наверное заметили это в моём коде:

nodeHistoryEntryRepository.deleteAll();

Так вот - именно это и обеспечивает гарантию того, что нодлисты будут задиффованны в нужном порядке. Да, это требует кучу ресурсов - зато это самое надёжное и безопасное решение. Лишние полчаса индексации в одном из сервисов никому не помешает :)

Итоги

Java Streams и Spring Boot JDBC дополняет друг-друга, и позволяет ускорить индексацию всего нодлистового архива, даже при полной индексации.

Кому нужен джавист к себе?

Комментарии (8)


  1. kolezz
    31.12.2025 21:31

    Может стоит рассказать что это вообще за проект и что он делает. Чтобы понять что именно ускоряется. И чем лучше, например, какой-нибудь системы контроля версий для исторических исследований


    1. oldzoomer Автор
      31.12.2025 21:31

      Если вкратце, это аналог nodehist.wfido.ru, только уже в рамках современного стека.

      Проект, по большей части, является полигоном, в котором я практикую многие современные технологии в Java-мире.


  1. ris58h
    31.12.2025 21:31

    В статье есть утверждения типа «значительно снизит потребление ОЗУ» и что-то про неэффективность другого подхода и ускорение, но я так нигде и не увидел цифр, которые бы наглядно показали читателю каких результатов удалось добиться.


    1. oldzoomer Автор
      31.12.2025 21:31

      У меня раньше, когда я использовал findAll() типа List<T>, NodehistJ мог сожрать огромное количество ОЗУ (требовался сервер с 8+ Гб ОЗУ). Потом перешёл на пагинацию - но это приводило к замедлению уже на стороне СУБД (из за LIMIT+OFFSET), и нодлисты могли грузиться по несколько часов (на слабых серверах) из за полного перебора по всему БД. Сейчас же индексация проходит намного шустрее, и не требует значительного объема ОЗУ (NodehistJ требует теперь 3-4 гига ОЗУ, из за своей микросервисной сущности).


      1. FisHlaBsoMAN
        31.12.2025 21:31

        Сервер для фидо, жрущий 8гб.. Вот они современные технологии!


        1. oldzoomer Автор
          31.12.2025 21:31

          Ну, сейчас оно где-то 2 гига жрёт в чистую. И то это связано с микросервисной архитектурой, где нужно несколько раз пихать практически один и тот же спринговый контекст в ОЗУ.

          Индексация же сейчас проходит намного быстрее - в среднем, индексация диффов на Cloud.ru Free Tier, без managed DBMS, занимает 5 минут максимум.


          1. oldzoomer Автор
            31.12.2025 21:31

            Сейчас проект перевожу на GraalVM, и очень надеюсь, что это снизит расход ОЗУ на порядок. Уже сейчас GraalVM позволило уменьшить размер Docker-образа за счёт использования native build вместо JRE, а если будет ещё и значительное уменьшение траты ОЗУ, будет вообще класс.


            1. oldzoomer Автор
              31.12.2025 21:31

              Короче, гралька мне чёт не зашла. Эффективность не такая высокая - зато куча гемора с теми либами, которые юзают рефлексию.

              А так, NodehistJ спокойно работает на Free Tier от Cloud.ru