Войти Регистрация
Код-ревью

36. Счётчик непрочитанных в мессенджере VK

Средняя

JavaScript · 47 строк

Три роута: список 50 последних диалогов с числом непрочитанных, отметка «прочитано до сообщения N» и общий счётчик для красной цифры на иконке. Счётчик кэшируется в Redis.

База — PostgreSQL через pg, Redis — клиент redis v4 с промисами. Групповые чаты бывают до 500 участников. Отметьте строки с ошибками.

В работе: Красная цифра на вкладке «Сообщения» и список диалогов в веб-версии.

Как проверяем Нажмите на строку с ошибкой и коротко напишите, что не так. Баг засчитан, если отмечена хотя бы одна его строка. Зачёт — найдено не меньше 60 % багов с учётом серьёзности (критичный весит 3, обычный 2, мелкий 1), а лишних отметок не больше, чем багов в коде. Пустые строки, скобки и комментарии лишними не считаем. Ответ один на подход: после него показываем разбор.
Node.jsExpressPostgreSQLRedisasync/awaitN+1

Источник: наша задача на данных VK: диалоги и непрочитанные сообщения. Условие пересказано своими словами.

  1. const express = require("express");
  2. const { pool } = require("./db");
  3. const { redis } = require("./cache");
  4. const router = express.Router();
  5. router.get("/api/dialogs", async (req, res) => {
  6. const userId = req.session.userId;
  7. const { rows: dialogs } = await pool.query(
  8. "SELECT d.id, d.title, d.peer_id FROM dialogs d " +
  9. "JOIN dialog_members m ON m.dialog_id = d.id WHERE m.user_id = $1 " +
  10. "ORDER BY d.last_message_at DESC LIMIT 50",
  11. [userId]
  12. );
  13. for (const d of dialogs) {
  14. const { rows } = await pool.query(
  15. "SELECT count(*) FROM messages WHERE dialog_id = $1 AND id > " +
  16. "(SELECT last_read_id FROM dialog_members WHERE dialog_id = $1 AND user_id = $2)",
  17. [d.id, userId]
  18. );
  19. d.unread = rows[0].count;
  20. }
  21. const total = dialogs.reduce((sum, d) => sum + d.unread, 0);
  22. redis.set(`unread:${userId}`, total);
  23. res.json({ dialogs, total });
  24. });
  25. router.post("/api/dialogs/:id/read", async (req, res) => {
  26. const { lastMessageId } = req.body;
  27. await pool.query(
  28. "UPDATE dialog_members SET last_read_id = $1 WHERE dialog_id = $2",
  29. [lastMessageId, req.params.id]
  30. );
  31. redis.del(`unread:${req.session.userId}`);
  32. res.sendStatus(204);
  33. });
  34. router.get("/api/unread", async (req, res) => {
  35. const key = `unread:${req.query.userId}`;
  36. const cached = redis.get(key);
  37. if (cached) {
  38. return res.json({ total: cached });
  39. }
  40. res.json({ total: 0 });
  41. });
  42. module.exports = router;