[Golang] Ошибки, которые нельзя обработать

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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


Паника в горутине

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

Eсли написать примерно вот такой код

type User struct {
	Email string
}

func UpsertUser(r *http.Request) (User, error) {
	return User{}, nil
}

func SendEmail(u User) {
	panic("sending email is not implemented")
}

func CreateUser(w http.ResponseWriter, r *http.Request) {
	user, err := UpsertUser(r)

	if err != nil {
		// handling error
	}

	go func() {
		SendEmail(user)
		// may be something else
	}()
}

То при вызове функции CreateUser сервис упадет.

Для того, что бы исправить это, нужно обрабатывать паники в каждой горутине

func CreateUser(w http.ResponseWriter, r *http.Request) {
	user, err := UpsertUser(r)

	if err != nil {
		// handling error
	}

	go func() {

		defer func() {
			if err := recover(); err != nil {
				log.Printf("panic recovered: %v", err)
			}
		}()
		SendEmail(user)
		// may be something else
	}()
}

и приложение не будет падать и все будут счастливы.

Бесконечная рекурсия

Если мы по какой-то причине используем рекурсию, то нужно обязательно смотреть что при любых входных параметрах не будет бесконечной рекурсии, так как обработать переполнение стека в Golang нельзя (бесконечная рекурсия приводит к переполнения стека) и приложение падает.

Допустим мы написали такую реализация вычисления числа Фибоначчи:

func Fib(n int64) int64 {
	if n == 1 {
		return 1
	}
	return Fib(n-1) + Fib(n-2)
}

И если у нас в коде будет вызов функции Fib с отрицательным числом, то приложение упадет с ошибкой fatal error: stack overflow.

Это может удивить людей, которые писали на скриптовых языках таких как Ruby или Python и начинают писать на Golang. Так как в скриптовых языках можно довольно просто поймать исключение, которое показывает что стек достиг максимального значения:

def fib(n):
    if n == 1:
        return 1
    return fib(n- 1) + fib(n - 2)

try:
    fib(0)
except RecursionError as e:
    print(e)

Код на Python выше просто выведет в консоль maximum recursion depth exceeded in comparison и приложение продолжит работу.

Работа с unsafe

При работе с модулем unsafe довольно просто сделать так что бы приложение упало. Поэтому не стоит использовать этот модуль, если все тщательно не проверено и не протестировано.

Допустим мы решили не копировать строку, а преобразовать ее в слайс байтов и написали вот такую функцию:

func ToSlice(a string) []byte {
	return *(*[]byte)(unsafe.Pointer(&a))
}

Теперь мы можем изменять строку:

	a := string([]byte("Andrey Berenda"))
	b := ToSlice(a)
	b[5] = 'i'
	fmt.Println(a) // Andrei Berenda

Но если случайно передать строку, которая известна на этапе компиляции и попробовать ее изменить, то будет ошибка, которая приведет к остановке сервера

	a := "Andrey Berenda"
	b := ToSlice(a)
	b[5] = 'i'
	fmt.Println(a) // fatal error: fault

Заключение

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

Источник: https://habr.com/ru/post/689356/


Интересные статьи

Интересные статьи

В своё время один из клиентов сообщил нам, что на Itanium его программа завершалась аварийно. Постойте, не закрывайте статью! На Itanium клиент выявил проблему, но она свойственна и всем остал...
Привет, Хабр!Меня зовут Дмитрий Матлах. Я тимлид в AGIMA. Мы с коллегами обратили внимание, что в сообществе часто возникает вопрос о том, как совместить на одном проекте Bitrix-компоненты и реактивны...
Мы продолжаем раскрывать тему биоинформатики как науки из первых рук. Завтра, 15 октября, в 20:00 в наших соцсетях выступит Ольга Кунявская, младший научный сотрудник лаборатории «...
1С Битрикс: Управление сайтом (БУС) - CMS №1 в России по версии портала “Рейтинг Рунета” за 2018 год. На рынке c 2003 года. За это время БУС не стоял на месте, обрастал новой функциональностью...
Ведущий разработчик — не зря «ведущий». Эту фразу я услышал на одной из конференций по IT-менеджменту и задался вопросом, а почему «не зря»? Именно он подтолкнул меня написать эту статью. Оце...