DoS-mongoose/mongodb экземпляр узла JS
У нас есть экземпляр nodeJS внутри AWS VPC, который делает запрос к экземпляру mongodb, работающему в другой зоне доступности.
Экземпляр узла сильно поражен определенным запросом. Этот запрос используется для получения большого количества информации из экземпляра mongodb. После первого запроса эта запись кэшируется в течение определенного периода времени.
Вчера что-то случилось с количеством данных, которые он возвращал. С кодом вроде:
console.log('before retrieve');
Model.find({}).exec(function() {
console.log('after retrieve');
});
Если вы нажмете "перед извлечением" 10 раз, а затем просто остановитесь, выполняя саму задачу. Я удалил некоторые данные, которые он извлекал как временное исправление.
На стороне mongoDB я иногда вижу:
SocketException handling request, closing client connection: 9001 socket exception [SEND_ERROR]
Как я могу избежать этого?
1 ответ
Проблема, которую вы описываете, имеет гораздо меньшее отношение к мангусте, mongodb и узлу и является скорее проявлением проблемы, обычно называемой "бегом по кэшу" или "собачьей кучей".
Как следует из названия, паническое бегство в кеше происходит, когда целая куча вещей пытается обновить кеш сразу. В вашем случае это происходит, когда срок действия кеша истекает или при начальной загрузке данных в кеш. Внезапно приходит много запросов, и на вашу базу данных накладывается большая нагрузка чтения, что 1) приводит к медленному обновлению кеша и 2) вызывает еще большее количество запросов в ожидании кеша. Это в основном приводит к поведению, которое вы видели, когда вещи просто терпят крах
На этой странице Википедии достаточно ясно описана проблема, и как ее можно решить, используя отдельный процесс или блокировки. Поскольку узел не имеет блокировок или потоков, это, вероятно, не является решением. Кроме того, хотя отдельный процесс будет работать, он намного сложнее.
Одна из техник, которую я использовал в прошлом, - это использование двух ключей кэша с истекающим сроком действия: один ключ используется только для указания того, когда должен быть обновлен кэш, а другой содержит фактические данные.
Для иллюстрации предположим, что у меня есть объект foo
что я хочу кешировать и истекает каждый час. Я могу создать другой ключ foo_refresh
что я истекаю за 1 минуту до foo
ключ.
Когда foo_refresh
ключ истекает, один работник / запрос немедленно заменяет foo_refresh
ключ и игнорирует кэш и вместо этого извлекает данные из базы данных, обновляя foo
ключ по окончании (также сбрасывает время истечения). Используя такой механизм, мы получаем своего рода "блокировку" при обновлении кэша, а это означает, что не более одного работника будет когда-либо делать дорогостоящее чтение.
Предполагая, что обновление кэша занимает менее 1 минуты, foo
объект никогда не истекает, вместо этого он обновляется по истечении foo_refresh
ключ.
Надеюсь, это поможет!