Skip to content

Conversation

@Alienjob
Copy link
Contributor

@Alienjob Alienjob commented Dec 27, 2023

Resolves (#444)

Synopsis

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

Solution

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

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

Checklist

  • Created PR:
    • In draft mode
    • Name contains issue reference
    • Has type and k:: labels applied
  • Before review:
    • Documentation is updated (if required)
    • Tests are updated (if required)
    • Changes conform code style
    • CHANGELOG entry is added (if required)
    • FCM (final commit message) is posted or updated
    • Draft mode is removed
  • Review is completed and changes are approved
    • FCM (final commit message) is approved
  • Before merge:
    • Milestone is set
    • PR's name and description are correct and up-to-date
    • All temporary labels are removed

@Alienjob Alienjob added enhancement Improvement of existing features or bugfix k::UI/UX UI (user interface) and UX (user experience) changes labels Dec 27, 2023
@Alienjob Alienjob force-pushed the 444-track-displace-client-events branch from 09b2405 to 35b2b8b Compare December 28, 2023 10:08
- rename EventsPool to OptimisticEventsPoolService and move to domain/service/optimistic_event_pool.dart
- add event pool to prorerties and constructors of ChatRepository, MyUserRepository, HiveRxChat
- update dependency injection in routes and tests
@Alienjob
Copy link
Contributor Author

@SleepySquash
Добавлен сервис OptimisticEventsPoolService
он предоставляет единый алгоритм обработки оптимистичных событий
Когда пользователь добавляет чат в избранные,

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

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

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

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

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

Вся логика сосредоточена в классе OptimisticEventsPoolService, для подключения события к этому механизму

  1. В месте, где клиент обрабатывает событие, обращение к серверу оборачивается в PoolEntry.
  2. В месте, где происходит определение разбор событий от сервера добавляется проверка на игнорирование события, одна на все типы событий.

@SleepySquash
Copy link
Contributor

@Alienjob, во-первых, это 100% не сервис, потому что разве это бизнес-логика? В доменной области "мессенджер" нет понятия "сервис событий", это не относится к сервисам. Это деталь реализации стора, только стор об этом и знает. Соответственно, такая структура будет утилитой, но не сервисом. Утилита может создаваться в каждом сторе, в неё будут кормить сторы свои события и действия для последовательной обработки, которую Вы описали.

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

@SleepySquash
Copy link
Contributor

@Alienjob, т.е. у меня пока претензия к реализации - мне кажется, что она излишне сложная. Но не буду утверждать, что можно проще, буду только просить Вас, пожалуйста, попробовать её упростить максимально сильно.

@Alienjob
Copy link
Contributor Author

Alienjob commented Jan 2, 2024

FCM

Implement tracking collapsable client events (#768, #444)

- implement EventPool for collapsable events
- add convertation from [MyUserEvent] to [PoolEntry] for [muteChatsToggled] type
- add convertation from [ChatEvent] to [PoolEntry] for [chatFavoriteToggled] type

@Alienjob Alienjob marked this pull request as ready for review January 2, 2024 09:06
@Alienjob
Copy link
Contributor Author

Alienjob commented Jan 2, 2024

@SleepySquash Постарался максимально упростить реализацию.

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

debounce может заменить механизм отмены, при этом усложнив его - понадобится дополнительно хранить последнее отправленное значение. Это запретило бы спам на сервер, но данный ПР не пытается решить проблему спама. Он уменьшает спам за счет последовательной обработки однотипных запросов и механизма отмены событий.

Можно было бы дополнительно упростить реализацию если

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

@SleepySquash SleepySquash modified the milestones: 0.6.4, 0.7.0, 0.6.5 Sep 29, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.6, 0.7.0 Oct 13, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.7, 0.7.0, 0.6.8 Oct 29, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.9, 0.7.0, 0.6.10 Nov 11, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.11, 0.7.0 Nov 24, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.12, 0.7.0 Dec 1, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.13, 0.7.0 Dec 8, 2025
@SleepySquash SleepySquash modified the milestones: 0.6.14, 0.7.0, 0.8.0 Dec 19, 2025
@SleepySquash SleepySquash modified the milestones: 0.7.1, 0.8.0, 0.7.2 Jan 7, 2026
@SleepySquash SleepySquash modified the milestones: 0.8.0, 0.9.0 Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement of existing features or bugfix k::UI/UX UI (user interface) and UX (user experience) changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants