Бодрый кодер – Telegram
Бодрый кодер
450 subscribers
248 photos
24 videos
4 files
164 links
Меня зовут Лев, я руководитель направления в ПСБ. Это мой личный блог о DevOps, разработке, системном анализе, AI и управлении IT-командами. Делюсь своими мыслями, инсайтами, полезными инструментами и тем, что меня вдохновляет.

Автор: @nemirlev
Download Telegram
Всех с выходными.

#пятничныйюмор
😁8🔥2🤣2❤‍🔥1
Главная новость сегодня - Сбер вернул оплату на iPhone, с помощью Bluetooth. Хорошее решение, но мне конечно больше нравится UWB, гораздо интереснее протокол. Надеюсь когда-нибудь найду время с ним поиграться. С помощью него например можно искать вещи в помещении до сантиметров или считать людей в зале :)
🧩 Кэш в Redis: не всегда "в лоб"

Часто вижу один и тот же паттерн:
– получили данные из БД,
– положили их в Redis,
– отдали пользователю.

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

👉 Основное правило при проектировании кэша — учитывать частоту обновления данных и, в зависимости от этого, строить логику инвалидации. Но данные даже внутри одной таблицы редко бывают однородными. Redis — это не просто "хранение JSON".

Например, типовой кейс:
у нас есть сущность *Post*, у неё есть "тяжёлые" атрибуты (текст, автор, дата), а есть счётчики (кол-во просмотров, лайков, комментариев).
Обновляются они с совершенно разной частотой.

Если всё хранить в одном JSON-ключе, то:
- при каждом инкременте счётчика мы будем перезаписывать весь объект,
- рискуем гонками и избыточной нагрузкой,
- теряем смысл "лёгкого" кэша.

🚀 Куда эффективнее хранить две записи:
1. сам объект (Post body),
2. отдельный ключ под счётчики.

Что это даёт:
- быстрые и атомарные обновления через INCR / HINCRBY,
- возможность сбрасывать "тело" поста по TTL, не трогая счётчики,
- гибкость в инвалидации: разные правила для разных типов данных.

А получать данные из кэша в этом случае лучше не "в лоб" (два `GET`), а через pipeline — так мы минимизируем RTT.

Для хранения вместо JSON отлично подходят структуры Redis:
- HASH (для набора атрибутов),
- STRING + INCR (для простых счётчиков).

Итог: кэширование — это не "сложили JSON и забыли". Это проектирование модели данных под конкретный паттерн использования. И чем тоньше вы это сделаете, тем больше выиграете в производительности и стабильности.

💡 А у вас в проектах Redis чаще живёт как "JSON-склад" или как полноценный инструмент с типами и пайплайнами?
🔥9👍2
В комментарии к прошлому посту, попросили поделиться подробнее про pipline.

Часто кэш читают/пишут «в лоб» — по одному запросу. Но основная цена Redis в проде — это RTT (туда-обратно по сети). Pipeline позволяет отправить пачку команд за один сетевой раунд — сервер выполнит их по порядку, а ответы придут одной очередью.

Что такое pipeline (и чем он НЕ является)
- Pipeline не делает операции атомарными. Это просто «упаковка» команд в один раунд-трип.
- Для атомарности нужен MULTI/EXEC (**TxPipeline** в go-redis) или Lua-скрипт, либо WATCH + TxPipelined.
- Команды в pipeline выполняются по очереди на сервере, но клиент не ждёт ответ после каждой — экономим RTT.

---

Когда использовать
- Серии однотипных чтений/записей (например, 50 ключей GET / `HGETALL`).
- Смешанные операции, где ответы предыдущих команд не нужны для формирования следующих.
- Интеграции «на пути» запроса (критично снижать задержку).

Когда не стоит:
- Когда следующая команда зависит от результата предыдущей → делайте двумя фазами.
- Огромные пакеты (тысячи команд) — растёт задержка одной пачки и риск таймаутов. Лучше батчить.

---

Простой пример на Go.


package main

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/redis/go-redis/v9"
)

type Post struct {
ID, Author, Title, Content string
}

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

rdb := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})

// 1) Запись: тело поста + счётчики одним pipeline
post := Post{ID: "42", Author: "alice", Title: "Pipeline 101", Content: "…"}
body, _ := json.Marshal(post)

_, err := rdb.Pipelined(ctx, func(p redis.Pipeliner) error {
p.Set(ctx, "post:42:body", body, 10*time.Minute) // TTL у тела
p.HSet(ctx, "post:42:counters", "views", 0, "likes", 0) // счётчики отдельно
p.Persist(ctx, "post:42:counters") // живут дольше тела
return nil
})
if err != nil {
panic(err)
}

// 2) Чтение: тело + счётчики за 1 RTT
var (
bodyCmd = rdb.Get(ctx, "post:42:body")
ctrCmd = rdb.HGetAll(ctx, "post:42:counters")
)
// Внимание: без Pipelined() мы сделали 2 команды, но они уйдут подряд на одном соединении.
// Для явного пайплайна:
pipe := rdb.Pipeline()
bodyCmd = pipe.Get(ctx, "post:42:body")
ctrCmd = pipe.HGetAll(ctx, "post:42:counters")
if _, err := pipe.Exec(ctx); err != nil && err != redis.Nil {
panic(err)
}

var p Post
if b, err := bodyCmd.Bytes(); err == nil && len(b) > 0 {
_ = json.Unmarshal(b, &p)
}
counters := ctrCmd.Val()
fmt.Println("noscript:", p.Title, "views:", counters["views"])
}
👍4🔥2
#пятничныйюмор в субботу. Пропустил
❤‍🔥5
Бодрый кодер
#пятничныйюмор в субботу. Пропустил
Правды ради, сейчас уже не совсем так.

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

На фоне этого, не мог пройти мимо самой популярного тулинга, которым пользуются почти все владельцы Zeekr (Zeekr ADB toolkit). Ну познакомился с создателем. У тулинга (код только у первой версии есть), код ужасный, написанный нейросетью. Но блин, он полностью решает возложенную на него задачу и куча людей которые с компом на ВЫ, экономит и экономила десятки тысяч рублей (ценны на работу по Zeekr просто космос, как и на Tesla в принципе).

Оказалось что создатель тулинга - фотограф, и просто за счет нейросетей прокачался так, что я был уверен - говорю с разработчиком. Для него это стало отдушиной от работы. И все благодаря GPT.

Я это к чему, нейросети - классно, но не нужно рассчитывать что они сделают за вас, а это большая возможность облегчить себе работу, быстро решить задачи и научиться тому, что никогда не делали.
8👍3
Рубисты могут сильно сэкономить. Среда разработки RubyMine от JetBrains стала бесплатной для некоммерческого использования

В 2024 году JetBrains сделала бесплатными для некоммерческого использования WebStorm, Rider и RustRover. В начале 2025 года к списку добавили CLion, а сейчас — RubyMine.

По бесплатной некоммерческой лицензии доступны все функции IDE, кроме Code With Me. Для доступа к ней надо оформлять подписку.
❤‍🔥7🌚2
Всех с выходными. #пятничныйюмор
😁10🌚2
Который раз убеждаюсь — профессия разработчика не даёт заскучать. Каждый день узнаёшь что-то новое.

Недавно со мной поделились интересной находкой: оказывается, в Nginx Ingress есть встроенная поддержка телеметрии. Можно буквально «из коробки» настроить отправку трейсов в OTLP, например, в Jaeger.

Я-то всегда был уверен, что в бесплатном Nginx такой возможности нет, и даже мысли не возникало искать её в Ingress. А зря 🙂

И самое приятное: даже для «чистого» бесплатного Nginx тоже есть решение — nginx-otel. Судя по описанию, этот модуль работает аж в 4 раза эффективнее официального варианта из Nginx Plus.

#nginx #otel #observability #devops #ingress
👍8
Вышли новые iPhone, Watch и AirPods, как всегда лучше, больше, тише, круче и прочее эпитеты.
Самое смешное

Акции Apple начали падать после презентации новых гаджетов


