TGViewer
Бессонный кодер Бессонный кодер @sleeplesscode · 4.24K subscribers
Post #513 2.03K
Порой меня спрашивают, какие баги в разработке максимально бывают мемные и глупые. Именно с таким багом я столкнулся и решил вам рассказать.

Для того чтобы отвечать вам за 30 миллисекунд, Имперский стражник кеширует некоторые данные в своей памяти, среди таких: базовые данные о пользователях и настройки чатов. И вот именно с кешированием чатов началась проблема. В последние пару дней вы могли заметить что после того как поменяли настройку чата, она применялась далеко не сразу.

Я начал искать почему это происходит. Для этого надо окунуться в логику настроек:
/**
* Обновляет значение параметра
*/
async updateSetting(ctx, param, value) {
const chat = await Database.instance.getChat(ctx.scene.state.chat);
const transformedValue = param.transform(value);

await Database.instance.updateChat(ctx.scene.state.chat, {
[param.id]: transformedValue
});
await this.saveAudit(ctx, param.id, transformedValue);
}

Вроде всё верно... А что в базе?
async updateChat(chat, data) {
const client = await this._pool.connect();
/** логика SQL запроса */
client.release();
await this._chatsCache.delete(chat);
}

И тут всё верно, в базе обновляется значение, после чего удаляется из кеша.
Я провёл эксперимент, обновил параметр и посмотрел в базу, значение новое и верное.

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

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

А кто в Стражнике отвечает за кэш? Правильно, я, вернее мой модуль rgcache. Давайте изучать проблему...
В библиотеке на текущий момент есть 3 вида кэша.
MemoryCache - хранит данные в оперативной памяти, работает полностью локально.
RedisCache - хранит данные в Redis, локально ничего не хранит и при каждом запросе делает запрос в Redis.
И их комбинация RedisSyncedMemoryCache - хранит данные в оперативной памяти, но если идёт инвалидация значения (оно обновилось), то через Redis посылает всем другим рабочим кешам задачу удалить у себя это значение.

Для хранения чатов как раз использовался RedisSyncedMemoryCache
Давайте посмотрим как он удаляет значения (для вас расписал построчно комментарии):
async delete(key) {
let entry = this._data.get(key); // Ищем значение локально
if (entry) { // Если значение найдено, то...
await this._options.preDestroy(key, entry.value); // Вызываем хук на удаление значения
this._data.delete(key); // Удаляем его
}
await this._pub.publish(this._name + ":invalidate", key); // Отправляет через Redis команду для всех остальных кешей инвалидировать это значение.
}

Хорошо, а как идёт инвалидация?
async _handleInvalidation(key) {
let entry = this._data.get(key); // Ищем значение локально
if (entry) { // Если значение найдено, то...
await this._options.preDestroy(key, entry.value); // Вызываем хук на удаление значения
this._data.delete(key); // Удаляем его
}
}

Вроде всё правильно, ошибок нет... А ПОТОМ СПУСТЯ 5 МИНУТ ПОДУМАТЬ Я ОСОЗНАЛ.

В качестве ключа чата я использую id чата, это отрицательное 64-битное число, число, ЧИСЛО. Так что происходило? Число становилось строкой! Ведь Redis при передаче данных через себя всё кастит в строку. И получалось что другие инстансы получали запрос на удаление строки... храня только числа. Вот так один тайпкаст в библиотеке заставил меня 2 дня перекапывать исходники стражника.

А увидеть код библиотеки и фикс вы можете даже сами, вот так, раз ошибся и ты ошибся.
https://github.com/RedGuys/rgcache/commit/ef610d4e6436a6b492e49b2c61a2cd8222ab2885

#downTime
  • ❤ 44
  • 👍 5
  • 🔥 4
  • 🤯 4
  • 🙊 2
  • 👾 1
More from @sleeplesscode
  1. Oct 5, 2026Post #923
  2. Oct 1, 2026Post #914
  3. Sep 18, 2026Post #913
  4. Sep 17, 2026Post #912
  5. Sep 15, 2026Post #911
  6. Sep 14, 2026Post #909
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →