Nalik
А понял
Nalik
Да как вариант
Nalik
Я просто пытаюсь исключить на будущее потенциально узкие и ресурсоемкие места и сразу сделать все правильно 😄
Алексей
Как ты себе это представляешь, когда тысячи юзеров?
в твоем видении мира клиент может подключиться с двух разных устройств и на обоих не видеть этого удаленного сообщения?
Nalik
навелосипедить это всегда пожалуйста)
Nick
Как ты себе это представляешь, когда тысячи юзеров?
я просто считаю, что тыщи юзеров как правило ни у кого не бывает, а тем более в одном чате. А у кого бывает они проектируют решения по другому. Ну и такие операции разовые при подгрузке событий и нагрузки не дают особо, а остальное реалтаймово на событиях и не приводит к доп запросам.
Nalik
в твоем видении мира клиент может подключиться с двух разных устройств и на обоих не видеть этого удаленного сообщения?
Это уже как реализовать, можно просто помечать сообщение удаленным у одного клиента на разных устройствах, можно не выдавать этого сообщения в целом. Вопрос в целом был не об этом
Nick
Только в этом чате 2872 человека
вы делаете телеграм и у вас больше 100М юзеров?)
Алексей
любой мессенджер начинается ровно с 1 юзера
Nalik
Я думаю если что-то делать, то нужно заранее предусмотреть и по возможности исключить слабые места)
Nick
Я думаю если что-то делать, то нужно заранее предусмотреть и по возможности исключить слабые места)
самое слабое место - наличие опыта у проектировщика решения. Если опыта нет, то заранее не предусмотришь, т.к. ситуаций множество и проблемы стреляют не из таких очевидных мест как кажется на первый взгляд. Поэтому надо просто сразу быть готовым все выкинуть через полгодика-год пром эксплуатации и переписать под конкретные потребности
Nalik
Окей, другой вопрос. Так же по архитектуре. Как сделать подсчет непрочитанных сообщений в чатах (как это реализовано допустим в том же Telegram). Допустим есть коллекции: chats_list - Здесь хранится информация обо всех чатах. chats_user - Здесь хранится информация о чатах в которых состоит пользователь messages_list - Здесь хранится информация обо всех сообщениях в чатах Сейчас у меня есть такая идея 1. В chats_list хранить ID последнего сообщения чата. 2. В chats_user хранить ID последнего прочитанного сообщения чата. 3. Делать выборку из messages_list сообщений созданных после "ID последнего сообщения чата" Но этот способ слишком ресурсоемкий, особенно когда много сообщений в коллекции messages_list
Алексей
после первого транша от инвестора!
Nalik
Есть и другая идея: Можно добавлять локальный msg_local_id сообщения в таблицу messages_list, в таблице chats_list запоминать msg_last_local_id последнего локального сообщения (он же будет идентификатором сколько сообщений всего) и при добавлении нового сообщения этот счетчик будет увеличиваться по типу autoincrement, в таблицу chats_user заносить msg_read_local_id последнего прочитанного сообщения. И когда нужно будет посчитать количество непрочитанных сообщений просто отнимаем от msg_last_local_id значение из msg_read_local_id. Такой запрос не будет потреблять ресурсы.
Nalik
Но мне кажется муторная схема
Nalik
внешний кэш со счетчиком, который на потоках инкрементится. можно без кэша прост ов коллекции
Можно и просто добавить поле для чата юзера с уведомлением и при приходе нового сообщения увеличивать его ) Но опять же, если 1000 юзеров, то нужно будет обновить 1000 записей при приходе нового сообщения)
Алексей
Но мне кажется муторная схема
позвольте поинтересоваться -вы на клиента какую то логику возлагаете или все должно на бекенде и монге решаться?
Nalik
позвольте поинтересоваться -вы на клиента какую то логику возлагаете или все должно на бекенде и монге решаться?
на бэке будет своя логика, на клиенте своя) в двух вопросах которые я задал логика заложена в бэк
Алексей
ну хотя бы тема оформления не будет в коллекции храниться?))
Алексей
а на чем бек пишете?
Nalik
Не, вообще мне кажется это очевидно должно вычисляться на бэке. Взять хотя бы API VK https://dev.vk.com/ru/reference/objects/conversation
Nalik
значение unread_count
Nick
Можно и просто добавить поле для чата юзера с уведомлением и при приходе нового сообщения увеличивать его ) Но опять же, если 1000 юзеров, то нужно будет обновить 1000 записей при приходе нового сообщения)
да, собственно поэтому и предлагал кэш. Для долгосрока да можно прихранить гдето ид последнего прочитанного в отложенном режиме, чтобы при включении клиента ему разово подгрузить список и прогреть кэш, а потом все на событиях и кэше
Nalik
Да определенно данные будут кэшироваться, вся возможная логика так же будет разгружена на клиенте, чтобы не нагружать сервер. Но сервер тем не менее должен знать все данные так или иначе чтобы иметь возможность разово отправить все данные на клиент не сильно потея. Вот сейчас я войду с нового устройства в телегу и она должна будет отправить мне все данные, а на это уйдет куда больше вычислительных ресурсов, чем при входе с устройства которым я постоянно пользуюсь.
Nalik
Как минимум телега должна будет мне отправить список чатов в которых я состою с доп.ифнормацией типа последнего сообщения и счетчика непрочитанных сообщений)
Nalik
@yatoba кстати по поводу вчерашней темы. group не подходит потому, что я сначала выбираю список своих чатов. await chats_user.aggregate([ { $match: { user_id: 1 } }, { $group: { _id: "$chat_id", count: { $sum: 1 } } }, ]); Если бы мне нужно было подсчитать инфу во всех чатах или в каких-то конкретных, то все было бы ок. await chats_user.aggregate([ { $group: { _id: "$chat_id", count: { $sum: 1 } } }, ]);
Nick
Да все есть
значит $match: {chat_id: $in:[]} $group: ....
Nalik
Но это уже получается 2 запроса, я не вижу ничего плохого в этом. Но мне казалось, можно как-то Join-ом решить и это было бы удобно) Про это знаю const selectedChatIds = await chats_user.distinct("chat_id", { user_id: 1, leave_time: 0 }); await chats_user.aggregate([ { $match: { chat_id: { $in: selectedChatIds } } }, { $group: { _id: "$chat_id", count: { $sum: 1 } } }, ]);
Nalik
Вообще не очень понятно, почему lookup count выполняется так долго { $lookup: { from: 'chats_user', let: { chatID: '$chat_id' }, pipeline: [ { $match: { $expr: { $and: [{ $eq: ['$chat_id', '$$chatID'] }, { $eq: ['$leave_time', 0] }] } } }, { $count: 'userCount' }, ], as: 'dbUserCount', }, }
Nalik
эксплейн что говорит?
Я если честно не знаю, что это
Nick
Я если честно не знаю, что это
https://www.mongodb.com/docs/manual/reference/method/db.collection.aggregate/#return-information-on-aggregation-pipeline-operation
Nick
чтоб посмотреть что делает база при выполнении запроса и куда уходит время надо еще передать параметр executionStats в экплейн
Nalik
{ explainVersion: '2', stages: [ { '$cursor': [Object], nReturned: 30, executionTimeMillisEstimate: 0 }, { '$lookup': [Object], totalDocsExamined: 300030, totalKeysExamined: 300030, collectionScans: 0, indexesUsed: [Array], nReturned: 30, executionTimeMillisEstimate: 542 },
Nalik
Ну что оно выполняется до*** 542 я и так знал
Nalik
и что затронуто 300к документов я тоже знал. Поскольку создал 10к пользователей. Создал 30 чатов и в каждый из них добавил эти 10к пользователей. Соответственно таблица с информацией о чатах пользователя содержит 300к записей
Nalik
Такой стресс-тест )
Nalik
Много :) Если не делать такого подсчета, то операция выполняется 20 мс.
Nalik
Если предположить, что одним запросом можно сделать выборку для 200 чатов время сразу кратно увеличиться. Если предположить, что пользователей будет больше, время кратно увеличиться. В общем нужно сразу проектировать так, чтобы таких проблем не было
Nalik
Я прихожу к мысли, что для этого не нужно ничего считать. По крайней мере более чем для 1 чата одновременно.
Nalik
раскройте полностью дерево и на https://pastila.nl/
https://pastila.nl/?000b885b/404d79944f99cd1e74713ad2bdde18fb#iS4XBykU15YGhzUGogW91Q==
Nalik
Может и не такая плохая идея делать updateMany при приходе нового сообщения. Делать асинхронно, чтобы не тормозить возврат. Все таки действительно не предполагается наличие такого большого количества пользователей..
yopp
относительно быстро считать можно используя covered query, но они не будут работать внутри $lookup из-за $expr
Nalik
Но это что касается количества непрочитанных сообщений. Для подсчета всех пользователей в чате можно так же создавать отдельное поле user_count где при вступлении в чат увеличивать счетчик. При выходе уменьшать..
Nalik
относительно быстро считать можно используя covered query, но они не будут работать внутри $lookup из-за $expr
Да я прихожу к мысли, что считать вообще не стоит ничего. Не правильная архитектура получается. Всегда есть узкие места. Нужно проектировать так, чтобы это всегда работало быстро вне зависимости от чего-либо.
Nalik
А если записей будет не 300к, а 300 миллионов.
yopp
если вы можете сделать счётчики, это конечно будет быстрее
yopp
А если записей будет не 300к, а 300 миллионов.
когда у вас будет 300 миллионов записей у вас будут совершенно другие проблемы 🙂
yopp
и вы их никогда не угадаете
Nalik
Да я все понимаю)
Nalik
Но зная о потенциальных слабых местах я не могу спокойно спать)
Nalik
О масштабировании это не сейчас)
yopp
если у вас нет того объёма данных о котором вы беспокоитесь, то у вас нет слабых мест 🙂 очень много проектов которые я вижу едут до огромных MRR не всегда имея даже индексы по очень горячим местам
yopp
и проблемы у них в совершенно не тех местах, которые тут обычно обсуждают как узкие 🙂
Nalik
если у вас нет того объёма данных о котором вы беспокоитесь, то у вас нет слабых мест 🙂 очень много проектов которые я вижу едут до огромных MRR не всегда имея даже индексы по очень горячим местам
Меня всегда это очень огорчало. Пусть у меня не много опыта, но смотря на некоторые популярные решения и их реализацию мне хочется плакать
Nalik
Хотя я понимаю, что скорее всего эти решения были сделаны за зарплату не с большой мотивацией)
Алексей
а может они были сделаны с мыслью что у вк апи реализовано так и значит очевидно что надо так? 😉
Nalik
а может они были сделаны с мыслью что у вк апи реализовано так и значит очевидно что надо так? 😉
А кто сказал, что я смотрю на ВК API? Я смотрел готовые кейсы и у ватсапа и телеги в том числе. И просто в интернете инфу как это реализовано у людей)
Nick
https://pastila.nl/?000b885b/404d79944f99cd1e74713ad2bdde18fb#iS4XBykU15YGhzUGogW91Q==
вот как раз то о чем @dd_bb говорит - 300к totalDocsExamined говорит что под лукапом индексы не задействуются
yopp
когда работаешь консультантом видишь что самый эффективный подход это «сносно делай, сносно будет» попытка оптимизировать проблему которой у вас нет, это попытка предсказать будущее. а человечество в предсказании будущего на сегодняшний день удивительно плохо
Nalik
Но в данном случае мое видение сходится с реализацией ВК АПи
yopp
по этому есть смысл тратить ресурсы там, где они мешают доставлять ценность конечному потребителю
yopp
и уже когда у вас будет понимание что текущее решение ну совсем уже никуда не едет и поддерживать его архисложно — делать новое решение с новой архитектурой
Nick
У меня totalDocsExamined все задействованы ведь
выше же писал что 300к всего, значит да все)
Nalik
Ну так там и видно: totalDocsExamined: 300030, totalKeysExamined: 300030,
Nalik
Подскажите мне по поводу количества непрочитанных сообщений в чате, как бы сделали это вы? наиболее удачным, хоть и компромиссным мне кажутся 2 решения: 1. Для каждого сообщения хранить локальный числовой ID относительно чата которому он принадлежит. И в итоге все сводится к простой арифметике: от последнего отнять непрочитанное (подробнее тут: https://t.me/mongodb_ru/132277 ) 2. асинхронный updateMany по полю unread_count при приходе нового сообщения :)
Nalik
Первый способ мне кажется надежнее. Но в разы муторнее.