Как правило растут, видимо ждали AI, но его не будет еще год.
Начался учебный год — у меня ещё нет пар, но в преддверии занятий делюсь полезной заметкой по БД. Часто вижу, как «на всякий случай» выбирают типы побольше — в итоге едим место, замедляем индексы и ловим баги.

Антипаттерны, которые встречаю:
- VARCHAR(255) «для всего подряд», даже если код фиксированной длины (например, 10 символов).
- TEXT для email-адресов.
- BIGINT для счётчика, где максимум — тысяча записей.
- FLOAT для денег (теряется точность).

Как делаю сам:
- Строки — по задаче: VARCHAR(254)`/`VARCHAR(320) для email (под стандарты), CHAR(2) для кода страны.
- Деньги — только NUMERIC(10,2) / DECIMAL(10,2) или целые «копейки/центы» в INT`/`BIGINT.
- Булево — BOOLEAN, а не INT.
- Даты/время — DATE / TIMESTAMP, не строка.
- Автонумерация — INT по умолчанию; BIGINT — только если реально ждёте > 2 млрд строк.

📌 Пример
-- Плохо
price FLOAT;

-- Хорошо
price NUMERIC(10,2);

⚡️ Итог: грамотный выбор типов = меньше места, быстрее запросы, меньше багов.

#БД #SQL #PostgreSQL #Оптимизация #Backend #DataEngineering
🔥12👍31
В продолжении поста про PostgreSQL, поделюсь про Redis. Чаще всего используют только строку, но он гораздо многообразнее.

🧱 String
— Ключ-значение, базовый кирпич. Кэш, флаги, счётчики, токены.
— Пример: SET key val, GET key, INCR views, SETNX, EXPIRE key 60
— Паттерны: rate-limit (`INCR + EXPIRE`), простые распределённые блокировки (`SET lock val NX PX 10000`)

🧩 Hash
— «Объект» с полями: компактно хранит мелкие атрибуты.
— Пример: HSET user:42 name Lev email lev@..., HGETALL user:42
— Когда: профили пользователей, настройки. Плюс — частичные обновления полей.
— Нюанс: нет TTL на отдельные поля, ставится на весь ключ.

📚 List
— Упорядоченная коллекция. Очереди (FIFO/LIFO), буферы, журналы.
— Пример: LPUSH queue job1, BRPOP queue 0
— Когда: простые очереди, ретраи. Не забывайте LTRIM для ограничения длины.

🏷 Set
— Множества без дубликатов. Теги, интересы, подписки.
— Пример: SADD tags:post:1 go devops, SINTER user:42:tags post:1:tags
— Когда: рекомендации по пересечению множеств, дедупликация.

🏆 Sorted Set (ZSet)
— Множество с весом (score). Рейтинги, приоритизация, «календарь по времени».
— Пример: ZADD leaderboard 1500 user42, ZRANGE leaderboard 0 9 WITHSCORES
— Когда: топ-N, очереди с приоритетом, time-index (`score = timestamp`).
— Хитрость: чистка старых данных — ZREMRANGEBYSCORE.

🧮 Bitmap
— Битовые флаги по смещению. Ультра-дешёвая памятью аналитика по дням.
— Примеры SETBIT active:2025-09-10 42 1, BITCOUNT active:*
— Когда: «приходил ли пользователь в день D», воронки по дням, календари активности.

📏 Bitfield
— Упакованные счётчики/флаги в одном ключе.
— Пример: BITFIELD key INCRBY u8 10 1
— Когда: компактные счётчики для ограниченного диапазона (экономия памяти).

🔢 HyperLogLog
— Приблизительное количество уникальных (distinct).
— Пример: PFADD u:visits user42, PFCOUNT u:visits
— Когда: уникальные посетители/поисковые запросы, где допустима погрешность.

📍 GEO (геоиндексы)
— Геопоиск по радиусу/полигону.
— Пример: GEOADD places 13.405 52.52 berlin, GEOSEARCH places FROMLONLAT 13.4 52.5 BYRADIUS 5 km
— Когда: «что рядом», ближайшие точки интереса.

