Forzend's channel


Гео и язык канала: Беларусь, Русский
Категория: Технологии


Канал обо всем и ни о чем.
Учил GoLang, хотел стать Backend Developer'ом — стал. Мечтаю о том, что когда-нибудь у меня дойдут руки до того, чтобы разобраться в web3
Учусь в Белорусском Университете

Связанные каналы

Гео и язык канала
Беларусь, Русский
Категория
Технологии
Статистика
Фильтр публикаций


📖 Про slog

Ещё недавно в Go не было встроенного структурированного логгера (логгера по уровням, со всякими key=value полями, который может писать json в разные системы сбора). Поэтому в большинстве проектов использовались сторонние логгеры (хотя, есть и люди, которые пишут log.Printf("[DEBUG] something happend") и радуются жизни, но я не из таких). Я же, в свою очередь предпочел zerolog.

zerolog предлагает возможность (хотя и не заставляет) прокидывать логгер через контекст. Таким образом вы можете внутри middleware добавлять в логер какие-то поля, такие как request_id, и использовать "расширенный" логгер в хендлере.
Хотя и "фу, context.WithValue", я привык к такому решению и мне оно казалось стандартным допущением.

Однако в стандартной библиотеке появился log/slog. К моему удивлению, функционала для вставки и извлечения логгера из/в контекст не оказалось. Меня это удивило и я, не долго думая, сам реализовал этот функционал. Но потом задумался: "разработчики go ведь не идиоты (хотя я лично знаю много людей, готовых спорить с этим утверждением), как они предлагают использовать логгер?".

slog умеет держать логгер в глобальном atomic.Pointer. И я начал анализировать, а нормально ли использовать глобальный логгер для приложения? С одной стороны весьма удобно, импортировал и юзаешь. Но это немного усложняет тестирование, выходит не очень явно и в целом как-то так себе, глобалы всё-таки...

Но главной причиной отказа от этого решения для меня являлось не понимание того, как же мне заткнуть request_id в логгер и прокинуть в handler логгер вместе с ним. Ведь не буду я в каждом логе руками request_id прописывать.

Хочу подчеркнуть, проблемой было непонимание. Здесь я углубился: на что способен slog? И был приятно удивлён. После стандартного логгера в python примитивность slog меня огорчила. Ведь всё, что у нас есть, текстовый и json логгер (handler) и пара методов для записи логов. Но в моменте мне открылась сила декораторов. Дело в том, что декорируя Handler, который использует логгер, вы можете сделать всё. Буквально всё. От цветного вывода в консоль и записи в несколько источников (хоть в telegram сообщения шли), до тех же самых request_id.

Осознав это, я будто познал тайны мироздания...
Вот, как я решил проблему request_id:
type RequestIDLogger struct {
slog.Handler
}

func (l *RequestIDLogger) Handle(ctx context.Context, r slog.Record) error {
request := getRequestID(ctx)

r.AddAttrs(slog.String("request_id", request))

err := l.Handler.Handle(ctx, r)
if err != nil {
return fmt.Errorf("failed to run parent of RequestIDLogger handler: %w", err)
}

return nil
}

Но использовать глобальный логгер я всё равно не захотел, поэтому распихал их по структурам, как обычно в Go делается dependency injection.
Кажется, это наилучший компромисс между всеми подходами.

Теперь middleware генерирует для каждого запроса request_id и добавляет его в контекст, а handler в slog.Logger извлекает его и добавляет при записи логов.

Кстати, slog уже оброс кучей всяких handler. Много интересного можно найти в awesome slog (думаю, тут есть всё, что вам может пригодиться).

#go #golang #logging #slog


🔄 Range Over Functions: Quick Start

Уже стал доступен первый Release Candidate Go 1.23, который вносит в язык синтаксический сахар в виде итераторов, наконец не в experiment.

Этот пост — быстрый тур в грядущее обновление

Цель: стандартизация

