Закон Мерфи для кода

12 февраль, 2021 - 12:15Игорь Шаститко

Техническая задача автоматического копирования BLOBов в Azure по мере поступления – которая изначально казалась довольно тривиальной и достаточно просто решалась в частном случае – при «продуктовом» исполнении только доказала Закон Мерфи о том, что «Все не так легко, как кажется» и «Всякая работа требует больше времени, чем вы думаете».

Хотя, казалось бы, чего может быть проще, чем использование Azure Logic App с тремя элементами:

  • коннектором к требуемому Azure Storage Account/ Container, который следит за появлением новых файлов в контейнере и инициирует работу требуемого процесса;
  • операции Azure Blob Storage Get Blob content, которая, получив информацию о вновь созданном файле – по его ID из предыдущей операции считывает содержимое файла;
  • операция Create Blob – которая, используя содержимое файла из результата предыдущей операции и имя/ путь файла из первой операции – создает новый файл в другом указанном Azure Storage Account.

Как говорится – «херяк, херяк – и в продакшен» – и оно вроде работает, пока не наступает день Х (или «Х… день») и в этот момент приходит прозрение.

Первая багафича такого решения всплывает практически сразу при тестировании – когда кто-то попробует записать файл не в корень контейнера, а предварительно создав там подкаталог и поместив файлы в него. В таком случае базовый коннектор Logic App для Azure Blob Storage не реагирует на новый файл/ файлы – он следит только за одним контейнером или каталогом в нем и только запись в корень данного контейнера/ каталога инициирует работу подобного Logic App. А файлы пишутся как раз сразу целыми каталогами.

НО, даже в ситуации с одним контейнеров возникает новая удивительная ситуация – одновременная загрузка десятков файлов на Azure Blob Storage (особенно из нескольких независимых источников) приводит к запуску сразу нескольких экземпляров нашей Azure Logic App, каждый из которых выполняет одновременно однотипные действия по чтению и записи файлов – и тут срабатывает еще одно ограничение – теперь уже Azure Storage Account. Включается режим Bandwidth throttling, что приводит к ошибкам чтения контента нового файла или его записи в другой Storage Account. Так всплывает вторая багофича, которая запросто «крешит» столь «быстро написанное» Logic App при активной работе.

В продолжение разговора о данной багофичи стоит сказать, что обработка ошибок исполнения и кодов возврата операция реализована в Azure Logic Apps весьма и весьма специфически – процесс контролируется двумя разными возможностями – одна из которых – это выбор исполнения следующей команды только при условии, если предыдущая была успешной/ неуспешной/ отвалилась по таймауту или была пропущена, а вторая – это команда result(), которая позволяет получить результаты выполнения группы операций, которые помещены в общую область (scope) – специально созданный в Azure Logic Apps элемент для подобной группировки операций Logic App.

И, наконец, на закуску, то, что может «выстрелить» совсем потом и очень «больно» – «обычное» приложение Azure Logic Apps оперирует с данными до 1ГБ – т.е. считать файл более 1ГБ данных из исходного файла такое приложение не в состоянии и отваливается по ошибке. Тут есть два пути – либо платить в разы больше за «изолированные» экземпляры Azure Logic Apps – в таком случае поддерживается обработка данных до 5ГБ, либо ограничивать административно размеры файлов до 1ГБ и, опять же, проверяя контент до копирования – прекращать работу с большими файлами и отправлять уведомление.

Итого, исходя из всех данных ограничений, получается, что простая задача из 3х блоков превращается в 10 раз больший код – закон Мерфи для кода будет звучать как «Любая задача требует в 10 раз больше кода, чем изначально планировалось».

Во-первых, для реакции на создание файла в любом каталоге хранилища придется создать триггер на основе Azure Event Grid и сконфигурировать сервис Event Grid и отправку в него событий из Azure Storage Account так, как описано здесь – https://docs.microsoft.com/en-us/azure/event-grid/blob-event-quickstart-portal.

В возвращаемых событием данных есть размер файла, что позволяет перехватывать файлы с размером более 1ГБ.

Реакция на Throttling реализуется в виде цикла Until с выполнением следующей операции по условию успешно/не успешно и обработкой кода ошибки, получаемой через result() – таким образом приложение в циклах будет пытаться считать/ записать файл и в случае throttling обрабатываем аварийное завершение блока чтения/ записи, после чего – через result() получаем дополнительную информацию об ошибке и в ней – время, на которое включен throttling – делаем паузу и ждем, когда Storage освободится от массового кол-ва запросов.

Закон Мерфи для кода