Lasiar работает 💡
161 subscribers
7 photos
47 links
Про Golang и около того

help: @Vallder
Download Telegram
Недавно вспоминал о своем докладе: "Почему Docker написан на Go", который я готовил год назад для подлодки.

В этом докладе вас ждет:
- 🕵️ Теория заговора
- 📅 История контейнеризации с 1979 года
- 💔 Почему K8s отказался от Docker (спойлер: не совсем отказался)
- 📑 Не совсем публичные слайды команды dotCloud (ex Docker, inc)

📺 Смотреть

P.S. Все еще актуально, как и год назад! 👀
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥95🥰2
Ранее говорил о GOMAXPROCS и cgroup.

И вот в новой версии Go (1.25) уже не понадобится библиотека github.com/uber-go/automaxprocs, так как cgroup для GOMAXPROCS будет учитываться "из коробки".
Уже даже документация обновлена для master ветки.
🔥1032🍓1💅1
Кстати, про 1.25:
сегодня-завтра должен выйти Go 1.25 RC1
🔥72🍾2💅1
Я пропустил что в Go 1.22 добавили работу с относительно новой сущностью Linux pidfd.

Зачем это в Linux?
Работа с PID по сути всегда гонка:
- Получаем PID процесса (например, PID=100);
- Процесс умирает,
- ОС создает новый процесс, переиспользуя PID=100;
- В итоге мы работаем с совершенно другим процессом.

Это причина уязвимостей: CVE-2019-6133, CVE-2019-15790... А так же причина одного из issue у Go .

Решение
В Linux 5.1 появился системный вызов pidfd_open(pid_t pid, unsigned int flags). Он даёт ссылку на конкретный процесс, а не на PID, который может быть переиспользован.
Дальше (вплоть до ядра 6.5) добавили и другие методы получения pidfd.

Что в Go?
И уже в Go 1.23 в методах и функциях, которые относятся к процессу используется по возможности pidfd.
🔥9👍4🍾2👨‍💻2😁1💅1
В последнее время довольно часто приходится работать напрямую с TCP/UDP, а иногда и по SSH.

В таких случаях особенно важно использовать connection pool и следить, чтобы соединение не "умерло" - - так мы избегаем накладных расходов на рукопожатие (особенно важно если в протоколе есть дополнительные "телодвижения").

Отлично в этом помогает библиотека от jackc -- puddle (автор, кстати, того же pgx).

Грубо говоря, это sync.Pool{}, только не для памяти, а для ресурсов, причём с возможностью ограничить количество создаваемых экземпляров. Если лимит достигнут, попытка получить новый ресурс блокируется до появления свободного -- что помогает контролировать нагрузку и избегать перерасхода соединений.

Примеры использования вне README можно посмотреть в исходниках pgx (файл pgxpool/pool.go) и ch-go (файл chpool/conn.go).

Ну и небольшой пример из README:

func main() {
constructor := func(context.Context) (net.Conn, error) {
return net.Dial("tcp", "127.0.0.1:8080")
}
destructor := func(value net.Conn) {
value.Close()
}
maxPoolSize := int32(10)

pool, err := puddle.NewPool(&puddle.Config[net.Conn]{Constructor: constructor, Destructor: destructor, MaxSize: maxPoolSize})
if err != nil {
log.Fatal(err)
}

// Acquire resource from the pool.
res, err := pool.Acquire(context.Background())
if err != nil {
log.Fatal(err)
}

// Use resource.
_, err = res.Value().Write([]byte{1})
if err != nil {
log.Fatal(err)
}

// Release when done.
res.Release()
}
👍9🔥7
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Уххх!
В Go начали завозить intrinsics для SIMD.

То есть, возможно, можно будет вызывать SIMD инструкции напрямую, прямо из Go кода.

Для теста можно подтянуть ветку через gotip:
go install golang.org/dl/gotip@latest  # если ещё не установлено
gotip download dev.simd # собираем ветку dev.simd


Правда, пока что поддержка только для amd64, так что локально потыкать у меня не получится :(

Как только появится arm -- обязательно сделаю бенчмарки и поделюсь результатами.

Кратко о текущем состоянии -- в комментарии Cherry Mui: https://github.com/golang/go/issues/73787#issuecomment-3208178910
🔥31👏1🍓1💅1👾1
Всем привет!)

Второй год участвую в программном комитете конференции E-CODE, помогаю с формированием трека backend.

Буду лично на мероприятии, оно пройдёт 13-14 сентября.

Если вы тоже там будете -- можно пообщаться в перерывах между докладами)
6🥰4🐳1🍓1👾1
В Go завезли поддержку valgrind -- утилиты, которая может обнаруживать утечки памяти.

Это, конечно, круто, но зачем?

На этот вопрос ответил автор CL, а также security team lead в Golang -- Roland Bracewell Shoemaker.

Основная цель -- находить функции, которые выполняются не за константное время.

Тут у кого-то может возникнуть вопрос:
Зачем нам функции, которые работают за константное время? Разве мы не хотим, чтобы они выполнялись как можно быстрее?
А тут стоит посмотреть на роль того, кто добавил CL SECURITY team lead.

В криптографии очень важно, чтобы функции выполнялись за константное время.
Если передавать разные входные данные и замерять время выполнения, можно провести timing oracle attack.

Тривиальный и упрощённый пример:
Мы побайтово сравниваем два хеша и при первом несовпадении выходим из функции раньше.

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

Чтобы предотвратить это, нужно, чтобы доступ к памяти и ветвление кода были одинаковыми для всех входных данных.

Для проверки этого как раз и нужен valgrind -- он отслеживает, какие области памяти и регистры используются в каждой ветке исполнения.

Подробнее про использование valgrind в рамках поиска функции, которые выполняются не за константное время можно почитать тут репозиторию аж 15 лет.
🔥93👾2💅1
TL;DR
Изменения пользователя и прав в Dockerfile при копировании можно выполнить одной командой.

COPY [--chown=<user>:<group>] [--chmod=<perms> ...] <src> ... <dest>


Docker использует файловые системы, c поддержкой Union mount -- накладывание одного дерева файлов на другой.

Одно такое дерево файлов в терминологии Docker называется слой (layer).
Слои нельзя изменить, изменения записываются в новый слой.

Каждая инструкция в Dockerfile создает отдельный слой.

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

Особенно больно, когда у нас много файлов или они тяжелые.
Для изменения атрибутов, приходится все копировать в новый слой.

Чтобы избежать этого можно сразу указать --chown и/или
--chmod при копировании.


┌─────────────────────────┐
│ Верхний слой │ ← Изменения прав (chmod/chown)
├─────────────────────────┤
│ Данные приложения │ ← COPY . /app
├─────────────────────────┤
│ Базовый образ (OS) │ ← FROM ubuntu/alpine/etc
└─────────────────────────┘
6👍3🥰2👾21💅1
Потихоньку в стандартную библиотеку Go добавляют работу с generic.

В Go 1.26 добавили функцию errors.AsType[E error](err error) (E, bool).

Релиз Go 1.18, где появились generic был 25 марта 2022 года, а уже через 10 дней появился issue, где предлагалось добавить generic функцию в пакет errors.
Только первоначально говорилось об изменении errors.As(), возможно из-за этого только спустя три года добавилась функция errors.AsType().

В целом -- мне нравится.

Во-первых, это выглядит симпатично:

pathError, ok := errors.AsType[*fs.PathError](err)


Во-вторых, это значительно быстрее:
Код бенчмарка: https://go.dev/play/p/AcQyInSlR7D
Результат:

goos: darwin
goarch: arm64
pkg: test
cpu: Apple M4 Pro
│ as.txt │ as_type.txt │
│ sec/op │ sec/op vs base │
Errors-12 33.480n ± 1% 2.113n ± 1% -93.69% (p=0.000 n=20)

│ as.txt │ as_type.txt │
│ B/op │ B/op vs base │
Errors-12 8.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=20)

│ as.txt │ as_type.txt │
│ allocs/op │ allocs/op vs base │
Errors-12 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=20)
🔥43💘2👍1💅1
https://t.me/LasiarAtWork/53

В Go 1.26 добавят реализацию SIMD для amd64.

При этом возможно этот пакет будет использоваться в green tea (новый GC, который будет включен по умолчанию в Go 1.26), что должно ускорить добавление других архитектур в пакете simd.
🔥5
Там Nvidia в ядре Linux готовят сетевую подсистему под 1600Gbps (8x200Gbps).

Скорость просто невероятная!

Думаю что это необоходимо для нейроных сетей.

Немного контекста:
Nvidia с 2020 года владеет компанией Mellanox, которая выпускает сетевое оборудование для HPC.
🔥42
Неожиданно для себя я узнал что в DNS можно передавать порт 🙂


Всё благодаря новому типу записи -- SVCB который появился пару лет назад и описан в RFC 9460.
Вообще в драфте он был достаточно давно, даже некоторые DNS клиенты/сервера уже реализовать начали опираясь на драфт.

Во-первых у нас не совем типичные запросы:
мы хотим обратиться к "foo://api.example.com:8443", из-за этого у нас должен быть запрос:
_8443._foo.api.example.com

Во-вторых можно передавать дополнительную информацию:
Я локально поднял DNS сервер и указал там SVCB запись.

запрос:

kdig _443._https.svc.example.test @127.0.0.1


ответ:

