Backend Matrix
описание
Ссылка: https://t.me/+FeuZC0lOM5wwNjI6 По всем вопросам: @Shustov_d
2 648
подписчиков
Охват к подписчикам
11,4%
ERR
Реакции к просмотрам
0,02%
1 на 20 постов
Пересылки к просмотрам
1,02%
62
Постов в день
0,0
всего 20
Где отзываются чаще
доля реакций к просмотрам- 24 июн.⚡️ Встречайте ty — убийцу Mypy Команда Astral решила окончательно переписать всю экосистему Python на Rust. Встречайте их новый амбициозный проект — ty, ультрабыстрый тайп-чекер, который призван отправить старый добрый mypy на пенсию. Если вы пишете на Python с жесткой типизацией в больших проектах, то знаете, что mypy или pyright на тысячах строк кода могут задуматься на десятки секунд. ty обещает космическую скорость, совместимость и интеграцию. uv tool install ty ty check src/ #python #astral #ty #backend #opensource0,26%
- 6 авг.В чем разница между P95, P99 и Average Latency? «Наш сервис показывает среднее время ответа (Average Latency) 50 мс. Всё отлично?» Правильный ответ: Нет, среднее значение в мониторинге — это иллюзия. 📊 Почему Average врет: Если из 100 пользователей 99 получили ответ за 10 мс, а 1 завис на 10 секунд, среднее время составит ~109 мс. Статистика выглядит неплохо, но 1% ваших пользователей просто уходит к конкурентам. 💡 Мониторьте минимум P95 и P99 в Prometheus/Grafana.0,00%
- 5 авг.Почему архитектура Event-Driven не всегда спасает (и когда лучше вернуть REST) Event-Driven Architecture (EDA) на Kafka или RabbitMQ стала стандартом де-факто для распределенных систем. Но в 2026 году всё больше команд совершают ретракт к классическому REST. Почему? 1️⃣ Distributed Tracing — ад: Отследить путь запроса через 15 топиков Kafka, когда что-то упало на третьем шаге, требует колоссальных затрат. 2️⃣ Eventually Consistency (Согласованность в конечном счете): Когда интерфейс говорит юзеру «Оплачено», а сервис списывания денег еще в очереди, UX страдает первым. 3️⃣ Скрытая связность: Изменение структуры одного события в Pub/Sub может незаметно сломать 3 потребителя, о которых вы даже не догадывались. 💡 Правило: Используйте Синхронное взаимодействие (gRPC/HTTP) для critical-path операций (авторизация, платежи, проверка остатков) и Асинхронное (Events) — строго для побочных эффектов (отправка email, аналитика, генерация отчетов).0,00%
- 27 июл.🚀 Оптимизация: ujson / orjson вместо стандартного json В бэкенде сериализация и десериализация JSON при высоком RPS может съедать до 30-40% времени обработки запроса. Стандартный модуль json написан на чистом Python и работает медленно. import orjson # Быстрая библиотека на Rust data = {"id": 101, "status": "success", "items": list(range(1000))} # Быстрая сериализация в байты: json_bytes = orjson.dumps(data) # Быстрый парсинг из байтов: parsed_data = orjson.loads(json_bytes)0,00%
- 27 июл.🗄 SQL: Как избежать трагедии с N+1 запросами в ORM Главный грех бэкендера при работе с ORM - случайно сгенерировать сотни запросов к БД в обычном цикле. # ❌ ПРОБЛЕМА (1 запрос за постами + N запросов за авторами): posts = session.query(Post).all() for post in posts: print(post.author.name) # Каждая итерация делала отдельный SELECT! # ✅ РЕШЕНИЕ (1 запрос с JOIN): from sqlalchemy.orm import joinedload posts = session.query(Post).options(joinedload(Post.author)).all() for post in posts: print(post.author.name) # Данные уже загружены заранее!0,00%
- 26 июл.⚡️ Трюк: asyncio.gather vs asyncio.TaskGroup (Python 3.11+) Если ты до сих пор используешь asyncio.gather для параллельного запуска асинхронных тасок, пора обновляться. В Python 3.11 появились TaskGroup import asyncio async def fetch_user(user_id: int): ... async def fetch_orders(user_id: int): ... # ❌ Старый подход (при ошибке в одной таске остальные продолжают работать): # users, orders = await asyncio.gather(fetch_user(1), fetch_orders(1)) # ✅ Современный подход через TaskGroup: async def main(): async with asyncio.TaskGroup() as tg: task1 = tg.create_task(fetch_user(1)) task2 = tg.create_task(fetch_orders(1)) # Сюда код попадет только когда ОБЕ таски завершатся user = task1.result() orders = task2.result() Если одна таска падает с ошибкой, TaskGroup автоматически отменяет все остальные таски в группе и выбрасывает ExceptionGroup0,00%
- 26 июл.🛡 Как защитить REST API от двойных списаний и дублей Сеть нестабильна. Клиент отправляет POST /payments, списание происходит, но ответ падает по таймауту. Пользователь нажимает «Оплатить» еще раз. Без обработки идемпотентности вы спишете деньги дважды. Стандартное решение в бэкенд-архитектуре — Idempotency-Key в заголовках: sequenceDiagram Client->>Backend: POST /pay (Header: Idempotency-Key: uuid-123) Backend->>Redis: SETNX idempotency:uuid-123 "processing" Backend->>DB: Выполняем платеж Backend->>Redis: Сохраняем ответ сервера в Кэш Backend->>Client: 200 OK (Payment success)0,00%
- 25 июл.🚀 Почему OFFSET убивает твой бэкенд и чем его заменить Классическая пагинация LIMIT 20 OFFSET 100000 — это тихий убийца производительности. Чтобы вернуть 20 записей с 100-й страницы, СУБД вынуждена прочитать и отбросить 100 000 предшествующих строк. Чем дальше листает пользователь, тем медленнее отвечает API. 💡 Решение: Keyset (Cursor-based) пагинация -- ❌ Плохо (O(N) по времени): SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 20 OFFSET 100000; -- ✅ Быстро (O(1) по индексу): SELECT id, title FROM posts WHERE id < :last_seen_id ORDER BY id DESC LIMIT 20;0,00%
- 25 июл.⚡️ Пишем свою легкую очередь задач прямо в PostgreSQL Когда вам нужно обрабатывать фоновые задачи (например, отправку писем или обработку платежей) несколькими воркерами, возникает проблема race condition: два воркера могут взять одну и ту же запись из БД. Вместо сложной блокировки всей таблицы используйте SKIP LOCKED: -- Каждый воркер выполняет этот запрос: BEGIN; SELECT id, payload FROM task_queue WHERE status = 'pending' ORDER BY created_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED; -- 👈 Вся магия здесь -- Обновляем статус найденной задачи UPDATE task_queue SET status = 'processing' WHERE id = :found_id; COMMIT;0,00%
- 15 июл.HTTP-код 429 Too Many Requests Если ваш API ничем не защищен, любой скрипт-парсер или злоумышленник может положить ваш сервер. Для защиты используется Rate Limiting Когда клиент превышает лимит (например, более 60 запросов в минуту), сервер должен вернуть статус 429 Too Many Requests. Но хорошим тоном считается не просто вернуть ошибку, а подсказать клиенту, когда можно повторить попытку, с помощью заголовка Retry-After. HTTP/1.1 429 Too Many Requests Content-Type: application/json Retry-After: 30 { "error": "Too Many Requests", "message": "Вы превысили лимит запросов. Пожалуйста, подождите 30 секунд." }0,00%
- 15 июл.Зачем нужен SELECT FOR UPDATE и как он спасает от багов Представьте: два пользователя одновременно нажимают кнопку «Купить» для последнего товара на складе. Оба процесса параллельно читают остаток из БД (он равен 1), оба видят, что товар есть, и оба успешно проводят списание. В итоге — у вас продано два товара, хотя физически был один. Чтобы этого избежать, при чтении строки используйте блокировку FOR UPDATE. Она заставит вторую транзакцию ждать, пока первая не завершит свою работу. -- Транзакция 1 блокирует строку с товаром BEGIN; SELECT quantity FROM products WHERE id = 42 FOR UPDATE; -- (БД возвращает 1, строка заблокирована для других изменений) UPDATE products SET quantity = 0 WHERE id = 42; COMMIT; -- Блокировка снимается0,00%
- 10 июл.⚠️ Главный убийца производительности: Проблема N+1 запросов Классическая ловушка при работе с ORM (Django ORM, SQLAlchemy, Hibernate, Sequelize). Это ситуация, когда вместо одного умного запроса к базе данных твой бэкенд делает сотни мелких. Представь, что нужно вывести 10 постов и имена их авторов. ❌ Как ORM делает по умолчанию (Проблема N+1): # 1 запрос: получает 10 постов posts = Post.objects.all() for post in posts: # Еще 10 отдельных запросов: по одному на каждого автора! print(post.title, post.author.name) 1 запрос для постов + 10 для авторов = 11 запросов к базе. А если постов будет 1000? ⚡ Как это чинится Мы сразу говорим базе вытащить постов вместе с авторами через JOIN. # Всего 1 ленивый запрос, который сразу соберет все данные posts = Post.objects.select_related('author').all() for post in posts: print(post.title, post.author.name) # Данные уже в памяти, запросов к базе нет!0,00%