![](https://habrastorage.org/getpro/habr/upload_files/417/014/be8/417014be8a0205d8ba4b7e1467bf8718.png)
Управление обработкой ошибок в Go всегда вызывает споры — это извечная тема в ежегодном опросе о самых больших проблемах, с которыми сталкиваются разработчики при работе с Go. Однако когда дело доходит до обработки ошибок в многопоточной среде или объединения нескольких ошибок одной и той же горутины, Go предоставляет отличные пакеты, которые упрощают управление обработкой множественных ошибок. Давайте посмотрим, как объединить несколько ошибок, генерируемых одной горутиной.
Одна горутина, несколько ошибок
Объединение нескольких ошибок в одну может быть весьма полезным решением, например, когда вы работаете над кодом, имеющим политику повторного запуска. Вот простой пример, в котором нам нужно группировать сгенерированные ошибки:
![](https://habrastorage.org/getpro/habr/upload_files/010/ba1/e15/010ba1e15f0327d142c7ca8c926237c1.png)
Эта программа считывает и анализирует CSV-текст и отображает найденную ошибку. Было бы намного удобнее группировать ошибки, чтобы получить полный отчет. Чтобы объединить ошибки в одну, у нас есть выбор между двумя отличными пакетами:
Используя go-multierror от HashiCorp, несколько ошибок можно объединить в одну стандартную ошибку:
![](https://habrastorage.org/getpro/habr/upload_files/538/693/69c/53869369cfdfbd4897e859ff18c6cdd6.png)
Затем можно вывести отчет:
![](https://habrastorage.org/getpro/habr/upload_files/7ea/7f8/114/7ea7f8114d72f8b24c4f3f1f7c2a226e.png)
Реализация здесь аналогична, вот результат:
![](https://habrastorage.org/getpro/habr/upload_files/eb6/88a/649/eb688a6490edc9a0c981688a48584c46.png)
Ошибки объединяются через точку с запятой без какого-либо другого форматирования.
Что касается производительности каждого пакета, вот бенчмарк на той же программе, но с большим количеством ошибок:
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
.
Одна ошибка, несколько горутин
Чтобы обеспечить корректность вашей программы при работе на нескольких горутинах для выполнения одной задачи, необходимо правильно управлять результатами и агрегированием ошибок.
Начнем с программы, которая использует несколько горутин для выполнения ряда действий; каждое из них длится одну секунду:
![](https://habrastorage.org/getpro/habr/upload_files/f86/cfb/a81/f86cfba813b32f9094b9b6e1290b1d1e.png)
В целях демонстрации распространения ошибки, первое действие третьей горутины будет завершаться ошибкой. Вот что происходит:
![](https://habrastorage.org/getpro/habr/upload_files/789/b0a/a88/789b0aa8820342b1a38c490b5916018b.png)
Как и ожидалось, программа занимает примерно три секунды, поскольку большинству горутин необходимо выполнить три действия, каждое из которых занимает одну секунду:
go run . 0.30s user 0.19s system 14% cpu 3.274 total
Однако мы могли бы захотеть сделать горутины зависимыми друг от друга и отменять их, если одна из них даст сбой. В качестве решения, чтобы избежать ненужной работы, можно добавить контекст, чтобы, как только горутина даст сбой, она отменяла его:
![](https://habrastorage.org/getpro/habr/upload_files/b2a/3f9/e91/b2a3f9e91353115ee9b429bdb8d9d760.png)
Это именно то, что предоставляет errgroup; распространение ошибки и контекста при работе с группой горутин. Вот обновленный код, использующий пакет errgroup:
![](https://habrastorage.org/getpro/habr/upload_files/b52/45f/cff/b5245fcff9c43a0cef31804033e6f6fc.png)
Теперь программы работают быстрее, поскольку они распространяют отмененный ошибкой контекст:
go run . 0.30s user 0.19s system 38% cpu 1.269 total
Преимущество пакета заключается в том, что нам больше не нужно беспокоиться о добавлении ожидающей группы и пометке горутин как выполненных. Пакет управляет этим за нас, нам просто нужно обозначить, когда мы будем готовы дождаться окончания процесса.
Перевод подготовлен в рамках набора студентов на курс "Golang Developer. Professional".
Всех желающих приглашаем на открытый вебинар «Форматирование данных». На этом demo-занятии рассмотрим:
- кодировки quoted-printable и base64;
- текстовые форматы JSON, XML и YAML;
- использование структур и интерфейсов для парсинга данных;
- сравнение бинарных сериализаторов: gob, msgpack и protobuf.
После занятия вы сможете сериализовывать и десериализовывать данные различных форматов стандартными средствами языка и сторонними библиотеками. Присоединяйтесь!