;; ANSWER SECTION:
_443._https.svc.example.com. 3600 IN SVCB 1 . alpn="h2,http/1.1" port=8443 ipv4hint=192.168.0.42 ipv6hint=fe80::1805:8978:a257:cea7


Можно так же заметить что передает не только порт, но:
- alpn Application-Layer Protocol Negotiation -- инфомрация про протоколы, чтоб сразу понимать по какому протоколу стоит подключиться;
- ipv4hint, ipv6hint -- ранее нужно было выполнить два DNS запроса A для IPv4 и AAAA для IPv6, а сейчас всё можно получить за один раз.

В целом DNS становится сложнее, но при этом -- мощнее.
С помощью SVCB можно экономить на RTT. Кажется, это действительно крутая штука.
👍6🔥42
В Steam вышла игра NebuLeet.

И, казалось бы, зачем это я пишу в техническом канале?
А дело в том, что разработчик -- Iskander (он же quasilyte), а игра написана на Golang!

Да, именно на Go -- не только гонять JSON с базы на фронт, K8s, Docker, но и геймдев.
🔥71👍1💅1🦄1👾1
в 1.26 кроме green tea есть еще интересная оптимизация связанная со слайсами:
stack-allocation optimization
for the backing store of slices.


В чем суть этой оптимизации:
В некоторых случаях аллоцировать слайс во время append(а) на стеке, а не в куче. Это снижает нагрузку на GC.

К примеру, типичный код:

func f(n int) []int {
var r []int
for i := range n {
r = append(r, i)
}

return r
}


Во время итерирования, когда возрастает capacity (1, 2, 4, 8... итерация) создается память, которая не нужна. Эта памяти будет зачищаться при работе GC.

И в связи с этим стараемся не нагружать GC, а аллоцировать эти мусорные объекты на стеке.

И уже перед выходом из функции помещаем слайс на heap.

func f(n int) []int {
var r []int
for i := range n {
r = append(r, i)
}

r = move2heap(r) // Подставляется во время комплияции
return r
}


Немного деталей:
Во время append(а) вызывается не growslice, а growsliceBuf, где буффер это поинтер на стэк (link).
Перечень того, где может использоваться эта оптимизация перечислина в тестовом файле, (где указано growsliceNoAlias, growsliceBuf).

Links:
CL: go-review.googlesource.com
Более подробная информация как это работат: cmd/compile/internal/slice/slice.go
👍5🔥3👏2
Крайне интересно исследуя Go находить наследия Plan 9.

Russ Cox, одна из ключевых фигур Go. До Go разрабатывал операционную системой Plan 9. Поэтому Go имеет некоторую наследственность Plan 9.

Одна простая строка:

setNoDelay(fd, true)


Имеет почти что 22-ух летнюю историю.

Февраль 2004: в Plan 9 появился комит с емким названием various tweaks, в котором было установлено стандартное повдение для TCP сокета TCP_NODELAY.

Сентябрь 2008: В Go Russ Cox добавляет network, где стандартное поведение c.SetNoDelay(true). Это повдение сохраняется и до сих пор.

Декабрь 2022: На Hacker News Russ Cox ответил зачем все это.

И вот краткий ответ:
TCP_NODELAY отключает Nagle's algorithm, который позволяет эффективнее использовать TCP соединение.

И казалось бы, зачем отключать алгоритм, который что-то улчшает?
А везде есть trade-off.
С помощью этого алгоритма мы не отправляем сразу же все данные, а немного их копим, чтоб в одном TCP пакете данные отправить (у каждого пакета есть overhead в виде заголовка, чем меньше пакет, тем больше процент overhead).
А для RPC важнее отрпавить как можно скорее, поэтому Nagle's algorithm для этой задачи абсолютно не подходит.


Таким образом TCP_NODELAY позволяет нам сразу же отрпавить пакет, не буфферизируя их.
🔥8👍41🥰1
Небольшой tip: как выйти из зависшего SSH при плохом соединении.

Бывает, что сеть просела/отвалилась и нельзя нормально отправить exit или ^D. В этом случае можно разорвать соединение со стороны ssh-клиента, через escape-последовательности.

TL;DR: нажми по очереди: Enter, затем ~, затем . (то есть ~.)

Важно: escape-символ распознаётся только в начале строки, сразу после перевода строки (поэтому и нужен Enter). Работает в интерактивной сессии.

Ещё пара полезных (вводятся так же: Enter, потом команда):

~. - disconnect (разорвать соединение)
~? - показать список всех escape-команд
~^Z - отправить ssh в background
~# - показать активные форвардинги
~R - запросить rekey
~V/v - уменьшить/увеличить verbosity


Они могут отличаться от версии к версии.

Cамая полезная это ~.: когда соединение мёртвое, это часто единственный способ выйти, не убивая терминал целиком.
1👍8🔥51👏1👨‍💻1
Channel photo updated