Артём Шумейко

Привет, друзья! Меня зовут Артём Шумейко. Я Senior Backend разработчик и основатель школы Python разработки "Pytex". На этом канале я помогаю разработчикам улучшать хард скиллы и развивать карьеру. По моим видео ты станешь крепким разработчиком и начнешь зарабатывать больше. Подписывайся 🤟🏻

По рекламе только сюда: ads@pytex.school
Для учеников: Телеграм @artem_shumeiko_support или Почта support@pytex.school
Сайт школы: pytex.school

Включена Роскомнадзором в перечень персональных страниц www.gosuslugi.ru/snet/6776cf994e740947bef4eea7


Артём Шумейко

ПОСЛЕДНИЙ ДЕНЬ НАБОРА НА КУРС

Сегодня в 23:59 по МСК закрываю набор учеников на курс по параллелизму и конкурентности в Python.

Курс для тебя, если ты:
— Уже работаешь Python-разработчиком, но чувствуешь, что знания по async заканчиваются на async/await и gather

— Хочешь увереннее проходить собеседования на Middle/Senior позиции, где спрашивают event loop, GIL, потоки, процессы, очереди и продакшен-кейсы

— Работаешь с FastAPI, aiohttp или другими асинхронными технологиями и хочешь понимать, что происходит под капотом

— Хочешь разобраться, как строить высоконагруженные распределенные приложения, которые не тормозят и не падают

На курсе мы будем разбирать и чинить полноценный продакшен-проект сервиса доставки продуктов.

Проблемы, которые ты научишься решать:
1️⃣ Как обрабатывать timeout, retry, rate limit и нестабильные внешние сервисы
2️⃣ Как бороться с cache stampede и координировать несколько воркеров через Redis
3️⃣ Как безопасно обновлять токены и синхронизировать состояние между инстансами приложения
4️⃣ Как встраивать синхронный код и сторонние SDK в async-приложение через to_thread
5️⃣ Как строить фоновые процессы через TaskIQ/Celery и не терять данные
6️⃣ Как организовать batching через asyncio.Queue и снижать нагрузку на БД
7️⃣ Как обрабатывать большие потоки событий через Kafka, FastStream и WebSocket
8️⃣ Как реализовывать backpressure и не утонуть под нагрузкой
9️⃣ Как искать event loop lag, slow callbacks и другие проблемы производительности
🔟 Как строить нормальное логирование через contextvars и отслеживать запросы в асинхронном приложении
🔢… и еще многое другое

Главный принцип курса — максимум практики, приближенной к продакшену.

Я специально собирал программу вокруг реальных задач, которые встречаются в современных Python-проектах, чтобы после курса ты открывал вакансию и понимал что знаешь о чём там написано, не плыл на собесе когда спрашивают про корутины или батчинг аналитики, и мог куски кода утащить прямо в свой рабочий проект.

➡️ Последний день, чтобы ПОПАСТЬ НА КУРС [tinyurl.com/ycryt28p]

2 weeks ago | [YT] | 44

Артём Шумейко

👉 Новое видео: 5 мифов про асинхронность, в которые верят разработчики  

В этом видео разбираю популярные мифы об асинхронности в Python и показываю на примерах, почему async/await не является панацеей.

Поговорим о том:
- Почему await сам по себе не ускоряет выполнение функции
- Где на самом деле могут находиться узкие места приложения
- Почему последовательный await часто убивает производительность
- В каких случаях await может запускать код в отдельном потоке

2 weeks ago | [YT] | 7

Артём Шумейко

👉 Новое видео: Python Конкурентность за 30 минут — Полный гайд

Разобрал конкурентность и параллелизм в Python простым языком: что происходит при обработке запроса, как работают потоки, процессы и корутины, почему процессы могут выполняться действительно параллельно, а также в каких случаях стоит выбирать async/await, потоки или процессы.

После этого видео вы будете понимать различия между этими инструментами и сможете правильно выбирать подход под конкретную задачу

4 weeks ago | [YT] | 13

Артём Шумейко

Видеоразбор продакшен-задач по конкурентности

Мне кажется, одна из главных проблем при изучении async/конкурентности в Python: слишком мало нормальных продакшен-примеров.

Если вы пробовали изучать эту тему, наверняка знаете, что обычно всё ограничивается чем-то вроде asyncio.sleep и gather

В канале по параллелизму выкладывал вам задачки, максимально приближенные к тому, что встречается в проде:
— одна про startup FastAPI-приложения
— вторая про aiohttp и внешний API

На первый взгляд там всё выглядит нормально, но в проде обе реализации могут сделать больно)

Сначала попробуйте найти проблемы сами 👉 [t.me/+1RkYlWso3f5jMWNi]

А затем смотрите видеоразбор, в котором я показываю, что именно там не так и как такие вещи чинить [t.me/+1RkYlWso3f5jMWNi]

1 month ago | [YT] | 42

Артём Шумейко

Завел отдельный канал [t.me/+1RkYlWso3f5jMWNi] для тех, кто хочет прокачаться в параллелизме и конкурентности в Python

Мыслей и примеров у меня много, и не хочется этот канал засыпать кучей постов про техничку, тут все-таки и новичков много.
Так что если ты уже давненько пишешь на Python, а в теме корутин/потоков/процессов/воркеров все еще плаваешь — подписывайся, будет полезно

👉 Подписаться [t.me/+1RkYlWso3f5jMWNi]

P.S. На аватарку поставил ии-чубзика, а то у питона нету норм маскота)

1 month ago | [YT] | 9

Артём Шумейко

Иллюзия скорости в Python

Я знаю, что многие разработчики, когда только начинают писать асинхронный код, очень быстро впадают в зону комфорта — пишешь себе async/await, все работает — и кажется, что ты познал асинхронность. Кто-то даже параллелит запросы через gather)

В целом, в простеньких проектах, где пишутся обычные crud'ы, мало специальной логики и небольшая нагрузка, этих знаний достаточно, так как банально нету сложной бизнес-логики. Причем часто со временем приедается проект: ты уже знаешь весь стек, быстро решаешь задачки, и хочется попробовать чего-то новенького. Почуствовать то самое удовлетворение от решения сложной задачи, как раньше — а сложных задач никто не дает. И тут у многих разработчиков просыпается желание попасть в какой-нибудь бигтех или хотя бы проект, где есть непростые задачи, так чтобы расти и профессионально, и по деньгам.

Но стоит попасть в компанию с непростой бизнес-логикой, высокими нагрузками и реальными требованиями к лейтенси, может поехать кукуха от резкого наплыва новой инфы. Например, в проде будет 10-15 воркеров, кубернетес, тяжёлые операции, которые нужно выносить в потоки, процессы или очереди типа celery/taskiq, и всё это должно консистентно работать вместе. И вроде, чтобы нормально справляться с задачами, нужно уметь в параллельность и конкурентность в Python, но как только копаешь чуть глубже в эти темы — понимаешь, что не все так просто.

Приведу пример с прода. Например, хэширование паролей или тяжёлые валидации являются синхронными операциями, они блокируют событийный цикл на 50-200 мс так, что воркер перестаёт отвечать на запросы — и ты можешь просто не понимать, почему апишка начинает тупить уже при 10-20 запросов в секунду, хотя, казалось бы, всё написано асинхронно и локально работает нормально.

Или например, у знакомого был на проде цикл с while True + asyncio.gather и у них со временем сильно росла оперативка, и они не понимали почему — приходилось убивать контейнер и поднимать заново. А дело было в том, что разработчик не учел, что в gather при падении одной таски все остальные продолжают работать! В итоге накапливался снежный ком из задач, который постепенно сжирал память процесса.

И вот такие баги случаются обычно на проде, когда локально у тебя вроде все ок работает, а при реальной нагрузке начинает тормозить/падать.

Мне очень нравится эта тема, хочу вам побольше рассказать о ней. Но сначала ответьте на вопрос в комментах 👇
- Что в теме параллельности/конкурентности/асинхронки в питоне для вас самое сложное?

1 month ago | [YT] | 94

Артём Шумейко

Взрослая аналитика

Мы в моем стартапе Солвит в какой-то момент заметили интересную особенность: есть пользователи, которые купили подписку на сайт и регулярно на него заходят, но при этом не делают ничего "продуктивного" — не решают задачи, не отмечают прогресс в роадмапах, не проходят вопросы со статусами "знаю" / "не знаю". Просто сидят на сайте по 20-30 минут и уходят. Я, честно говоря, не понимал, что они вообще делают и зачем им подписка.

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

Мы начали с логирования на бэкенде. Суть простая: каждый запрос, который делает пользователь — GET, POST, PUT, DELETE, вообще всё — мы записываем в базу. Причём логируем и авторизованных, и неавторизованных пользователей. Реализовали через dependency в FastAPI:
@router.get("/coding", dependencies=[Depends(questions_logger(action="get_coding"))])
async def get_coding_questions(...):
...

@router.get("", dependencies=[Depends(mentors_logger(action="get_list"))])
async def get_mentors(...):
...
Фабрика questions_logger / mentors_logger возвращает зависимость, которая логирует событие с нужным action, user_id, таймстемпом и метаданными. Получается довольно удобно — добавляешь одну строчку в роут, и всё работает, при этом сам эндпоинт и бизнес-логика остаются чистыми.

Данные льём в ClickHouse — это колоночная СУБД, которая заточена под аналитические запросы по большим объёмам событий. За пару месяцев накопилось уже 2 миллиона записей. Пока мы сами аналитикой не занимаемся — некогда, но в ближайшие месяцы планирую нанять аналитика, который будет работать с этими данными, исследовать пользовательский путь, искать инсайты и предлагать решения проблем. В итоге это поможет нам улучшить опыт пользователей на сайте и сделать обучение более приятным. И когда аналитик придёт — данные уже будут, можно сразу начинать работать.

Мне кажется, это корректная стратегия — заранее думать о таких вещах. Мы, например, начали трекать посещения сайта авторизованными пользователями только полгода назад, и сейчас очень жалеем, что у нас нет данных за предыдущий период. Хранение стоит копейки, а вот машину времени не купишь)

Понятно, что сейчас мы видим не всё — какие-то переходы между страницами, нажатия на кнопки не вызывают запросы на бэкенд, но при этом тоже показывают путь пользователя по сайту. В будущем, возможно, добавим ещё и отслеживание событий на фронтенде, чтобы картина была полной.

А у вас на проекте есть подобный сбор аналитики по действиям пользователей?

3 months ago | [YT] | 32

Артём Шумейко

Пагинация в Postgres: это спрашивают на собеседовании

На собеседовании вас вполне могут подловить на этом вопросе, и если вы никогда не реализовывали бесконечный скроллинг — легко дать поверхностный ответ и упустить важное. А штука в том, что бесконечная лента встречается много где: список уведомлений, история покупок, решённые задачи пользователя, лента постов — везде, где нет классической пагинации с номерами страниц, а контент подгружается при скролле. Даже если вы такое не писали, полезно понимать, как Postgres обрабатывает разные подходы под капотом.

Суть задачки: нужно сделать бесконечную ленту записей. Как вы выстроите получение следующих записей в базе?

Вариант 1 — OFFSET-пагинация (классика):
SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 10
SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 1000

Вариант 2 — пагинация "по ID" (она же cursor-based):
SELECT * FROM posts WHERE id < :last_id ORDER BY id DESC LIMIT 10

Какой лучше и почему? (ответьте про себя и потом читайте дальше)
OFFSET на больших смещениях начинает тормозить, т.к. базе приходится пройти все предыдущие строки, прежде чем вернуть нужные 10. То есть когда пользователь долистал до сотой страницы и мы делаем OFFSET 1000 — Postgres пробегает всю тысячу записей, выбрасывает их за ненадобностью и только потом отдаёт наш десяток.

Пагинация по ID работает иначе: мы сразу говорим "дай мне записи, где id меньше вот этого", и база использует индекс, чтобы сразу прыгнуть в нужное место. Никакого пробега по лишним строкам и сильный буст производительности.

А теперь почему так происходит на уровне базы

Когда мы пишем OFFSET 1000, Postgres не может магически "перепрыгнуть" к нужной позиции — он вынужден последовательно пройти все строки от начала выборки, отсчитать 1000 штук и только потом начать возвращать результат. Это происходит потому, что OFFSET — это не условие фильтрации, а инструкция "пропусти N строк из результата", и база не знает заранее, какие именно строки окажутся на позициях 1001-1010, пока не пройдёт предыдущие.

С cursor-based пагинацией всё иначе: условие WHERE id < :last_id — это полноценный фильтр по значению, и если на колонке id есть индекс (а на primary key он есть всегда), то Postgres делает Index Scan — сразу прыгает в нужную точку B-tree и оттуда забирает 10 записей. Сложность получается O(log n + limit) вместо O(offset + limit), и на больших смещениях разница становится очень заметной.