📨 Streams
— Лог событий с потребительскими группами (Kafka-лайт).
— Пример: XADD events * type=signup user=42, XREADGROUP GROUP g c COUNT 10 STREAMS events >
— Когда: event sourcing, обработка событий с подтверждениями.

📣 Pub/Sub
— Эфемерные сообщения «здесь и сейчас».
— Пример: PUBLISH chan msg, SUBSCRIBE chan
— Когда: оповещения в реальном времени, без хранения истории (не путать со Streams).

🧾 RedisJSON (Redis 8+)
— Хранение JSON как дерево, выборка/патчи по путям.
— Пример: JSON.SET user:42 $ '{"name":"Lev","age":33}', JSON.NUMINCRBY user:42 $.age 1
— Когда: нужны частичные обновления и индексирование полей (с RediSearch).
— Альтернатива: String с сериализацией — просто, но без частичных апдейтов.

🧠 Итог
— В Redis выбор структуры = половина решения. Строка с JSON — ок для простого кэша, но для очередей, рейтингов, счётчиков и аналитики есть куда более точные инструменты.

#Redis #БД #Кэширование #Backend #Шпаргалка #DataEngineering
6👍5🔥2❤‍🔥1
Нашел прям лучший подход по видению ToDo.
# TODO(DEV-12346): Надо поправить конфиг

То есть не просто пишешь, а потом в IDE контролируешь, а сразу завешиваешь задачу. Прям гениально и просто.

P. S. Понятно что все равно не гарантия исправления.
👍3🔥1
Всех с выходными!

#пятничныйюмор
7😁3🌚1
Бодрый кодер
Который раз убеждаюсь — профессия разработчика не даёт заскучать. Каждый день узнаёшь что-то новое. Недавно со мной поделились интересной находкой: оказывается, в Nginx Ingress есть встроенная поддержка телеметрии. Можно буквально «из коробки» настроить отправку…
Всех причастных с профессиональным праздником, днём программиста!

Спустя неделю, пишу другу, говорю «Зацени какая фича есть», это я так с оф.доки Nginx ingress вылез, изучая что еще упустил. А он мне скидывает наш диалог, где как раз об этом рассказал, после чего я сделал пост.

И тут я понял, что рабочая неделя была напряжённой.
😁75
PostgreSQL 18 уже почти на подходе, и одна из самых приятных новинок для разработчиков — нативная поддержка UUIDv7.

Если раньше gen_random_uuid() выдавал только UUIDv4 (UUIDv4 генерируются случайно, поэтому новые записи вставляются в разные места B-tree индекса. Это вызывает фрагментацию, рост размера индекса и замедление операций), то теперь у нас появляется функция uuidv7(). Она генерирует идентификаторы, упорядоченные по времени, что решает старую боль с раздутием B-tree индексов (новые ключи ложатся в «хвост» индекса, как автоинкремент, и поэтому работают быстрее).


CREATE TABLE test (
id uuid DEFAULT uuidv7() PRIMARY KEY,
name text
);

INSERT INTO test (name) VALUES ('foo'), ('bar');
SELECT uuid_extract_timestamp(id), name FROM test ORDER BY id;


Теперь IDшники не только уникальны, но и красиво сортируются по времени создания 👌
Для распределённых систем и масштабных БД — прямо must-have.

#postgresql #uuid #uuidv7 #базыданных
🔥6👍2
Я тут хвалил новые веб приложения в телеге, тут вообще удивился. Открыл себе карту, для оплат, дополнительную. За 2 минуты. Банкинг в телеге - это конечно мощно и удивительно.

P.S. Не реклама сервиса, только начал пользоваться , если понравился и проблем не будет - порекомендую.
👍1🔥1
Вчера ездил на экскурсию в ЦОД Яндекс, во Владимир. Получилось классно.

Инженерные решения конечно потрясающее. Особенно собственные стойки и решения по юнитам.

Ну и конечно посмотрели сердце одного из суперкомпьютеров, на котором в том числе обучают Алису.
👍11🔥53😍1