Сама фича позволяет нам теперь в качестве аргумента оператора range использовать функцию, имеющую следующие сигнатуры
func(yield func() bool)
func(yield func(K) bool)
func(yield func(K, V) bool)
(название аргумента, конечно же, может быть любым)

При создании конструктора, чтобы не прописывать трудно читаемый тип возвращаемой функции, можно использовать iter.Seq[T] и iter.Seq2[T]

Легче всего понять что и как на примере.
Вот пример функции
package slices

import "iter"

func Backward[E any](s []E) iter.Seq2[int, E] {
return func(yield func(int, E) bool) {
for i := len(s)-1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}

Вот пример использования
s := []string{"hello", "world"}
for i, x := range slices.Backward(s) {
fmt.Println(i, x)
}

Тем временем компилятор заменит код следующим образом:
slices.Backward(s)(func(i int, x string) bool {
fmt.Println(i, x)
return true
})

Становится понятно, что никакой магии здесь нет. Тело цикла попадает в виде функции-аргумента (yield) в функцию-итератор. Если внутри цикла встречается break — yield возвращает false и итерацию нужно прекратить. В противном случае будет паника.

Собственно, этой информации вам будет достаточно, чтобы писать свои итераторы. Вот набор ссылок по теме:
* 1.23rc1 Release Notes
* Issue с "мотивацией"
* Go Wiki: RangeOverFunctions Experiment
* Proposal of iter library (С чего всё началось)
* go iter package




🧱 Что там с ошибками в Go?

Ошибки в го настолько хороши, насколько ужасны. Отсутствие какого-то синтаксического сахара вынуждает разработчика писать кучу шаблонного кода. Это бесит абсолютно всех.

Проблема не нова и, естественно, известна разработчикам языка.
С нетерпением ждём, что же придумают и сделают во второй версии языка.
Пойдём и посмотрим в issue языка, что там пишут по поводу обработки ошибок. У нас, всё-таки, опен сорс.

Начнём с 40432: резюме по основным решениям.
Check/handle подход, на который я ссылался в одном из постов, был отменён из-за возможной путаницы между handle и defer. На его основе была выдвинута новая идея, которая, на данный момент, имеет больше всего шансов на появление в ожидаемой версии.

Идея в том, чтобы добавить для переменных catch-id (синтаксически, это обычное имя переменной, чем-то выделяющееся, например, символом # в начале имени). После одного или нескольких таких определений, следует блок catch по этому самому id, который обрабатывает ошибку (это может быть любой тип, не обязательно реализующий интерфейс ошибки) если она не пустая. Один блок catch может обработать несколько прежде полученных в один идентификатор ошибок.

Выглядеть это может следующим образом
func (db *Db) GetX(data []byte) (int, error) {
n, #return := db.name() // специальное зарезервированное имя для ошибки, которая вернётся из функции

f, #err := os.Open(n) // если файл не найден, возникнет ошибка, которая будет обработана ниже в блоке catch
defer f.Close()
_, #err = f.Seek(42, io.SeekStart)
l, #err := f.Read(data)

#return = db.process(data)

catch err error { // обработка всех #err ошибок
if !os.IsNotExist(err) { log.Fatal(err) }
log.Println(n, "not found; proceding")
}

#return = db.index(data) // если catch не завершил выполнение программы, продолжаем работу
return l, nil
}
Подход мне кажется не очень привычным и немного запутанным. Хотя так всегда относишься к чему-то новому. Думаю, это всяк лучше того, что мы имеем сейчас. Преимуществом подхода отмечают сходтво с блоком try-catch без блока try.

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


Репост из: dev.insuline.eth
Очень интересный хак Web 3.0 фронтенда происходит прямо сейчас на наших глазах 👀

Под удар попал пакет леджера – ConnectKit, который используется для подключения Ledger Live к приложениям.

Пакет внутри себя загружает в глобальную область видимости js скрипт, расположенный на cdn.jsdelivr.net с дрейнером. Прикладываю кусок кода, где расположен импорт в самой библиотеке

Следовательно, если вы используете какую-то библиотеку для обработки подключения к Ledger Live – ваш фронтенд не безопасен и пользователи подвержены дрейну после авторизации (afaik заменено модальное окно подключения кошелька, так что владельцы любого кошелька в опасности, не только использующие Ledger Live)


Недавно мне довелось выполнять простое тестовое задание. Его суть заключалась в том, чтобы реализовать простой CRUD для пользователей и Basic Auth через gRPC. Несмотря на всю простоту и примитивность проекта, я бы хотел поделиться им с вами. Комментарии открыты, я рад любой обратной связи. Предлагаю обсудить то, что у меня получилось: что вы бы делали иначе и почему?
https://github.com/F0rzend/grpc_user_auth

2.5k 0 14 26 11

🚽 Самое интересное из Technology Radar 29

Ниже представлены инструменты, языки и фреймворки, которые привлекли моё внимание в последнем радаре.

Я рассматриваю утилиты, которые можно смело использовать в своих проектах — Adopt, а так же те, к которым стоит присмотреться — Trial.

🔹 Adopt tool ruff — современный линтер и код форматтер, собравший в себе лучшее из популярных подобных решений. Уже активно используется такими проектами, как FastAPI и Pandas.

🔹 Adopt tool mermaid — утилита для генерирования разного рода схем из текста, весьма удобно для проектирования.

🔹 Trial tool Jetbrains HTTP client plugin — плагин для IDE для отправки http запросов, аля user friendly curl. Думаю может быть полезен для каких-нибудь тестов.

🔹 Trial tool mockserver — инструмент для создания мок http сервера. Можно красиво внепроцессорные неконтролируемые зависимости. Мне кажется довольно удобное стредство для разработчиков различных клиентских библиотек.

🔹 Adopt framework playwright — фреймворк для создания e2e тестов. Очень удобный и гибкий инструмент. Тесты можно писать на python, js, java. Go, к сожалению, нет, но думаю это не критично.

🔹 Trial framework mockery — GoLang инструмент для создания моков. Он выглядит очень симпатично, однако не очень явно и немного "волшебно", складывается ощущение, что писал шарпист. Moq мне нравится больше.

🔹 Trial programming language Dart — мне кажется о дарте знают все и говорить здесь нечего. Очень соблазнительный язык программирования для разработки кросс-платформенных приложений. Думаю, если бы я устал от бекенда, я бы полез в него.

🔹 Trial framework armeria — фреймворк для разработки микросервисов на Kotlin (Java). Мне показался интересным проект. Я не вникал, но выглядит, как Nginx с Django...


😎 Вышел новый Technology Radar. Я видел его ещё год назад, но тогда не понимал что это и зачем.

Эта статья на хабре помогла мне разобраться что и к чему.


🖥 Тестирование Базы Данных

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

Конечно, интеграционные тесты не юнит-тесты и их не так много, поэтому производительность не является ключевым критерием, однако всем нам хочется, чтобы интеграционные тесты у нас выполнялись быстро и не требовали много ресурсов (чтобы их можно было выполнять на локальной машине).
Понятно, что последовательные тесты быстрыми не бывают. А чтобы запустить тесты параллельно, их нам нужно изолировать друг от друга.

Подход №1: В лоб — одна СУБД хорошо, а X лучше
Первое, что приходит в голову, просто (не очень) поднять для каждого теста (или ряда тестов, чтобы не совсем в наглую ресурсы тратить) свой контейнер с СУБД. Делается это с помощью testcontainers, существующем для многих языков, в том числе и для го. Тесты изолированы, спокойно выполняются параллельно. Однако, далеко не каждая машина может держать одновременно огромное количество докер контейнеров, особенно, если тестов действительно много.

Подход №2: Человеческий — СУБД одна, а баз много...
Я предлагаю использовать иной подход. Говорю я применимо к PostgreSQL, однако подобные механизмы есть во всех популярных реляционных СУБД.
Что если для всех интеграционных тестов поднять одну СУБД, но для каждого отдельного теста создавать отдельную базу данных. Преимущества очевидны. Но как сделать это так, чтобы не настраивать БД перед каждым тестом. PostgreSQL позволяет вам использовать существующую базу данных в качестве шаблона при создании новой. В таком случае схема базы данных сохранится и вам не придётся выполнять десяток миграций перед каждым тестом. Т.е. я предлагаю вам с помощью testcontainers перед тестами создать один контейнер с PostgreSQL, после чего накатить на него все миграции с необходимыми таблицами. После чего с этой базой данных вы НЕ взаимодействуете, а перед каждым тестом создаёте новую, используя эталонную базу как шаблон. Вы так же можете просто руками перед тестами поднимать в докере одну СУБД, но не хотелось бы каждый раз думать об этом перед запуском тестов.

Простейший пример того, как это может выглядеть (без testcontainers) можно глянуть в репозитории: https://github.com/pantafive/demo-repository-test/blob/main/app/database_test.go#L43


🔹 Как достигается масштабируемость эфириума и что нас ждёт в будущем?

Ежедневно в сети Ethereum проходит около миллиона транзакций, что является причиной относительно высокой их стоимости (~0.5$).

Как ethereum уже сейчас повышает пропускную способность сети без ущерба для децентрализации и безопасности?
Решения, дающие нам данную возможность называют Layer 2 (собирательный термин для решений масштабирования) — отдельный блокчейн, расширяющий Ethereum. Уровень 2 снимает нагрузку с уровня 1 (основной сети), позволяя ему стать менее перегруженным.
Одним из типов Layer 2 являются Rollups.
Изменения, выполняемые транзакциями в Rollup сети собираются в одну транзакцию, после чего отправляются в Layer 2, разделив комиссию между всеми отправителями транзакций внутри Rollup.

Но Rollups существовали не всегда... Изначально планировалось реализовать шардинг блокчейна (разбить весь блокчейн на небольшие куски, чтобы за валидаторами стояла часть сети, а не вся сеть) ещё до перехода на PoS, однако Layer 2 решения развивались быстрее и мы получили то, что получили.

Но, текущего решения не достаточно. Судя по ethereum roadmap цена и количество транзакций остаётся серьёзной проблемой для сети и её исправление является первостепенной задачей. Авторы планируют достичь цены транзакции менее чем $0.001

Главной причиной, по которой транзакции в Rollaps стоят своих денег (заявлено около 90% стоимости) является необходимость Ethereum хранить Rollap данные. К концу 2023 года планируется реализовать временное хранилище и перенести эти данные в него. Данные из временного хранилища могут быть удалены в случае ненадобности, что сделает выполнение подобных транзакций дешевле. Но rollup данные будут храниться системами, которые ими манипулируют: биржи, системы индексирования и т.д. Данные rollup не единственное, что может быть перенесено во временное хранилище, таким образом планируется достичь более чем 100-кратной масштабируемости сети.

Из интересного, нас так же ждут stateless клиенты, но об этом уже когда-нибудь в другой раз, может быть...


🗂 Автоматическое тестирование логирования

Честно говоря, раньше я даже и не задумывался о том, что журналирование может быть необходимо тестировать...

Каждый из нас может оказаться в ситуации, когда требуется реализовать логи каких-то критических (с точки зрения бизнеса) мест в коде. Такие логи могут быть особенно полезны для службы поддержки. На них могут строиться какие-то аналитические сервисы и прочие вещи, что требует их защиты от изменений в будущем... Подобного вида логи отличаются от привычных каждому логов, помогающих реализовывать отладку проекта. Так у нас появляется понятие двух различных типов логирования: служебные (для бизнесса) и диагностические (для программиста).
Диагностические логи являются деталью имплементации и в целом-то тестироваться и не должны. Однако со служебными логами дела обстоят куда интереснее. Служебные логи — требование бизнеса, а следовательно они являются наблюдаемым поведением. В этом случае наша главная задача — гарантировать их неизменность.

Система журналирования является внешней неуправляемой зависимостью. Поэтому мы будем заменять её моком при тестировании, и в интеграционном тесте проверять взаимодействие нашего приложения с этой зависимостью.
Для того, чтобы что-то мокнуть, нам нужен интерфейс. Более того, вам стоит создать доменный логер, у которого будут чётко определенные бизнес-методы, например DomainLogger.UserEmailHasChanged, а не какой-нибудь Printf. Создав интерфейс для доменного логера, вы можете без особых проблем создать мок и использовать его в тестах для проверки взаимодействия.

Если вы создаёте логи в контроллере, всё в вашей жизни прекрасно. Но что если вам вдруг понадобилось создать служебный лог внутри слоя бизнес-логики? Наличие внепроцессорных зависимостей внутри домена переусложняет его и делает невозможным его юнит-тестирование. Для решения данной проблемы стоит все логи создавать в контроллере. Для того, чтобы вынести служебное логирование из бизнес слоя в контроллер, можно прибегнуть к событиям. Внутри доменного слоя вы создаёте события, а внутри контроллера проходитесь по ним и обрабатываете (логируете). Таким образом вы имеете возможность легко проверить события, полученные доменом, в юнит тестах, а в интеграционных тестах проверить их обработку контроллером. Диагностическое логирование в слое бизнес логики лучше вовсе опустить.

За более подробным объяснением с примерами кода обращайтесь к главе 8.6 книги Владимира Хорикова "Принципы юнит-тестирования"


🏗 Как неправильное применение патернов может принести больше вреда чем пользы?

Я думаю многие слышали о SOLID и его OCP. Часто люди, прочитав "чистую архитектуру" обмазываются интерфейсами под предлогом "так завещал дядя Боб". Пожалуйста, не нужно делать вещей, которые вам в данный момент не нужны. Вот статья Владимира Хорикова на эту тему: OCP vs YAGNI

DRY, думаю, впитывается у программистов с молоком матери. Многие любят им злоупотреблять и ссылаться на него даже там, где это абсолютно не нужно. Про это есть статья Miłosz Smółka из Three Dots Labs: Things to know about DRY (aka When to stay away from DRY)

893 0 10 6 10

📖 Логгирование в GoLang

Ещё в июне вышел релиз кандидат GoLang 1.21. Зацепила в нём меня одна вещь: log/slog. Да, в Go добавляют пакет для человеческого логгирования.

Для тех, кто слабо знаком в го, уточню, что до этого момента в стандартной библиотеке Go был логгер. Но, грубо говоря, он содержал пару методов для вывода. Естественно, мало для какого серьёзного приложения этого было бы достаточно, поэтому появилось множетсов библиотек. Я обычно использовал zerolog.

Лично мне не хватало своеобразной стандартизации. Как, например, в питоне. Логгер в стандартной библиотеке питона очень мощный и вполне пригоден к использованию без каких-либо библиотек.

Теперь и в Go будет логгер, который позволит писать подробные логи. В стандартной библиотеке уже реализован JSON логгер (помимо обычного), что не мало важно, ибо json логги используются очень часто (мне кажется даже чаще обычных текстовых) для отправки в бд и агрегирования через какую-нибудь графану.

Slog прост и минималистичен, при этом очень гибок и настраеваем. В общем-то, всё как мы в любим в Go. Осталось дать немного времени камьюнити реализовать красивых хендлеров, как например этот, который делает очень красивые человеко-читаемые логи в консоли, мидлварей для логгирования запросов, как здесь и прочих приколов


👨‍💻 Workspaces в GoLang

Go 1.18 добавляет в Go режим рабочего пространства, позволяющий работать над несколькими модулями одновременно. Т.е. у вас есть несколько "проектов", каждый из которых имеет свой индивидуальный go.mod. Один проект может импортировать модули из другого. Это довольно удобно, если вам нужно взять библиотеку и, расширив её API, потестить на рядом лежащем проекте, пример при контрибьютинге.

Подробнее о рабочих пространствах можно почитать в блоге разработчиков: https://go.dev/blog/get-familiar-with-workspaces

Сюда же тутор по использованию воркспейсов: https://go.dev/doc/tutorial/workspaces


🇬🇧 Как я развиваю свой английский

Программирование это конечно хорошо, но далеко на программировании в наших странах без английского не уедешь. Думаю нет необходимости объяснять почему знание английского так важно. Почти все знания в программировании выше начального уровня на английском. Вакансии требуют знания английского.

Недавно я перешёл к новому подходу в обучении. Я скачал на свой смартфон приложение 2books, которое позволяет удобно читать английские книги. Когда вы находите в тексте новое для себя слово, вы можете прочесть перевод в нескольких вариантах, объяснение на английском его значения, перейти в разные приложения для перевода и словари. Но с таким успехом можно было просто через переводчик читать, как я делал раньше. Однако здесь есть серьёзная проблема. Вы один раз перевели слово, контекст усвоили и забыли. Думаю, многие знают про то, что память условно делится на долговременную и рабочую. Поэтому, для того, чтобы слова действительно запоминались, их необходимо повторять, чтобы они остались в долгосрочной памяти. Для этого в приложении 2books я добавляю слова в "словарь" для изучения, импортирую его в Anki и учу слова внутри anki.

К сожалению, не весь функционал 2books доступен бесплатно. С покупкой премиума ($9 пожизненный) вы получаете возможность загрузить любую epub книгу в приложение, а не выбрать из предложенных на сайте (таким образом очень удобно читать книги по программированию, Learning DDD от Vlad Khononov, например, не прибегая к переводчикам и тд). Возможность импорта словаря так же не доступна без премиума, а словарь ограничен сотней слов. Хотя для повторения слов есть свой простой механизм.

2.3k 1 38 47 26

🦾🧠 Курсы по AI от Microsoft и Google

На волне хайпа, связанного с нейронными сетями, в последнее время, конечно вырос спрос на специалистов в этой области. А спрос пораждает предложение.

Поэтому вот вам курсы от гугла
и от майкрасофт


📖 О вреде оператора Go To

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

Но нет, этот пост вовсе не об этом. Здесь я хочу предложить вам прикоснуться к истории программирования. Эдсгер Вайб Дейкстра — человек, который изобрёл структурное программирование. И "О вреде оператора Go To" называется его статья 1968 года. На тот момент goto использовался повсеместно и именно Дейкстра показал, что его использование негативно сказывается на наших программах. Благо ещё до него Бём и Якопини доказали, что и без goto прекрасно программистам живётся.

Теорема Бёма-Якопини

Перевод оригинальной статьи "О вреде оператора Go To"


Видео недоступно для предпросмотра
Смотреть в Telegram
Удивляет, как быстро нейронные сети ворвались в нашу повседневность. Теперь даже в Notion добавили поддержку ИИ. Он умеет продолжать текст, упрощать, сокращать, удлинять и делать выводы. Весьма удобно при написании конспектов.


😩 Университетская аттестация в самом разгаре. Но вместо усердной подготовки мы изучаем блокчейн. Как бы то ни было, будет что рассказать сослуживцам. А пока расскажу вам...

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

Автор Tal Kol в своей статье How a Blockchain Can Help You on a Deserted Island описал, как блокчейн поможет решить данный вопрос.

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


👩‍💻 Dockerfile за который не стыдно

Не знаю, как много питонистов до сих пор на меня подписаны, но мне на глаза попалась интересная статья о лучших практиках Docker для разработчиков на Python:
https://testdriven.io/blog/docker-best-practices/#order-dockerfile-commands-appropriately

Здесь и про multi-stage building, и про образы с интерпритатором со сравнением их размеров (alpine, slim, slim-buster, buster), про права внутри контейнера и ещё много чего интересного

Показано 20 последних публикаций.