Нияз Хадимуллин | Ментор по GO – Telegram
Нияз Хадимуллин | Ментор по GO
1.24K subscribers
138 photos
1 video
35 links
Авторский канал ментора Нияза про Go, базы данных и разработку

Если хочешь записаться на моё менторство и начать получать офферы, не стесняйся писать мне https://mentor-niyaz.ru
Download Telegram
В Go отсутствует традиционная система исключений, как в других языках, таких как Python или Java. Как вы будете обрабатывать ошибки в Go, и каким образом можно реализовать механизм аналогичный try-catch? Можете ли вы привести пример, где использование panic и recover будет уместным, и объяснить, почему этот подход не следует использовать повсеместно?

❗️ В Go ошибки обрабатываются с помощью возвращаемых значений, что отличает этот язык от других, где используется система исключений. Стандартная практика заключается в том, чтобы возвращать пару значений (result, error) из функции и проверять наличие ошибки при каждом вызове.

🌟 Пример:

package main

import (
"errors"
"fmt"
)

func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("деление на ноль")
}
return a / b, nil
}

func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Ошибка:", err)
} else {
fmt.Println("Результат:", result)
}
}

🌟 В этом примере функция divide возвращает ошибку, если деление на ноль невозможно, и вызывающая функция проверяет эту ошибку перед использованием результата.

❗️ Механизм panic и recover

💡 В Go также есть механизмы panic и recover, которые можно использовать для управления критическими ошибками, аналогично try-catch в других языках. panic вызывает ошибку и завершает текущую функцию, а recover позволяет перехватить панику и продолжить выполнение программы.

🌟 Пример использования:

package main

import "fmt"

func riskyFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Поймана ошибка:", r)
}
}()
fmt.Println("Выполняется riskyFunction...")
panic("Что-то пошло не так!")
fmt.Println("Этот код не будет выполнен")
}

func main() {
fmt.Println("Начало программы")
riskyFunction()
fmt.Println("Конец программы")
}

🌟 В этом примере вызов panic завершает выполнение функции, но благодаря recover мы перехватываем ошибку и корректно завершаем программу.

💡 Когда использовать panic и recover?

Механизмы panic и recover следует использовать только в следующих случаях:

🌟 Критические ошибки: Ситуации, когда продолжение работы программы невозможно или может привести к нестабильности, например, повреждение данных или нарушение инвариантов.

🌟 Реализация сложной логики: Например, в парсерах, где обнаружение ошибок на каждом шаге обработки затруднительно, или в задачах, требующих точного контроля за выполнением (например, при реализации транзакционных систем)
Please open Telegram to view this post
VIEW IN TELEGRAM
🤩31🎉2419🔥14👍8🥰8👏2
🎯 С завтрашнего дня в канале запускается ежедневная викторина по Golang и базам данных.

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

💡 Это отличный способ не только проверить свои знания, но и получить развёрнутое понимание по каждой теме. До завтра!
👍35🎉26🤩24🔥14🥰125👏1
День 1️⃣

Сегодня обсудим две важные темы: интерфейсы в Go и команду EXPLAIN ANALYZE в Postgres.

Поехали! 🚀
🤩37🎉28👍132🔥2
Рунет умер, но вы держитесь. А у вас как дела?

👍 — да, тоже всё упало
⚡️ — нет, всё работает
👍47🤩18🎉1612🥰6🔥51👏1
Рунет ожил! Техдолга по сегодняшним постам не будет 🎉

Если у вас тоже всё вернулось в норму, ставьте 👍
👍51🤩30🔥20🎉195
Нияз Хадимуллин | Ментор по GO
Вопрос: Что происходит, когда интерфейс в Go сравнивается с nil?
Итак, уважаемые знатоки, внимание, вопрос... Что происходит, когда интерфейс в Go сравнивается с nil?

📊 Лишь 25% проголосовавших в опросе выбрали верный вариант!

⁉️ Правильный ответ: Сравнение возвращает true, только если и тип, и значение интерфейса равны nil.

💡 Почему так?

Интерфейс в Go — это структура, которая состоит из двух компонентов:
1. Указатель на тип (type): Содержит информацию о типе данных, который реализует интерфейс. Например, *MyStruct.
2. Указатель на значение (value): Содержит конкретное значение этого типа. Например, указатель на структуру или nil.

package main

import "fmt"

type MyInterface interface {
Method()
}

type MyStruct struct{}

func (m *MyStruct) Method() {
fmt.Println("Oh, hi, Mark!")
}

func main() {
var i MyInterface // интерфейс не инициализирован, поэтому его тип и значение равны nil
fmt.Println(i == nil) // true

var s *MyStruct = nil // указатель на структуру равен nil
i = s // интерфейс содержит тип *MyStruct, значение nil
fmt.Println(i == nil) // false
}


‼️ Таким образом, когда вы сравниваете интерфейс с nil, Go проверяет оба компонента и, если хотя бы один из них не равен nil, интерфейс не считается nil.
👍39🎉26🤩22🔥103🥰1
Нияз Хадимуллин | Ментор по GO
Вопрос: Что показывает команда EXPLAIN ANALYZE в Postgres, в отличие от EXPLAIN?
Итак, уважаемые знатоки, внимание, вопрос... Что показывает команда EXPLAIN ANALYZE в Postgres, в отличие от EXPLAIN?

📊 86% проголосовавших в опросе выбрали верный вариант!

⁉️ Правильный ответ: План выполнения и статистику по времени выполнения.

💡 Почему так?

1. EXPLAIN: Показывает, как Postgres планирует выполнить запрос, включая операции (например, сканирование таблиц, использование индексов) и оценку стоимости. Запрос при этом не выполняется. Например:

Запрос
EXPLAIN SELECT * FROM users WHERE age > 34;


Вывод
Seq Scan on users  (cost=0.00..16.00 rows=420 width=34)
Filter: (age > 34)


2. EXPLAIN ANALYZE: Выполняет запрос и добавляет фактические данные: время выполнения, количество обработанных строк и другие метрики. Например:

Запрос
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 24;


Вывод
Seq Scan on users  (cost=0.00..15.00 rows=500 width=36) (actual time=0.034..0.300 rows=450 loops=1)
Filter: (age > 24)
Rows Removed by Filter: 50
Planning Time: 0.052 ms
Execution Time: 0.322 ms


Важно понимать, хоть EXPLAIN и не выполняет запрос, а только показывает план, EXPLAIN ANALYZE может быть затратным для тяжелых запросов, так как он выполняет их полностью.

‼️ Таким образом, EXPLAIN используется, когда нужно быстро понять, как Postgres планирует выполнить запрос, без затрат на его выполнение. EXPLAIN ANALYZE же используется, когда нужно точно определить узкие места в производительности запроса, особенно для тяжелых запросов.
👍30🤩25🎉1513🥰5🔥4👏4
День 2️⃣

На ночь глядя поговорим о WaitGroup в Go и уровнях изоляции транзакций в базах данных.

Сегодня у нас параллельное программирование и надёжность данных, поехали 🚀
🔥32👍2520🥰20👏13
Нияз Хадимуллин | Ментор по GO
Что произойдет, если вызвать Done() у WaitGroup больше раз, чем Add()?
Итак, уважаемые разработчики, внимание!

📊 75% проголосовавших выбрали правильный ответ.

⁉️ Правильный ответ: Возникнет паника с ошибкой.

💡 Давайте детально разберем почему:

WaitGroup в Go - это счетчик, который следит за количеством активных горутин. Когда счетчик достигает нуля, WaitGroup разблокирует основную горутину. Рассмотрим как это работает:

1. Add(n) увеличивает счетчик на n
2. Done() эквивалентен Add(-1)
3. Wait() блокируется, пока счетчик не станет равным 0

Что происходит при избыточных Done():
package main

import (
"sync"
"fmt"
)

func main() {
var wg sync.WaitGroup
wg.Add(1) // счетчик = 1

go func() {
defer wg.Done() // счетчик = 0
}()

wg.Done() // счетчик = -1, ПАНИКА!
wg.Wait()
}


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

Как правильно использовать:
var wg sync.WaitGroup

// Правильно:
wg.Add(n) // добавляем n горутин
// запускаем n горутин
for i := 0; i < n; i++ {
go func() {
defer wg.Done() // каждая горутина вызывает Done() один раз
}()
}
wg.Wait()


‼️ Итого: паника при отрицательном счетчике - это механизм защиты, который помогает обнаружить ошибки синхронизации. Всегда следите за балансом Add() и Done() вызовов!
🔥32👏2417👍15🥰5
Какой уровень изоляции транзакций НЕ защищает от проблемы Lost Update?
Anonymous Quiz
84%
READ COMMITTED
7%
REPEATABLE READ
3%
SERIALIZABLE
6%
Все защищают
37🔥33👍19🥰14👏5
Нияз Хадимуллин | Ментор по GO
Какой уровень изоляции транзакций НЕ защищает от проблемы Lost Update?
📊 93% проголосовавших выбрали верный ответ.

⁉️ Правильный ответ: READ COMMITTED не защищает от Lost Update.

💡 Почему?

Lost Update - это ситуация, когда 2 транзакции параллельно читают и обновляют одни и те же данные. Изменения одной транзакции перезаписывают изменения другой, в результате чего теряется обновление первой транзакции.

Потеря данных происходит из-за того, что READ COMMITTED позволяет читать только зафиксированные данные, но не запрещает параллельное изменение прочитанных данных, из-за чего вторая транзакция затирает первую.

READ COMMITTED. Не защищает от параллельных изменений. Транзакции могут видеть изменения, сделанные другими транзакциями, что может привести к потере обновлений.
REPEATABLE READ. Транзакции видят снимок данных на момент начала, используя версионирование (MVCC). Первая транзакция получит ошибку при COMMIT, если другая транзакция пытается изменить ту же строку.
SERIALIZABLE. Полная изоляция транзакций. Выполнение становится последовательным, предотвращая все проблемы с конкурентностью.

‼️ Итог: READ COMMITTED - это компромисс между производительностью и изоляцией. Для защиты от Lost Update используйте более высокие уровни изоляции или явные блокировки!
👏6563🔥58👍56🥰32
🌠 Предлагаем вам бонусный пост на ночь. Давайте поговорим о том, как multi-stage builds в Docker могут значительно улучшить ваши процессы сборки и развертывания.

📦 Multi-stage builds в Docker - это техника, позволяющая использовать несколько этапов сборки в одном Dockerfile. Каждый этап может использовать разные базовые образы и инструменты, а затем копировать только необходимые артефакты в финальный образ.

💡 Пример:

# Первый этап. Сборка приложения
FROM golang:1.23 AS builder

WORKDIR /app

COPY . .

RUN go build -o myapp .

# Второй этап. Создание минимального образа
FROM alpine:latest

WORKDIR /root/

COPY --from=builder /app/myapp .

CMD ["./myapp"]


⁉️ Почему это лучше?

1. Уменьшение размера образа. В финальный образ попадают только необходимые артефакты, что значительно уменьшает его размер.
2. Улучшение безопасности. В конечном образе отсутствуют инструменты и зависимости, используемые только на этапе сборки, что снижает поверхность атаки.
3. Упрощение управления зависимостями. Каждый этап может использовать свои собственные зависимости и инструменты, что упрощает управление ими.
4. Быстрая сборка. Использование кэша Docker для каждого этапа позволяет ускорить процесс сборки, так как повторные сборки используют уже собранные артефакты.

‼️ Multi-stage builds в Docker - это мощный инструмент для оптимизации процесса сборки образов. Он позволяет создавать более легкие, безопасные и быстрые образы, что особенно важно для развертывания в облачных средах и контейнерных оркестраторах, таких как Kubernetes.

❤️ Если вам понравился этот пост, ставьте реакции, и мы будем выкладывать ещё больше подобного контента!
👍68🔥57🥰4936👏7
День 3️⃣

Сегодня разбираем работу с rune и UTF-8 в Go, а также все виды JOIN в SQL.

Поехали 🚀
👏2726👍25🔥15🥰15