Найти тему
Nuances of programming

Обработка ошибок в Go

Оглавление

Источник: Nuances of Programming

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

Одна горутина  —  несколько ошибок

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

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

  • При использовании go-multierror от HashiCorp, ошибки можно совместить в одну стандартную:

После чего вывести отчет:

-2
  • В случае использования multierr от Uber реализация будет аналогична, но вывод получится следующий:

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

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

name time/op alloc/op allocs/op
HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0%
UberMultiErrors-4 9.26µs ± 1% 10.3kB ± 0% 126 ± 0%

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

name time/op alloc/op allocs/op
HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0%
UberMultiErrors-4 6.02µs ± 1% 7.06kB ± 0% 77.0 ± 0%

Оба пакета задействуют Go-интерфейс error с реализацией функции Error() string в их пользовательском варианте.

Одна ошибка  —  несколько горутин

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

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

-3

Чтобы отобразить распространение ошибки, первое действие третьей горутины будет давать сбой. Вот, что происходит:

-4

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

go run . 0.30s user 0.19s system 14% cpu 3.274 total

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

-5

Именно это и позволяет сделать errgroup — распространить ошибку и контекст при работе с группой горутин. Вот новый код, применяющий этот пакет:

-6

Теперь программа выполняется быстрее, так как распространяет отмененный ошибкой контекст:

go run . 0.30s user 0.19s system 38% cpu 1.269 total

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

Читайте также:

Читайте нас в Telegram, VK

Перевод статьи Vincent Blanchon: Go: Multiple Errors Management