Хеширование паролей — одна из самых ресурсоёмких операций в серверной части современных приложений. Алгоритмы вроде bcrypt, scrypt или Argon2 специально замедлены, чтобы усложнить подбор паролей, но это приводит к обратной стороне: при росте числа одновременных регистраций и авторизаций система начинает испытывать нагрузку.
В Node.js это особенно заметно, поскольку криптографические операции, даже если они реализованы через нативные биндинги, способны блокировать event loop или занимать значительное время выполнения в пуле потоков libuv. В результате падает пропускная способность API, увеличивается latency, а при пиковых нагрузках возможны отказы в обслуживании.
Библиотека Password-hash в типичных сценариях используется как обёртка над выбранным алгоритмом хеширования, но сама по себе она не решает проблему масштабирования. Решение возникает на уровне архитектуры — через внедрение очередей задач.
При малых нагрузках простая модель выглядит так:
Однако при росте количества запросов возникают проблемы:
Ключевая проблема заключается в отсутствии буферизации запросов. Система пытается обработать всё сразу, вместо того чтобы распределить нагрузку во времени.
Очередь задач вводит промежуточный слой, который отделяет приём запросов от их фактической обработки.
Общая модель:
Такой подход позволяет контролировать поток операций и предотвращает перегрузку.
Базовая реализация может быть построена прямо в приложении:
const queue = [];
function enqueue(task) {
queue.push(task);
}
async function worker() {
while (true) {
const task = queue.shift();
if (!task) {
await sleep(50);
continue;
}
await processTask(task);
}
}
Преимущества:
Недостатки:
Такая модель подходит только для прототипов или систем с низкой критичностью данных.
Для production-систем используется отдельный слой очередей, например Redis-based или message broker решения. Общая идея:
Пример логики с использованием условного брокера:
await queue.add("hash-password", {
userId,
password
});
Воркер:
queue.process("hash-password", async (job) => {
const { userId, password } = job.data;
const hash = await PasswordHash.hash(password);
await saveHash(userId, hash);
});
При высокой нагрузке важно не только ставить задачи в очередь, но и контролировать её рост. Без этого очередь превращается в «чёрную дыру», где задержки становятся непредсказуемыми.
Основные механизмы:
1. Ограничение скорости добавления задач (rate limiting) API может ограничивать количество регистраций в единицу времени.
2. Ограничение параллельных воркеров Количество одновременно выполняемых хеширований регулируется:
3. Backpressure Если очередь переполнена:
Не все задачи хеширования равны по важности. Например:
Очереди позволяют разделять потоки:
Это предотвращает ситуацию, когда массовые фоновые операции блокируют регистрацию новых пользователей.
При очень высокой нагрузке эффективным становится группирование задач:
Это уменьшает накладные расходы на переключение контекста и I/O операции.
Очереди позволяют горизонтально масштабировать обработку:
Каждый воркер забирает задачи из общей очереди, что автоматически распределяет нагрузку.
Важно учитывать:
При работе с хешированием возможны сбои:
Очередь должна поддерживать retry-механику:
Пример логики:
queue.process(async (job) => {
try {
return await PasswordHash.hash(job.data.password);
} catch (err) {
throw err;
}
});
При повторной обработке задачи важно, чтобы результат не дублировался. Для этого применяются:
Без идемпотентности повторная обработка может привести к конфликтам данных.
При использовании очередей важно отслеживать:
Эти данные позволяют понять, когда система приближается к пределу пропускной способности.
Типовой поток обработки пароля через очередь выглядит следующим образом:
API получает пароль
создаётся задача:
queue.add("hash-password", { userId, password });воркер вызывает:
PasswordHash.hash(password)результат сохраняется в БД
задача помечается как выполненная
Такой подход позволяет отделить криптографически тяжёлую операцию от слоя HTTP-запросов и добиться стабильной работы даже при резких всплесках нагрузки.
В некоторых системах используется комбинированный подход:
Определение режима может зависеть от:
Это позволяет сохранять низкую задержку в нормальных условиях и устойчивость в пиковых.
На практике часто встречаются проблемы:
Эти ошибки приводят к тому, что очередь перестаёт быть инструментом стабилизации и становится источником задержек.
При построении системы хеширования паролей через очереди важны следующие принципы: