Golang вопросы собеседований
13.5K subscribers
631 photos
3 videos
1 file
433 links
@notxxx1 - админ

@Golang_google - Golang для разработчиков

@itchannels_telegram - 🔥лучшие из ит

@golangl - chat

@golangtests - golang tests

@golang_jobsgo - go chat jobs

@ai_machinelearning_big_data - AI

@data_analysis_ml

РКН: clck.ru/3FmtKd
加入频道
⚡️Маст-хэв список для программистов, каналы с последними книжными новинками, библиотеками, разбором кода и актуальной информацией, связанной с вашим языком программирования.
Лучший способ получать свежие обновлении и следить за трендами в разработке.

Python: t.me/pythonl
C#: t.me/csharp_ci
C/C++/ t.me/cpluspluc
Машинное обучение: t.me/ai_machinelearning_big_data
Data Science: t.me/data_analysis_ml
Devops: t.me/devOPSitsec
Go: t.me/Golang_google
Базы данных: t.me/sqlhub
Rust: t.me/rust_code
Javascript: t.me/javascriptv
React: t.me/react_tg
PHP: t.me/phpshka
Android: t.me/android_its
Мобильная разработка: t.me/mobdevelop
Linux: t.me/+A8jY79rcyKJlYWY6
Big Data: t.me/bigdatai
Хакинг: t.me/linuxkalii
Java: t.me/javatg

💼 Папка с вакансиями: t.me/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: t.me/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: t.me/addlist/eEPya-HF6mkxMGIy

📕 Бесплатные Книги для программистов: https://yangx.top/addlist/YZ0EI8Ya4OJjYzEy

🎞 YouTube канал: https://www.youtube.com/@uproger

😆ИТ-Мемы: t.me/memes_prog

🇬🇧Английский: t.me/english_forprogrammers
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Расскажите про шаблон проектирования Стратегия? Приведите пример реализации на Go.

Стратегия (. Strategy) — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путём определения соответствующего класса. Шаблон Стратегия (. Strategy) позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.

Реализация шаблона представлена на кратинке.

В примере у объекта toy есть DialogueReciter, который является интерфейсом, и это важно, поскольку мы хотим, чтобы поведение объекта-игрушки можно было изменять во время выполнения программы. Если бы это был не интерфейс, а конкретный тип, то мы могли бы присвоить объекту-игрушке только поведение этого конкретного типа.

Поведение для объекта toy определено в интерфейсе, а фактическая реализация поведения, например, Recite, выполняется отдельно различными конкретными типами, например, SpiderMan, SuperMan и BatMan.

Если бы поведение toy было закодировано в конкретном типе, то мы были бы заблокированы на использование только этого конкретного поведения.

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

Стратегия позволяет алгоритму изменяться независимо от
клиентов, которые его используют".

Пишите свой пример реализации шаблона в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Experimenting with project templates

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

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

Начните с установки gonew с помощью go install:

$ go install golang.org/x/tools/cmd/gonew@latest

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

$ gonew golang.org/x/example/helloserver example.com/myserver
$ cd ./myserver


А затем вы можете читать и редактировать файлы в ./myserver для настройки.

Для начала мы написали два шаблона:

hello: Инструмент командной строки, печатающий приветствие, с флагами настройки.
helloserver: HTTP-сервер, передающий приветствия.

https://go.dev/blog/gonew

Еще примеры: https://github.com/GoogleCloudPlatform/go-templates

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Расскажите про внутреннее устройство mutex

Мьютекс гораздо более комплексная структура по сравнению с atomic, чем может показаться на первый взгляд. Прежде всего у нас есть два вида мютексов: sync.Mutex и sync.RWMutex. У каждого из них есть методы Lock и Unlock, у RWMutex есть дополнительно метод RLock (блокирование для чтения). В обоих типах методы Lock довольно долгие по сравнению с атомиками.

У Mutex код метода Lock включает два вида блокировки. Первый вариант - это когда удается захватить не заблокированный мьютекс, второй вариант довольно долгий и он запускается если мьютекс заблокирован:

// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// Slow path (outlined so that the fast path can be inlined)
m.lockSlow()
}

Первый вариант довольно быстрый и включает в себя одну atomic операцию. Второй вариант включает довольно много кода и здесь подробно разбираться не будет: там также используются атомики в процессе блокировки, но очевидно, что он исполняется еще дольше первого варианта (Fast path).

У RWMutex код метода Lock включает в себя вызов метода Lock структуры Mutex:

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// First, resolve competition with other writers.
rw.w.Lock()
// Здесь пропущена часть кода ...
}
}

Да и сама структура RWMutex включает в себя структуру Mutex как одно из полей:

type RWMutex struct {
w Mutex // held if there are pending writers
// Здесь пропущена часть кода ...
}


Метод RLock у RWMutex гораздо быстрее и содержит меньше кода, но тем не менее не быстрее атомика, который там задействован:

func (rw *RWMutex) RLock() {
// Здесь пропущена часть кода ...
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
//


📌Вопрос что производительнее атомики или мьютексы?

Напишите свой ответ в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Для чего нужен тип http.ResponseController?

http.ResponseController:
Позволяет переопределять ваши общесерверные таймауты/дедлайны чтения и записи новыми для каждого отдельного запроса.

Шаблон использования интерфейсов http.Flusher и http.Hijacker стал более понятным и менее сложным. Нам больше не нужны никакие утверждения типов!

Он делает проще и безопаснее создание и использование пользовательских реализаций http.ResponseWriter.

Таймауты для отдельных запросов
http.Server Go имеет настройки ReadTimeout и WriteTimeout, которые вы можете использовать для автоматического закрытия HTTP-соединения, если время, затраченное на чтение запроса или запись ответа, превышает какое-либо фиксированное значение. Эти настройки являются общесерверными и применяются ко всем запросам, независимо от обработчика или URL.

С появлением http.ResponseController можно использовать методы SetReadDeadline() и SetWriteDeadline(), чтобы ослабить или, наоборот, ужесточить эти настройки для каждого конкретного запроса в зависимости от ваших потребностей. Например:

func exampleHandler(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)

// Установим таймаут записи в 5 секунд.
err := rc.SetWriteDeadline(time.Now().Add(5 * time.Second))
if err != nil {
// Обработка ошибки
}

// Делаем здесь что-нибудь...

// Записываем ответ как обычно
w.Write([]byte("Done!"))
}

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

Несколько деталей, о которых стоит упомянуть:

Если вы установите очень короткий общесерверный таймаут, и этот таймаут будет достигнут до того, как вы вызовете SetWriteDeadline() или SetReadDeadline(), то они не возымеют никакого эффекта. Общесерверный таймаут в этом случае побеждает.

Если базовый http.ResponseWriter не поддерживает установку таймаутов для отдельных запросов, то вызов SetWriteDeadline() или SetReadDeadline() вернет ошибку http.ErrNotSupported.

Теперь можено отменять общесерверный таймаут для отдельных запросов, передав обнуленную структур time.Time в SetWriteDeadlin() или SetReadDeadline(). Например:

rc := http.NewResponseController(w)
err := rc.SetWriteDeadline(time.Time{})
if err != nil {
// Обработка ошибки
}

Интерфейсы Flusher и Hijacker
Тип http.ResponseController также делает более удобным использование «опциональных» интерфейсов http.Flusher и http.Hijacker. Например, до Go 1.20, чтобы отправить данные ответа клиенту, вы могли использовать кода следующего вида:

func exampleHandler(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
if !ok {
// Обработка ошибки
}

for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Write %d\n", i)
f.Flush()

time.Sleep(time.Second)
}
}

Теперь можно сделать это так:

func exampleHandler(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)

for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Write %d\n", i)
err := rc.Flush()
if err != nil {
// Обработка ошибки
}

time.Sleep(time.Second)
}
}
Шаб
лонный код перехвата (hijacking) соединения аналогичен:

func (app *application) home(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)

conn, bufrw, err := rc.Hijack()
if err != nil {
// Обработка ошибки
}
defer conn.Close()

// Делаем здесь что-нибудь...
}

Опять же, если ваш базовый http.ResponseWriter не поддерживает flush или перехват соединения, то вызов Flush() или Hijack() в http.ResponseController также вернет ошибку http.ErrNotSupported.

Теперь также проще и безопаснее создавать и использовать пользовательские реализации http.ResponseWriter, которые поддерживают flush и перехват соединения.

Приведите пример с кодом реализации своей http.ResponseWriter в комментариях 👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Как можно оценить работу кэша? Что можно кэшировать?

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

Объем памяти, выделенной под кэш. Это базовый показатель, по которому можно судить, сколько используется ресурсов

RPS чтения/записи – количество операций чтения/записи за единицу времени. В обычной ситуации количество операций чтения должно быть в разы больше количества операций записи. Обратное соотношение свидетельствует о проблемах в работе кэша

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

Hit rate – процент извлечения данных из кэша. Чем он ближе к 100%, тем лучше. Этот параметр буквально определяет то, насколько наш кэш полезен и эффективен

Expired rate – процент удаления записей по истечении TTL. Этот показатель помогает обнаружить проблемы с производительностью, вызванные большим количеством записей с одновременно истекшим TTL

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

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

Все данные можно условно разделить на 3 группы по частоте изменений:

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

Пример: ошибки (кэширование ошибок может быть настолько важным, что мы посвятили ему целую главу ближе к концу статьи)

Меняются нечасто. Такие данные изменяются в течение минут, часов, дней. Именно в этом случае вы чаще всего задаетесь вопросом “Стоит ли мне кэшировать это?”

Примеры: списки товаров на сайте, описания товаров

Меняются крайне редко или не меняются никогда. Такие данные меняются в течение недель, месяцев и лет. В этом случае данные можно спокойно кэшировать. НО! Ни в коем случае нельзя усыплять бдительность верой в то, что какие-либо данные никогда не изменятся. Рано или поздно они изменятся, поэтому всегда выставляйте всем данным разумный TTL. ВСЕГДА!

Напишите какие стратегии работы с кэшем вы знаете в комментариях 👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Как управлять сборщиком мусора в GO? Приведите пример с кодом.

Существует параметр, который позволяет управлять сборщиком мусора в Go - это переменная окружения GOGC или ее функциональный аналог SetGCPercent из пакета runtime/debug.

Параметр GOGC определяет процент новой необработанной памяти кучи от живой памяти, при достижении которого будет запущена сборка мусора. Значение GOGC по умолчанию равно 100, что означает, что сборка мусора будет запущена, когда объем новой памяти достигнет 100% от объема живой памяти

Рассмотрим пример программы и отследим изменение размера кучи с помощью инструмента go tool trace. Для запуска программы используем версию Go 1.20.1.

В данном примере, функция performMemoryIntensiveTask использует большое количество памяти размещаемой в куче. Данная функция запускает обработчик с размером очереди NumWorker и количество задач равное NumTasks.

package main

import (
"fmt"
"os"
"runtime/debug"
"runtime/trace"
"sync"
"time"
)

const (
NumWorkers = 4 // Количество воркеров.
NumTasks = 500 // Количество задач.
MemoryIntense = 10000 // Размер память затратной задачи (число элементов).
)

func main() {
// Запись в trace файл.
f, _ := os.Create("trace.out")
trace.Start(f)
defer trace.Stop()

// Установка целевого процента сборщика мусора. По умолчанию 100%.
debug.SetGCPercent(100)

// Очередь задач и очередь результата.
taskQueue := make(chan int, NumTasks)
resultQueue := make(chan int, NumTasks)

// Запуск воркеров.
var wg sync.WaitGroup
wg.Add(NumWorkers)
for i := 0; i < NumWorkers; i++ {
go worker(taskQueue, resultQueue, &wg)
}

// Отправка задач в очередь.
for i := 0; i < NumTasks; i++ {
taskQueue <- i
}
close(taskQueue)

// Получение результатов из очереди.
go func() {
wg.Wait()
close(resultQueue)
}()

// Обработка результатов.
for result := range resultQueue {
fmt.Println("Результат:", result)
}

fmt.Println("Готово!")
}

// Функция воркера.
func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()

for task := range tasks {
result := performMemoryIntensiveTask(task)
results <- result
}
}

