Разберем одну из типичных задач организации CI/CD процессов, а именно отслеживания и поддержки структуры объектов для облачной базы данных Snowflake. Из готовых решений, в первую очередь мы обратили внимание на провайдер для Terraform, который называется chanzuckerberg/terraform-provider-snowflake.

Более внимательно изучив доступный функционал и способ его реализации, мы пришли к выводу, что сфера его применения весьма специфическая и я бы не рекомендовал использовать его, по крайней мере для CI/CD процессов для объектов хранения, или по крайней мере в текущей реализации провайдера. Возможно в будущем этот провайдер будет доработан, но текущая его реализация у меня вызвала множество вопросов.

Еще раз обращу внимание, что мы рассматриваем CI/CD инструментарий для поддержания модели данных, в первую очередь. Администрирование БД, в нашем рассмотрении отодвинуто на второстепенный план. Если мы решаем задачу, связанную с поддержанием систем разграничения прав доступа, или с поддержанием целостности объектов, отвечающих за изменение метаданных, то, возможно, этот инструментарий можно использовать.

Пройдемся по основным моментам, возникающим при использовании данного провайдера Terrafоrm для Snowflake.

Первое с чем можем столкнуться, это то что Snowflake позволяет создавать объекты, как в верхнем регистре, так и нижнем регистре, но при манипуляциях с объектами через коннекторы Snowflake, или через JDBC драйвер, Snowflake автоматически переводит наименование объектов в верхний регистр и только имена, заключенные в кавычки, обрабатывает в том регистре в котором они переданы, а Terraform создает объекты в переданном регистре в независимости от использования кавычек.

Как один из возможных вариантов обходных путей мы можем ввести внутреннее правило наименования объектов только в uppercase.  

Если тип данных мы укажем с символом нижнего регистра, неявное преобразование внутри Snowflake заменит это на конечный внутренний тип данных и Terraform скрипт, конечно, не будет знать об этом. Каждый раз, когда Terraform будет пытаться проверить есть ли изменения в нашем окружении, он будет всегда видеть, что есть различия, но на самом деле ничего не изменилось. Это, скорей всего, происходит потому, что Terraform применяет скрипт и после этого забирает текущее состояние полученного объекта, которое получилось внутри Snowflake и оно может отличаться от изначального скрипта Terraform. Например, у нас колонка имела тип данных integer. По факту Terraform, когда произвел применение изменений, получает описание, что этот объект был создан, как number. Получается мы записали изменение в Terraform, задеплоили их через Terraform в Snowflake, но состояние объектов внутри Snowflake Terraform получил совершенно другое, чем мы указали в скрипте и теперь каждый раз Terraform будет считать, объект следует модифицировать.

 

Наиболее часто встречающиеся проблемы:

Available input data type Will be always converted in Snowflake
INT NUMBER(38,0)
TEXTVARCHAR(16777216)
VARCHAR(ANY SIZE)VARCHAR(16777216)

Наиболее частыми, хоть и синтаксически разрешенными конструкциями, которые приводят к такому результату, являются попытки создания полей с типом integer. Неявное преобразование типов в Snowflake создаст колонку с типом number 38. Тип данных текст также будет преобразован в varchar с максимальной длинной, причем если мы создаем varchar с каким-то ограничением, например varchar(10) внутри Snowflake все равно получим максимальный размер типа данных. Остается только констатировать, что на данный момент, провайдер не знает каким образом работать с этими аналогами названий типов данных.

Следующий тип проблемы. Каждый раз когда мы пытаемся изменить типы данных в колонке таблицы, Terraform, имея у себя последний слепок данных из Snowflake, пытается изменить налету тип данных внутри колонки в табличке, но попытка добавить новую колонку в конец таблицы приводит к тому, что Terraform решает пересоздать эту таблицу с помощью простого уничтожения и создания нового объекта. Это, похоже, связано с тем, что провайдер Terraform писался, как инструмент для обслуживания ресурсов, например, как обслуживание ресурсов в облачной среде, в которой простое пересоздание нового ресурса не должно вызывать проблем. 

Есть определенные механизмы (и в других провайдерах Terraform тоже) бесшовной интеграции, например blue-green deployment, но в провайдере, который используется вместе с Snowflake, механизмов для реализации такой интеграции нет. Чтобы добавить новую колонку в конце таблицы можно было бы задать значение по умолчанию. Однако провайдер не предоставляет такой возможности. Для этой колонки мы не можем обработать, или написать какой-то скрипт по изменению нашего объекта в базе данных.Даже если бы мы и захотели не просто удалить объект с потерей всех данных, а именно контролируемо трансформировать этот объект, либо создать копию этого объекта рядом, скопировать исходные данные в новые объекты, удалить с последующим переименованием копии объекта на целевое имя – таких механизмов текущая версия провайдера не предоставляет. 

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

В Snowflake есть объекты, которые не хранят данные. При работе с ними не так страшно уничтожение и пересоздание. Например, представления, или объекты разграничения прав доступа, они не содержат в себе историю и не содержат в себе данные. По факту метаданные это единственное, что необходимо для этих объектов, и если мы пересоздадим какую-то роль, то такой механизм вероятно будет работать. Однако неявное преобразование на стороне Snowflake и здесь может приводить к неожиданным результатам. 

В нашем примере у нас есть определенные роли, которые заложены в систему распределения прав доступа. Мы создали глобальную роль с правами только на чтение, и глобальную роль чтения/записи. Пытаемся создать схему и передать привилегии для группы public. Snowflake интерпретировал public, как весь набор подролей, входящий в public, скорей всего public – ключевое слово с внутренней логикой, заложенной внутри snowflake. Terraform, применяя определенный скрипт принимает эти изменения и вносит их в свою структуру, но обратно от Snowflake Terraform получает список ролей, которые входят внутрь public. Мы снова получаем ситуацию, когда скрипт вроде бы синтаксически верный, успешно применяется на окружении, но провайдер при каждом запуске будет предполагать, что мы внесли изменения в окружение.

 

На текущий момент я бы рекомендовал не использовать провайдер chanzuckerberg/terraform-provider-snowflake для Terraform. В качестве альтернативных решений для Snowflake можно рассмотреть более зрелые решения такие как: