Что такое персистентная очередь? Это очередь, события в которой не потеряются в случае перезагрузки приложения.

Персистентная очередь во фронтенде (или в мобильном приложении) может быть использована только... пока фронтенд работает. Так ли уж она полезна?

Давайте разбираться.

Сперва разберёмся с вариантами применения просто очередей на фронте (всё сказанное о фронтенде можно так же смело применять и к мобильным приложениям).

Очереди на фронтенде могут быть полезны для

  • снижения общей пиковой на бакенд. Вместо того чтобы выполнять множество запросов к бакенду одновременно, выполняем все запросы фронта в обработчике очереди. Интерактивность приложения в таких случаях страдает обычно несильно, а вот пиковую нагрузку на бакенды можно довольно серьёзно размазать во времени. Метрика RPS в таком случае означает "пользователей (приложений) в секунду".

  • организации батчинга запросов. Пока выполняется очередной запрос к бакенду, фронт копит следующие запросы в очереди. Затем выполняет их одним пакетом (конечно, API бакенда должен такое уметь).

То есть, как видим, очередь на фронте - сама по себе весьма полезный инструмент. Если кто-то с этим паттерном не знаком, весьма рекомендую попробовать использовать при построении мобильных приложений/фронта.

А что же мы можем получить от персистентной очереди?

Конечно же, проблему обратной совместимости версий приложений! :) Каждое обновление приложения должно иметь в виду, что ему придётся, возможно, обработать задачи от очереди предыдущей версии. Впрочем, это не такая уж и большая проблема. Давайте рассматривать полезные применения.

Рассмотрим несколько примеров.

Лайки, пометки просмотренности итп

Пользователь просматривает ленту новостей, ленту в соцсети и т.п. В процессе просмотра ставит лайки и дизлайки. Сохранять информацию о принятых им решениях (таплы (id: решение)) в персистентной очереди - практически идеальное архитектурное решение:

  • если в данный момент связь с интернетом есть, то информация о лайке уйдёт на сервер с минимальным лагом;

  • если со связью проблема, то обработчик очереди выполнит задачу тогда, когда проблемы уйдут (через несколько секунд, минут). Возможно, даже в другой день.

Да, теоретически, информация о принятом пользователем решении может никогда не дойти до сервера, поэтому не стоит через такой инструмент проводить, скажем, финансовые транзакции пользователя.

Хотя... В некоторых случаях именно для финансов персистентная очередь тоже подойдёт.

Терминалы оплаты

Представьте себе терминал, принимающий денежные купюры, печатающий чек и делающий зачисление денег на счёт. Не банкомат, а, например, терминал а-ля Qiwi.

Если в данный момент времени связи с внешним миром у терминала нет, то он вполне может принять деньги у пользователя, выдать ему чек. Транзакция будет исполнена тогда, когда появится связь.


Если Вы посмотрите на своё мобильное приложение или сайт, то с большой степенью вероятности Вы найдёте места, где можно использовать персистентную или обычную очередь и улучшить дружелюбность приложения к пользователю, а где-то и сгладить пики нагрузки на бек.

Но во всех случаях, когда очереди применяются для аггрегации запросов, важно помнить об идемпотентности.

Идемпотентность

Википедия: "Идемпоте́нтность — свойство объекта или операции при повторном применении операции к объекту давать тот же результат, что и при первом. Термин предложил американский математик Бенджамин Пирс в статьях 1870-х годов."

То есть, обработчик запросов фронтенда на беке должен иметь в виду, что связь между ним и приложением может быть нестабильна. А вследствие этого запросы могут повторяться.

Упреждающая простановка стейта

Поставив лайк, пользователь должен видеть результат сразу. Даже если этот лайк физически проставится позднее. Из этого следует, что действия пользователя, проводимые через очередь, в некоторых случаях должны менять локальный стейт немедленно.

Это может приводить к тому, что перезагрузка приложения с неразобранной персистентной очередью покажет пользователю, что действие ещё не выполнено.

С этим можно также бороться, добавляя персистентности в стейт. А можно... оставить, как есть. Для случаев, где это не выглядит большой проблемой.

Фреймворки

Для многих популярных фреймворков есть множество решений, реализующих персистентные и неперсистентные очереди в виде расширений фреймворков.

Если для Вашего фреймворка не нашлось очереди, то её довольно просто написать самостоятельно.

Заключение

