Уймин - про разработку
190 subscribers
3 photos
1 file
40 links
Авторский канал про backend-разработку. Подробнее - в закрепллённом сообщении.

Личиный аккаунт: @maksimuimin
Download Telegram
Как писать обработчики ошибок?

Функции в программах возвращают ошибки. Что с ними делать?

1️⃣ Запиши в лог. Логи позволят расследовать влияние ошибки на сервис. Вместе с сообщением об ошибке запиши имя функции, которая её вернула:
err = foo()
if err != nil {
log.Error("foo: %s", err)
return
}

Не добавляй в сообщение вводные слова: “failed foo()”, “foo() returned error” или “unable to call foo()”. Это не несёт новой информации и мешает чтению. Лучше в лог добавить аргументы, с которыми функция была вызвана. Это поможет в отладке.

2️⃣ Ограничь влияние на сервис. Ошибкам свойственно распространяться. Например, необработанное исключение в одном запросе аварийно завершает всю программу. Когда программа обслуживает нескольких клиентов, это нежелательное поведение. Лучше ограничить влияние ошибки одним запросом:
while (true) {
try {
ServeClient();
} catch (const Exception e) {
log.Error("ServeClient: %s", e);
// Больше ничего сделать не можем
// ¯\_(ツ)_/¯
}
}


3️⃣ Разным ошибкам - разные обработчики. Ошибки отличаются между собой. В случае временных ошибок можно повторить попытку:
int err = EINVAL;

// 3 попытки
for (int i = 0; i < 3; i++) {
err = do_some_stuff();
switch (err) {
case 0:
// Успех
return 0;
case EINTR:
// Временная ошибка, сработало прерывание
// Можно сразу пробовать ещё раз
continue;
case EAGAIN:
// Временная ошибка, сокет не готов к вводу/выводу
// Можно пробовать ещё раз, чуть позже
sleep(1); // TODO: лучше подписаться на событие вместо слипа
continue;
default:
// Непредвиденная ошибка
return err;
}
}

return err;

Часть ошибок может быть вариантом нормы. Например, ошибка ENOENT, файл не найден.

Ошибки можно сравнивать по типу или числовому коду при выборе обработчика. В примере выше, стандартные коды ошибок errno. ⚠️ Не следует сравнивать ошибки по текстовому сообщению. В нём может быть изменяемая часть.

Обработчики будут выглядеть одинаково вне зависимости от выбранной модели обработки ошибок. Пиши код так, чтобы он был готов к ошибкам. Это повысит отказоустойчивость программы и твой авторитет в глазах коллег 🔥

#hardskills #coding #errorhandling #logging #bestpractice
Что под капотом у исключений

Пост для тех, кто раньше об этом не задумывался. Программисты думают об исключениях как о фиче языков программирования. ЯП поддерживает исключения, если предоставляет операторы try, catch, fially, throw. Но всё не так однозначно. Если бы ты писал свою реализацию исключений, как бы ты это сделал?

Рассмотрим фичи исключений.
- Исключение может прервать поток выполнения программы.
- Исключение должно раскручивать стек до ближайшего обработчика.
- Обработчик исключений, если установлен, должен выполниться незамедлительно при срабатывании исключения.
- Если обработчика нет, процесс должен аварийно завершиться.
- Исключения должны быть совместимы с фатальными ошибками ОС, такими как Segmentation fault.
- Обработчик исключений должен возвращать управление в точку после try/catch.

Исключения реализуются с помощью UNIX-сигналов и longjmp. Обе фичи есть стандартной библиотеке С, хотя готовых исключений там нет.

Наколдуем свои исключения.
🐱 Листинг кода: gist.github.com/maksimuimin/c14b066b0b454ac7a1962631c3a238bb
▶️ Результат выполнения:
Try 1
Got signal: Aborted
Exception 1
Try 2
Try 3
Got signal: Segmentation fault
Exception 3


Практические выводы.
📌 ОС сообщает о фатальных ошибках с помощью UNIX-сигналов. Такие ошибки можно обработать и как исключения, и как сигналы. Обработка фатальных ошибок - хорошая практика безопасного программирования, даже если ваш ЯП не поддерживает исключения. Пример из реального мира, почтовый сервер Exim: github.com/Exim/exim/blob/exim-4.97.1/src/src/receive.c#L3805

📌 longjmp работает как goto, но может передавать управление между функциями. Позволяет реализовать оператор throw без сигналов. Пример из реального мира, функция error в Lua: pgl.yoyo.org/luai/i/lua_error

Исключения - сложный механизм. Я предпочитаю модель обработки ошибок на основе возвращаемых значений - там всё просто. Однако, эта модель никак не обрабатывает фатальные ошибки ОС. Поэтому, на практике используется комбинация двух моделей: возвращаемые значения для стандартных сбоев и исключения/сигналы для фатальных ошибок. Это хороший компромисс между простотой и отказоустойчивостью 🔥

#theory #coding #Linux #errorhandling
===
Мои любимые посты в канале t.me/uimindev/37
Please open Telegram to view this post
VIEW IN TELEGRAM