// performMemoryIntensiveTask функция требующая много памяти.
func performMemoryIntensiveTask(task int) int {
// Создание среза большого размера.
data := make([]int, MemoryIntense)
for i := 0; i < MemoryIntense; i++ {
data[i] = i + task
}

// Имитация временной задержки
time.Sleep(10 * time.Millisecond)

// Вычисление результата.
result := 0
for _, value := range data {
result += value
}
return result
}

Для трассировки работы программы результат записывается в файл trace.out:

// Запись в trace файл.
f, _ := os.Create("trace.out")
trace.Start(f)
defer trace.Stop()

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

Hidden text
Обратите внимание, что точные детали и возможности инструмента go tool trace могут варьироваться в разных версиях Go, поэтому рекомендуется обратиться к официальной документации для получения более подробной информации о его использовании в вашей конкретной версии Go.

 Статья

@golang_interview
👣 Что выведет код ?

package main

import "fmt"

func sum(a, b any) any {
return a + b
}

func main() {
fmt.Println(sum(2, 3))
}


📌Ответ

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Задача. Нам дан связный список целых чисел, отсортированных в порядке возрастания. Наша задача – удалить те узлы, значения которых фигурируют в списке более одного раза. Нужно не только создать список уникальных значений, но и полностью удалить все прочие узлы с этими значениями.

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

Для этого во многих рекурсивных функциях предусмотрены дополнительные аргументы, действующие в качестве флагов в тех ситуациях, когда на предыдущем или последующем уровне выполняются определённые условия.

В контексте поставленной задачи
давайте продумаем каждую ситуацию, в которой может оказаться узел, и как она соотносится с фактом потенциального дублирования конкретного значения:
•Недублирующийся узел указывает на дублирующийся узел
•Дублирующийся узел указывает на дублирующийся узел
•Недублирующийся узел указывает на дублирующийся узел
•Дублирующийся узел указывает на недублирующийся узел


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

Но задумайтесь, что случится, если вы достигнете конечного (nil), имея строку дублей. Допустим, мы работаем с 2 -> 3 -> 3 -> nil, давайте разберём вышеприведённые случаи узел за узлом:

2 -> 3

2 не равно 3, поэтому здесь мы никаких изменений в узел вносить не будем и вновь вызовем функцию, на этот раз передав ей в качестве аргумента узел '3'.

3 -> 3
a b


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

В данном случае мы можем перезаписать указатель 'a', так, чтобы он стал 'b' – в сущности, удалив 'a'. Теперь снова вызываем функцию, на этот раз передав ей узел 'b'.

3 -> nil
b

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

Вот что можно сделать для решения этой проблемы: мы можем не только полагаться на флаг, сигнализирующий о дубле и посылаемый вниз по пути с рекурсивным изменением. Вдобавок давайте создадим флаг, сигнализирующий, не всплыли ли мы вверх с того уровня, где было дублирующееся значение, смежное с nil. Таким образом, у нас сохранится уловка, позволяющая добраться до nil, и вместе с тем мы сможем безопасно перезаписывать указатели. А если бы мы достигли уровня, имея этот флаг со значением true, мы переключаем узлы и устанавливаем его в false – так сообщается, что дубля в конце у нас уже нет.

📌Решение

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Задача: Конференц-залы II.

Сложность задачи: Средняя

Условие задачи:
Дан массив интервалов времени проведения совещаний, intervals, где intervals[i] = [start(i), end(i)]. Найдите минимальное требуемое количество конференц-залов.

Пример:
Ввод: intervals = [[0,30],[5,10],[15,20]]
Вывод: 2

Ввод: intervals = [[7,10],[2,4]]
Вывод: 1

📌Решение

Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 Задача: Самые дешевые авиабилеты в пределах K остановок

Условие: Имеется n городов, соединенных некоторым количеством рейсов. Вам дан массив flights, где flights[i] = [fromi, toi, pricei] означает, что существует рейс из города fromi в город toi со стоимостью pricei.

Также даны три целых числа src, dst и k, возвращаем самую дешевую цену из src в dst с не более чем k остановками. Если такого маршрута не существует, возвращается -1.

Пример:

Ввод:
n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Вывод: 700

Ввод: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
Вывод: 200

Решение

Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 Задача: Лучшая команда, в которой нет конфликтов

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

