Go: Управление обработкой множественных ошибок

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

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

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

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

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

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

Затем можно вывести отчет:

  • Использовать 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.

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

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

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

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

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

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

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

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

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

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.
После занятия вы сможете сериализовывать и десериализовывать данные различных форматов стандартными средствами языка и сторонними библиотеками. Присоединяйтесь!

Источник: https://habr.com/ru/company/otus/blog/558404/


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

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

Я активно интересуюсь роботами, и недавно сделал двуногого робота под управлением Arduino. После этого я захотел сделать четвероногого робота, имитирующего таких животных, как собаки ...
Ранее в одном из наших КП добавление задач обрабатывалось бизнес-процессами, сейчас задач стало столько, что бизнес-процессы стали неуместны, и понадобился инструмент для массовой заливки задач на КП.
Довольно часто мне приходится бывать на всевозможных семинарах, конференциях, митапах, хакатонах и презентациях. Там, где в один прекрасный момент кому-то из гостей приходится вставать с мест...
У нас было 4 Amazon-аккаунта, 9 VPC и 30 мощнейших девелоперских окружений, стейджей, регрессий — всего более 1000 EC2 instance всех цветов и оттенков. Раз уж начал коллекционировать облачные р...
Небольшой рассказ о граблях, встреченных на пути познания ARM на примере stm32f103c8t6 и stm32l151rct6. Читать дальше →