Описанный в статье довольно простой архитектурный приём позволит строить более дружелюбные к пользователю приложения/сайты. Множество мобильных приложений могли бы быть улучшены с помощью данной технологии и вместо показа "крутилок-лоадеров" или экранов с прогрессбарами реагировать на действия пользователя моментально. Но, увы, в жизни подобные подходы встречаются редко.

Возможно, эта статья поможет уменьшить количество секунд, которые пользователь проводит в ожидании перед Вашим приложением, и сэкономленное время он потратит на выставление хорошей оценки в сторе? Кто знает...

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


  1. dopusteam
    08.10.2021 05:03
    -1

    Персистентная очередь во фронтенде (или в мобильном приложении) может быть использована только... пока фронтенд работает

    Что значит 'Пока фронтенд работает'?

    Если в данный момент времени связи с внешним миром у терминала нет, то он вполне может принять деньги у пользователя, выдать ему чек. Транзакция будет исполнена тогда, когда появится связь.

    А если пользователь ошибся в данных? А если свет вырубят? Что за ужасное решение вообще?

    выполняем все запросы фронта в обработчике очереди. Интерактивность приложения в таких случаях страдает обычно несильно

    Насколько не сильно? Выглядит как раз таки как будто сильно может пострадать

    организации батчинга запросов

    Но зачем?

    То есть, как видим, очередь на фронте - сама по себе весьма полезный инструмент

    Вы этого не показали, я этого не увидел

    А что же мы можем получить от персистентной очереди?

    Конечно же, проблему обратной совместимости версий приложений

    И ещё кучу проблем


    1. SpiderEkb
      08.10.2021 07:16

      На самом деле как-то особых проблем там нет.

      Есть Kafka, Rabbit, MQ... Все это вполне себе персистентные очереди.

      В случае, когда основной сервер, где хранятся все данные и реализована вся бизнеслогика, изолирован от внешнего мира (например, из соображений безопасности), общение с ним через персистентную очередь есть один из вариантов взаимодействия.

      Внутри сервера, платформа на которой он работает, имеет такой системный объект как DataQueue - *DTAQ (есть еще MessageQueue - *MSGQ) - это тоже персистентные очереди. *DTAQ широко используется для распараллеливания обработки в качестве конвейера при построении батчмашин. Головное задание запускает несколько однотипных заданий-обработчиков, затем занимается выборкой данных для обработки, формированием "пакетов" и выкладыванием их на конвейер. Обработчики разбирают пакеты с конвейера и обрабатывают их. Скорость конвейера тут не является критичной (в достаточно широких пределах) т.к. в конечном итоге все упирается в скорость обработки. В отличии от сокетов и пайпов *dtaq мониторится - можно всегда узнать сколько там в данное время лежит пакетов, оценить скорость разбора (обработки), варьировать количество обработчиков, избегать переполнения очереди.

      Так что персистентные очереди вполне себе мощный инструмент.


      1. dopusteam
        08.10.2021 10:18

        Есть Kafka, Rabbit, MQ

        Вы сравниваете очередь в памяти браузера (или где она там) с Кафкой, серьёзно? Очереди - штука крутая в том числе за счёт гарантий которые они дают, а тут всё лежит в браузере и может быть сломано в любой момент, ну не знаю.
        Ну и они дают задержку в отличие от прямого запроса, что на клиенте может быть более чем критично


        1. SpiderEkb
          09.10.2021 09:51

          Вообще-то "персистентная" подразумевает что она не исчезнет после закрытия или падения браузера (или что там у вас работает). Т.е. это явно не память браузера, а некое хранилище на диске.

          Мне приходилось разрабатывать систему, которая была критична к сохранности данных. Т.е. все, что компонент системы "принял в свою зону ответственности" ни при каких условиях не могло пропасть и должно было быть обработано и отправлено дальше. И задержка обработки/пересылки была менее критична, нежели потеря информации (которая по условиям задачи была недопустима). И там были персистентные очереди. Получили пакет - положили во входную очередь - отправили подтверждение - обработали, послали дальше - удалили из входной очереди - положили в выходную - получили подтверждение - удалили из выходной.

          При старте первым делом проверяем содержимое очередей.

          Да, это снижает скорость обработки. Но на то и дан нам мозг чтобы оценивать уровни критичности различных событий и правильно выбирать подходящий алгоритм, реализующий нужный сценарий снижения рисков.

          Кстати, пакет тоже может иметь метку уровня критичности. От "попробовать доставить - не получится да и бог с ним" до "доставить любой ценой в любом случае". И это у нас тоже было реализовано.


    1. git-merge Автор
      08.10.2021 09:21
      +1

      Что значит 'Пока фронтенд работает'?

      Если пользователь закроет окно браузера (или приложение), то то что осталось в персистентной очереди не будет обработано до тех пор пока браузер/приложение снова не запустят.

      А если пользователь ошибся в данных? А если свет вырубят? Что за ужасное решение вообще?

      Если пользователь ошибается, ему делают экраны "вы уверены?" итп.

      Если Вы в банкомате переводили деньги и нечаянно перевели не ту сумму, что Вы можете сделать? Ничего. Звонить в банк и ждать решения вашего вопроса в течение нескольких дней.

      Здесь отличий никаких

      Насколько не сильно?

      На доли секунд. Понятное дело, что если где-то это критично то решение не подойдёт. Важно никакое решение не воспринимать как "серебрянную пулю". Таковой не существует.


      1. dopusteam
        08.10.2021 10:12
        -1

        Если Вы в банкомате переводили деньги и нечаянно перевели не ту сумму, что Вы можете сделать? Ничего. Звонить в банк и ждать решения вашего вопроса в течение нескольких дней.

        Здесь отличий никаких

        Банк проверяет не только сумму, но и корректность счета, например, и много чего ещё. А у Вас терминал деньги забрал, но банку ничего не отправил и что мне делать? Искать производителей терминала? Это явно не 'здесь отличий никаких'.

        Если пользователь закроет окно браузера (или приложение), то то что осталось в персистентной очереди не будет обработано до тех пор пока браузер/приложение снова не запустят.

        При старте системы придётся учитывать, что возможно, есть очередь, которая уже, возможно, неактуальна и нужно разбираться в этом всём.


        1. git-merge Автор
          08.10.2021 13:58

          При старте системы придётся учитывать

          я об этом написал в статье.

          При использовании любой технологии что-то придётся учитывать. Это плата за использование технологии.


        1. git-merge Автор
          08.10.2021 22:18

          А у Вас терминал деньги забрал, но банку ничего не отправил и что мне делать?

          то же что с банкоматом.

          звонить в поддержку, посылать им фото чека (если он есть) или называть время, сумму и телефон, привязанный к счёту.

          никакой разницы.

          мы делали такую систему на приём выручки от таксистов.

          Терминал собирал в себя до нескольких миллионов в день.

          в терминале авторизационный кеш для таксистов (номера их телефонов).

          таксист вносит сумму, указывая свой номер счета (с контрольной суммой внутри цифры) или номер телефона (проверяется по кешу).

          затем вносит деньги. Транзакция попадает в персисиентную очередь браузера.

          Есть связь или нет её совершенно не важно. Когда-либо связь восстановят и деньги доедут до счета :)


          1. dopusteam
            08.10.2021 22:46

            звонить в поддержку, посылать им фото чека (если он есть) или называть время, сумму и телефон, привязанный к счёту.

            Я ж Вам привел пример когда терминал ничего не отправил, так как я ошибся в данных и в банк вообще ничего не ушло. Деньги остались в терминале. Это ещё лишняя прослойка какая то.

            Я предпочитаю узнать о проблеме сети в терминале, а не получить ложное заявление и чек. Который, кстати, юридически вообще может быть выдан если операция не совершена? Время транзакции какое на чеке?


            1. git-merge Автор
              09.10.2021 09:19

              если вы ошиблись в данных, терминал Вам написал на экране "Ошибка", Вы исправили данные и все хорошо.


              1. dopusteam
                09.10.2021 09:35
                -1

                Банк проверяет не только сумму, но и корректность счета, например, и много чего ещё

                Вы как то упускаете то, что я писал раньше

                Терминал не может проверить всё.


                1. git-merge Автор
                  09.10.2021 10:04

                  я это уже комментировал.

                  терминал может проверить.

                  многие вещи очень легко проверяются оффлайн

                  вот например номер карты

                  1234 5678 9012 3456

                  Довольно просто проверяется на валидность

                  • код оператора карты

                  • контрольная сумма

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

                  В обоих случаях (если такое таки произошло) после ошибки клиент будет разбираться с поддержкой банка, которая будет смотреть в логи транзакций или в логи самого банкомата, если там сбой его уровня произошёл или в записи видеокамер наблюдения.

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


                  1. dopusteam
                    09.10.2021 11:41

                    Вы игнорируете ошибки, которые нельзя проверить офлайн

                    Вот я ввёл счёт (не номер карты) некорректно.

                    Терминал сказал, что всё ок.

                    Денег нет.

                    Банк ничего не знает о переводе, так как либо терминал ещё не отправил данные, либо не смог отправить т.к. банк отклонил

                    Мне теперь банк ничем не поможет, мне нужно в терминал, понимаете? Вероятно, в конкретный терминал в конкретном городе

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

                    Именно

                    И пользователь должен знать об этом, а Вы вводите в заблуждение


                    1. git-merge Автор
                      09.10.2021 11:53

                      в номере счёта точно так же присутсвуют опорные цифры, как и в номере карты


                      1. dopusteam
                        09.10.2021 11:58
                        -1

                        Вы утверждаете, что офлайн можете проверить всё и банк никогда не откажет в операции по причине ошибки в данных, я понял


                      1. git-merge Автор
                        09.10.2021 16:10

                        я утверждаю что серебряных пуль не бывает.

                        а если банк делает терминал для зачисления денег исключительно себе на счета, то да - не откажет.

                        обобщенному банкомату, конечно такое решение не подойдет.

                        Терминалу по оплате квартплаты, зачислению денег на телефон, автомату по сбору выручки итп - вполне


  1. navferty
    08.10.2021 13:33
    +1

    Поставив лайк, пользователь должен видеть результат сразу. Даже если этот лайк физически проставится позднее.

    А вот меня такое поведение в некоторых сетях расстраивает. Фактически - это обман пользователя. Например, на том же пикабу я ставлю лайк посту, после открываю его - и вижу, что моего лайка там нет. То ли та самая очередь еще не подошла, то ли вообще лайк не проставился - не знаю. Я хотел бы понимать, что результат моего действия есть, даже если, как на хабре, после клика на upvote я на секунду увижу спиннер.

    Хотя, надо признать, я не самый типичный пользователь соцсетей - большинству, наверно, предпочтительнее визуально быстрый сайт, но обманывающий их...

    Ах, обмануть меня не трудно!..
    Я сам обманываться рад!


    1. git-merge Автор
      08.10.2021 13:59

      Кстати именно у пикабу этой фичи нет. Кликаешь лайк - пикабу получает "сеть недоступна" и молча это проглатывает.

      Была бы очередь - то воркер бы повторял отправку лайка до тех пор пока у него бы не получилось.


      1. Alexandroppolus
        09.10.2021 10:10

        Была бы очередь - то воркер бы повторял отправку лайка до тех пор пока у него бы не получилось.

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


  1. noodles
    09.10.2021 19:48

    если со связью проблема, то обработчик очереди выполнит задачу тогда, когда проблемы уйдут (через несколько секунд, минут). Возможно, даже в другой день.

    А если у меня что-то в реальном мире завязано на эту задачу? т.е., например я уверен что задача выполнилась успешно, и звоню кому-то что-то подтвердить, или делаю другую важную активность в реальном мире на основании того что я уверен что моя задача выполнена успешно. А она оказывается не выполнена, она оказывается в очереди!). Так значит об этом и надо честно сказать.

    Поставив лайк, пользователь должен видеть результат сразу. Даже если этот лайк физически проставится позднее. 

    я как пользователь предпочитаю увидеть правду, т.е. в данном случае что есть проблемы с сетью или с сервером.

    Имхо, все эти offline-first, оптимистичный ux, pwa - зло, которое усложняет жизнь как разработчикам так и пользователям.
    Нет интерента - так покажи ты правду, что мол "чувак, сейчас нет интернета, или решай сначала эту проблему, или займись пока чем-то другим полезным".


    1. git-merge Автор
      09.10.2021 20:05

      А если у меня что-то в реальном мире завязано на эту задачу?

      то Вам эта технология не подойдёт


  1. wataru
    11.10.2021 13:14
    +1

    Термин вы откуда взяли? Вообще, персистентные очерди — это немного другое (особенно в контексте тега "алгоритмы").


    У вас тут скорее просто сохраниение стейта в персистентном хранилище. То, что вы храните там очереди, ничем принципиально не отличается от хранения там каких-то пользовательских настроек.


    1. git-merge Автор
      11.10.2021 17:56

      Из мира БД.
      in-memory vs персистентные

      Если рассматривать понятие "персистентный" как требующее вечное существование однажды сохранённого значения, то термин конечно будет некорректным.

      Цитата оттуда же

      Уровни персистентности

      Есть несколько уровней персистентности:

      - частичная (англ. partial),

      - полная (англ. full),

      - конфлюэнтная (англ. confluent),

      - функциональная (англ. functional).

      В частично персистентных структурах данных к каждой версии можно делать запросы, но изменять можно только последнюю версию структуры данных.

      Очередь с частичной персистентностью тоже может называться персистентной (почему нет?).

      В этом смысле персистентная очередь - та, которая сохраняет состояние на диске, в постоянном не зависящем от работы текущего приложения месте. Например на диске.

      Но для того чтобы оставаться персистентной - ей не нужно хранить всё вечно. Достаточно реализовать частичную персистентность.


      1. wataru
        11.10.2021 18:37

        В этом смысле персистентная очередь — та, которая сохраняет состояние на диске, в постоянном не зависящем от работы текущего приложения месте.

        Нет. Сама очередь, как структура данных, не обладает какими-то свойстами. Она хранится в персистентном хранилище, да. Но это свойство хранилища (памяти), а не очереди.


        В связи с этим предлагаю вам переименовать статью на "Очередь в персистентном хранилище на фронтенде".


        Edit: вы же не будете называть таблицу, допустим, пользователей в персистентной DB — персистентным списком пользователей?


        1. git-merge Автор
          11.10.2021 18:58

          Но SQS, Rabbit итп очередями называют. Несмотря на то, что это не структуры данных.

          вы же не будете называть таблицу, допустим, пользователей в персистентной DB — персистентным списком пользователей?

          Почему нет?

          Если эта таблица сохраняется в персистентном хранилище и переживает всё что происходит с приложением, то для приложения это будет персистентный список пользователей.

          Да, возможно поддерживающий частичную, а не полную персистентность. Но именно так.


          1. wataru
            11.10.2021 19:50
            +1

            Но SQS, Rabbit итп очередями называют.

            Поскольку эти продукты реализуют именно очередь. В них невозможно/сложно хранить что-то другое.


            Почему нет?

            Потому что сам список — никакой не персистентный, а просто список?


            Да, возможно поддерживающий частичную, а не полную персистентность. Но именно так.

            Я же не придераюсь к частичной или полной персистентности. Просто термин "персистентная очередь" уже занят и он как раз про свойство очереди. А у вас свойство хранилища, в которое очередь засунута. Но сама очередь от этого не становится какой-то другой.


            1. git-merge Автор
              11.10.2021 20:02

              Поскольку эти продукты реализуют именно очередь. В них невозможно/сложно хранить что-то другое.

              Чем SQS, реализующая "именно очередь", отличается от класса в JS, реализующего "именно очередь"?

              Наличием урлов конфигурации?

              Потому что сам список — никакой не персистентный, а просто список?

              • Список сохраняется в независимой (от приложения) памяти? Да

              • Список переживает перезапуск приложения? Да

              Почему список нельзя назвать персистентным? Частичная персистентность? Да. А полной в природе не встречается, ибо полная персистентность - понятие идеализированное. Рано или поздно хранилище будет удалено и данные из него потеряются. С этого момента нельзя будет запросить данные.

              Я же не придераюсь к частичной или полной персистентности. Просто термин "персистентная очередь" уже занят и он как раз про свойство очереди.

              Я бы не рассматривал данный источник как место где застолбили название для строго одного явления.

              Мало того понятия "синонимы", "одни и те же термины на разных уровнях" никто не отменял (Подробнее см. в книге Богданова "Тектология или всеобщая организационная наука").

              Когда мы говорим о структурах данных - персистентная очередь это одно.

              Когда мы говорим о коде или сервисе, реализующем очередь , то персистентная очередь - это другое.

              Когда мы говорим об очереди людей в поликлинику, то эта очередь снова будет персистентной, если они взяли талончики заранее. И не будет персистентной, если очередь т.н. "живая".

              В данной статье речь идёт о технологии, как паттерне построения фронтендов/мобильных приложений. "Хозяйке на заметку": Применили просто очередь - смогли не делать параллельные запросы на сервер. Пиковая нагрузка на нём упала.
              Применили очередь с памятью на диске - смогли сразу показать пользователю некоторые вещи как "уже сделано", хотя оно ещё может быт и не сделано.