Однако в баскетбольной команде не должно быть конфликтов. Конфликт возникает, если младший игрок набрал строго больше очков, чем старший. Конфликт не возникает между игроками одного возраста.

Задав два списка, scores и ages, в которых каждый scores[i] и ages[i] представляет собой счет и возраст i-го игрока соответственно, верните наибольший общий балл среди всех возможных баскетбольных команд.

Пример:

Ввод:
scores = [1,3,5,10,15], ages = [1,2,3,4,5]
Вывод: 34

Ввод: scores = [4,5,6,5], ages = [2,1,2,1]
Вывод: 16

Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Какие рекомендации по безопасности нужно учитывать при разработке Golang приложений?

При разработке Golang приложений необходимо учитывать следующие рекомендации по безопасности:

Валидация ввода: Никогда не доверяйте входным данным. Всегда проводите валидацию и фильтрацию пользовательского ввода, чтобы предотвратить атаки типа SQL-инъекции, XSS и другие уязвимости.

Защита от переполнения буфера: Обязательно проверяйте размеры буферов и массивов при обработке данных. Используйте безопасные функции для работы с памятью, чтобы предотвратить переполнение буфера.

Использование параметризованных запросов: При работе с базами данных используйте параметризованные запросы, чтобы избежать SQL-инъекций. Никогда не создавайте запросы, включающие пользовательский ввод напрямую.

Защита от утечек памяти: Убедитесь, что ваши приложения Golang не страдают от утечек памяти. Правильно управляйте ресурсами и освобождайте память после использования.

Шифрование данных: При передаче и хранении конфиденциальных данных используйте шифрование для защиты данных от несанкционированного доступа.

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

Пишите свой ответ в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Что такое Overhead от стандартного профайлера?
Стандартный профайлер в Go имеет некоторый незначительный overhead, который может оказать влияние на производительность вашего приложения. Однако этот overhead обычно незначительный и не должен существенно замедлять ваше приложение.

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

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

Однако, стоит отметить, что overhead от профайлера обычно не является значительным и не должен быть проблемой в большинстве случаев. Если вы обнаружите, что профайлер существенно замедляет ваше приложение, то, возможно, имеет смысл использовать его только во время отладки и разработки, а не в продакшн-среде.

Если вы хотите более точно измерить overhead профайлера в вашем конкретном случае, вы можете использовать инструменты профилирования, такие как go tool pprof, чтобы анализировать профилировочные данные и определить, какое влияние профайлер оказывает на производительность вашего приложения.

В целом, стандартный профайлер в Go предоставляет ценную информацию о производительности вашего приложения, и его overhead обычно не является значительным. Однако, стоит оценить его использование в зависимости от конкретной ситуации и требований вашего приложения.

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Обновленный гайд с примерами от команды Go по использованию profile-guided optimization для версии 1.21.

https://go.dev/blog/pgo

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
🎉 Релиз Go 1.21.1 и 1.20.8

🔐 Новые релизы включают исправления безопасности для cmd/go (CVE-2023-39320), html/template (CVE-2023-39318, CVE-2023-39319) и crypto/tls.

🗣 Announcement: https://groups.google.com/g/golang-announce/c/Fm51GRLNRvM

⬇️ Загрузить: https://go.dev/dl/#go1.21.1

@golang_interview
👣 Стандартный набор метрик prometheus в Go-программе?
В Go-программе для сбора и экспорта метрик в Prometheus обычно используется пакет github.com/prometheus/client_golang/prometheus. Этот пакет предоставляет стандартный набор метрик и инструментов для работы с Prometheus.

Вот несколько основных типов метрик, которые можно использовать с помощью пакета prometheus:

Counter: Счетчики (Counter) представляют собой метрики, которые увеличиваются только вверх и никогда не уменьшаются. Они полезны для подсчета количества событий или запросов. Пример:

counter := prometheus.NewCounter(prometheus.CounterOpts{
Name: "my_counter",
Help: "This is my counter",
})
counter.Inc()

Gauge: Градусники (Gauge) представляют собой метрики, которые могут изменяться вверх и вниз. Они полезны для отслеживания изменяющихся значений, таких как количество активных соединений или текущая нагрузка на систему. Пример:

gauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "my_gauge",
Help: "This is my gauge",
})
gauge.Set(42)

Histogram: Гистограммы (Histogram) представляют собой метрики, которые измеряют распределение значений в заданном диапазоне. Они полезны для измерения времени выполнения операций или размера запросов. Пример:

histogram := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "my_histogram",
Help: "This is my histogram",
Buckets: []float64{0.1, 0.5, 1, 2, 5},
})
histogram.Observe(0.6)

Summary: Суммары (Summary) также представляют собой метрики, которые измеряют распределение значений, но с учетом квантилей. Они полезны для измерения производительности и времени выполнения операций. Пример:

summary := prometheus.NewSummary(prometheus.SummaryOpts{
Name: "my_summary",
Help: "This is my summary",
})

summary.Observe(2.5)
Кроме того, пакет github.com/prometheus/client_golang/prometheus также предоставляет дополнительные инструменты для регистрации метрик (Register), создания коллекторов (Collector), экспорта метрик (HTTPHandler) и т.д.

Это лишь небольшой обзор стандартного набора метрик, доступных в Go-программе через пакет prometheus. Более подробную информацию о создании и использовании метрик в Prometheus вы можете найти в официальной документации Prometheus для Go-программы: https://pkg.go.dev/github.com/prometheus/client_golang/prometheus

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
📌 Предположим, ваша функция должна возвращать детализированные Recoverable и Fatal ошибки. Как это реализовано в пакете net? Как это надо делать в современном Go

В пакете net в стандартной библиотеке Go возвращение детализированных ошибок реализовано с помощью ошибок, которые являются значениями пользовательских типов, реализующих интерфейс net.Error. Этот интерфейс определен следующим образом:

type Error interface {
error
Timeout() bool // возвращает true, если ошибка связана с таймаутом
Temporary() bool // возвращает true, если ошибка является временной
}

Интерфейс net.Error включает методы Timeout() и Temporary(), которые позволяют клиентам проверять, является ли ошибка связанной с таймаутом или временной. Это позволяет клиентам принимать соответствующие действия в зависимости от типа ошибки.

В современном Go, начиная с версии 1.13, рекомендуется использовать синтаксис ошибок, основанный на типах, с помощью применения пакета errors и интерфейса fmt.Formatter. Этот подход позволяет создавать богатые по содержанию и информативные ошибки, которые могут хранить дополнительные данные и форматироваться в нужном формате при выводе.

Вот пример, как можно реализовать детализированные ошибки в современном Go:

package main

import (
"errors"
"fmt"
)

type RecoverableError struct {
Message string
}

func (e *RecoverableError) Error() string {
return fmt.Sprintf("RecoverableError: %s", e.Message)
}

type FatalError struct {
Message string
}

func (e *FatalError) Error() string {
return fmt.Sprintf("FatalError: %s", e.Message)
}

func main() {
err := doSomething()

switch {
case errors.Is(err, &RecoverableError{}):
fmt.Println("Recoverable error:", err)
case errors.Is(err, &FatalError{}):
fmt.Println("Fatal error:", err)
default:
fmt.Println("Unknown error:", err)
}
}

func doSomething() error {
// Ваша логика здесь

return &RecoverableError{Message: "Something went wrong"}
}

В этом примере у нас есть два типа ошибок: RecoverableError и FatalError. Оба типа реализуют интерфейс error и предоставляют дополнительные данные в своих методах Error(). Функция doSomething() возвращает ошибку типа RecoverableError.

Функция main() использует функцию errors.Is() для проверки типа ошибки и выполняет соответствующие действия в зависимости от типа ошибки.

Важно отметить, что настройка и использование детализированных ошибок может различаться в зависимости от конкретной задачи и предпочтений разработчика. Рекомендуется следовать общим рекомендациям и принципам языка Go при работе с обработкой ошибок. Например, рекомендуется использовать типы ошибок, которые являются значениями пользовательских типов и реализуют интерфейс error, чтобы их можно было легко сравнивать с помощью функции errors.Is(), как показано в примере выше. Также рекомендуется предоставлять информативные сообщения об ошибках, чтобы облегчить отладку и понимание причины ошибки.

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

@golang_interview