Но есть нюансы (и это важно проговорить на собесе):
— Если OFFSET в пределах первых 100-200 записей и нагрузка небольшая — разницы можете вообще не заметить, и городить cursor-пагинацию ради этого не нужно
— Пагинация по ID требует, чтобы поле, по которому фильтруешь, было проиндексировано
— OFFSET удобнее, когда нужно прыгнуть на конкретную страницу ("покажи страницу 50"), с cursor-подходом так не сделаешь

Кстати, узнал об этом я, когда выполнял задачу по созданию сервиса уведомлений на позиции Middle+. Тогда мой руководитель обратил на это внимание, и я удивился, что казалось бы даже в такой простой задаче с пагинацией можно наговнокодить)

3 months ago | [YT] | 79

Артём Шумейко

Про синдром самозванца
 
Он часто возникает у новичков/junior разработчиков, потому что наша сфера устроена так, что приходится сталкиваться с новыми технологиями, чужим кодом, сложными архитектурными решениями и требованиями, которые выходят за рамки знаний. Даже если уже что-то умеешь, всегда найдётся область, в которой знаний не хватает, и мозг автоматически делает вывод: я недостаточно хорош.

Кто сейчас с этим столкнулся, я вас понимаю ведь тоже проходил это, поскольку на начальном этапе тяжело оценить свою экспертизу, и непонятно, в какой момент можно идти на собеседование, или оценить, насколько справляешься с работой 😱

Поэтому я собрал практический курс по backend’у [clck.ru/3SPKdK] так, чтобы он закрывал весь цикл разработки: от первых эндпоинтов до полноценного production-ready проекта с архитектурой, тестами, Docker, CI/CD и финальным код-ревью. 

На курсе рассматриваются:
➡️ Архитектура Python приложений (слоистая архитектура, паттерны и приемы production)
➡️ Тестирование: Unit, Интеграционные и API тесты
➡️ Авторизация и аутентификация (JWT токены)
➡️ Комфортная работа с Docker и Docker Compose для развертывания проектов
➡️ Конфигурация Nginx для защиты от DOS атак
...и множество других тем

Также есть отдельный модуль с подробными разборами production-кода на FastAPI и Flask объёмом 50 000+ строк с разбором концепций, которые дают ту самую насмотренность и понимание, как на самом деле пишут сервисы в Бигтехе.

Просмотр этих видео заменит вам несколько месяцев работы на реальном проекте.

Присоединяйтесь к обучению сейчас, пока действует скидка 15% 👉 Изучить программу [clck.ru/3SPKdK]

Если у вас есть вопросы или вы хотите получить экскурсию по обучению, нажимайте сюда [clck.ru/3SPKfs]

4 months ago | [YT] | 9

Артём Шумейко

Как мы снизили нагрузку на бэкенд, почти ничего не делая 😎

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

Один из способов этого добиться, про который я рассказывал на вебинаре по кэшированию в декабре — это HTTP-кэширование через заголовок Cache-Control. Суть простая: ты, как бэкенд-разработчик, говоришь браузеру "эй, эти данные не изменятся ближайший час, сохрани их у себя и не дёргай сервер". И браузер слушается — при повторном запросе он даже не пойдёт на бэкенд, а возьмёт данные из своего локального кэша.

Тут мы сразу убиваем трёх зайцев:
— нагрузка на бэкенд падает, т.к. часть запросов просто не доходит до сервера
— у пользователей сайт грузится быстрее, потому что данные уже локально лежат
— и даже трафик экономится

В Солвите мы так кэшируем всё, что редко меняется: список языков программирования и направлений для собеседований, каталог баз вопросов, список кодинг-задач. Ставим Cache-Control: public, max-age=600 — это 10 минут. Даже если что-то обновится, через 10 минут юзер получит свежие данные, а для словарей это вполне приемлемо. С точки зрения технической реализации мы просто юзаем библиотеку fastapi-cache (github.com/long2ice/fastapi-cache).

Понятно, что это не серебряная пуля и подходит не для всех данных — профиль пользователя или баланс так кэшировать не стоит. Но для всяких справочников, конфигов, статичных списков — самое то.

Любопытно, что такое кэширование внутри браузера немного портит аналитику — мы не видим все запросы, которые браузер мог бы отправить на бэкенд, потому что часть из них он обслуживает сам из кэша.

Кстати, те кто был на том вебинаре по кэшированию — насколько вам зашло? Стоит выложить запись в паблик?

4 months ago | [YT] | 37