Как мы переезжали с PostgreSQL на Data Lake в AWS и какие грабли собрали по пути

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

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

За несколько лет Whoosh в несколько раз вырос по числу самокатов, пользователей и локаций, а данных по ним накопилось на 30 терабайт. Прежней архитектуры уже не хватало для работы. К тому же платить за I/O (input/output)-операции на Aurora (PostgreSQL) выходило дорого (тогда еще не было I/O‑optimized версии, однако с ее появлением, актуальность не исчезла). Другое дело — Redshift: расходы постоянны (n$/час), а работает он быстрее, благодаря колоночному формату хранения данных. В этом году мы переехали с одного хранилища на базе PostgreSQL — того, где вся отчётность для бизнеса и модели dbt — на рельсы Data Lake в AWS.

Меня зовут Никита Зеленский, я главный по данным в Whoosh. Эту статью я написал вместе с другими участниками переезда — Пашей Сивохиным, ГИС-аналитиком, и Костей Малыхиным, руководителем группы анализа данных. Надеюсь, наш опыт будет полезен всем, кому предстоит миграция данных, особенно если вы работаете с геоаналитикой.
WHOOSH

Архитектура до переезда

До переезда наша архитектура состояла из двух основных частей — Aurora Prod и Aurora DWH, обе на базе PostgreSQL. Все данные с наших самокатов и приложения поступали сначала в Aurora Prod — там у нас протекают все процессы, связанные с клиентами и устройствами. Затем оттуда по логической репликации они переносились в Aurora DWH, которая была основной базой для разработчиков, дата-инженеров, аналитиков, и здесь же находился динамический тариф и BI-инструменты — Grafana и геосервис Martin (о том, как мы работаем со всем этим — чуть дальше). 

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

Первое. BI-отчёты начинают деградировать по скорости. На то, чтобы обновить карту города или страны и сделать обыкновенный отчёт, требовалось 7 минут ожидания на рельсах PostgreSQL (сейчас — секунд 10 на рельсах Редшифта).

Второе. Логическая репликация — жёсткая сцепка между двумя базами данных. Что это означает? Если какой-то процесс начинает лочить какую-нибудь таблицу в Aurora DWH, то логическая репликация этой таблицы приостанавливается, а на Aurora Prod начинают копиться транзакционые логи и dead rows (которые Автовакуум не будет вычищать, пока не закончится транзакция). Не самая удачная идея, когда у нас на Aurora Prod завязан весь бизнес, ведь процессору придется сканировать все больше строк, которые уже не нужны.

Третье. И самое главное. Амазон биллит за количество I/O-операций. Если наш продуктовый аналитик напишет неоптимизированный запрос, который приведёт к большому числу I/O-операций и full-scan исходных таблиц, то компания за эту аналитику спасибо не скажет — запрос получится дорогим.

Наша цель состояла в том, чтобы отказаться от Aurora DWH. В этой точке начинается приключение на 5,5 месяцев.

Как мы представляли наш переезд

Всю работу мы ведем в dbt — это фреймворк с открытым исходным кодом для выполнения, тестирования и документирования SQL-запросов. В нём много приятных вещей: от автоматизации sql-моделей до настройки автоматического накопления историчности таблиц по SCD-2 и не только. Переезд означал, что мы должны были разнести все модели dbt на две составные части:

  • Облако S3 (Simple Storage System) — фактически Data Lake

  • Redshift — колоночная база данных облака AWS

Переезд означал, что мы должны были разнести все модели dbt на две составные части
Переезд означал, что мы должны были разнести все модели dbt на две составные части

Предполагалось, что мы сделаем "водораздел" между дата-инженерами, которые будут работать с источниками данных в S3 с помощью Athena и Glue-каталогов, и аналитиками, — они получат готовые агрегаты в Редшифте. Также было желание, чтобы Grafana и Martin работали на Редшифте.

Для миграции данных в режиме реального времени из Aurora Prod в Redshift мы выбрали амазоновскую DMS (Database Migration Service), про неё мы обстоятельно расскажем дальше.

И у нас было дополнительное условие. Мы хотели переехать, не прибегая к помощи команды разработки. У ребят хватает своих задач, к тому же нам было интересно прокачать компетенции работы с AWS и показать, что мы можем справиться сами. 

Казалось бы, что по этому плану могло пойти не так?

Особенности гео-аналитических расчетов

Редшифт отлично подходит для DWH-хранилища и работы с длинными аналитическими запросами, которые зачастую требуют множественной агрегации, соединения таблиц. К тому же он быстрее и отлично масштабируется. Но есть одно но.

Мы в Whoosh сильно завязаны на геоданных, поэтому нам важно иметь инструмент, способный грамотно работать с ними. Спойлер: мы так и не перенесли часть процессов по геоаналитике в Редшифт, сейчас поддерживаем их также и в PostgreSQL.

Дело в том, что на PostgreSQL завязаны два важных продукта:

  • динамический тариф — он напрямую влияет на ценообразование для конечного пользователя.

  • Martin — тайловая система с открытым исходным кодом, которую мы переделали под себя для визуализации пространственных данных. 

Перевезти Martin и динамический тариф трудно или невозможно, особенно, не затрагивая ресурс команды разработки.

  • Во-первых, для расчёта динамического тарифа нужен набор инструментов для работы с пространственными данными, которых нет в Редшифте.

  • Во-вторых, если даже с инструментами можно что-то придумать, то никуда не денется бутылочное горлышко Редшифта — работа с короткими запросами. А командам внутри Whoosh нужно иметь доступ к данным в реальном времени и для этого — формировать много коротких запросов в базу. Сотни запросов в секунду будут губительны для Редшифта. PostgreSQL гораздо больше подходит под такие задачи.

Что касается инструментов для работы с пространственными данными. Если вы не сталкивались с ними, то стоит уточнить, что на первый взгляд — это обычные данные, которые можно также просматривать в табличном виде, с набором полей и характеристик. Но с дополнительным критерием, который и делает их пространственными, — наличие геопривязки. Например, это точка, которая может быть отображена как парковка, самокаты, велосипеды. Линия — треки поездок. Полигоны — медленные зоны, где самокат снижает свою скорость, или зоны запрета для поездок.

Из этих трёх составляющих складывается вся магия пространственной аналитики, где данные можно по-разному пересекать и агрегировать, экстраполировать, считать площади и расстояния, и на их основе строить гипотезы.
Из этих трёх составляющих складывается вся магия пространственной аналитики, где данные можно по-разному пересекать и агрегировать, экстраполировать, считать площади и расстояния, и на их основе строить гипотезы.

Расчёты по геоданным мы делаем с помощью наших ETL-процессов на Python и функций в PostGIS, а визуализируем всё в Мартине. Martin— опенсорс-инструмент для создания векторных тайлов с помощью PostGIS (это расширение для PostgreSQL). Первый нюанс — PostGIS работает только с PostgreSQL.

Стоит отметить, что PostGIS — лидер по числу пространственных функций. Позволяет работать с пространственными данными под любым углом. В Редшифте в 3,5 раза меньше функций для работы с этим типом данных. А некоторых важных инструментов в нём совсем нет, например, для починки проблемной топологии — когда нужно преобразовать невалидную геометрию в валидную. 

Или другой пример — построение регулярной или нерегулярной сетки. PostGIS позволяет создавать наборы регулярной сетки, например, гексагоны. Redshift — нет. А это отличный инструмент для аналитики. На картинке ниже вы видите поездки в Санкт-Петербурге. С помощью таких данных можно определять хот-споты в городе, понимать где больше всего ездит людей, чтобы подстраивать инфраструктуру компании под потоки пользователей: устанавливать парковки, наращивать флот в определённых локациях и давать советы операционной команде. 

Скриншот из Мартина - плотность поездок по Санкт-Петербургу за 2023 год
Скриншот из Мартина - плотность поездок по Санкт-Петербургу за 2023 год

Попытки решить эти проблемы

Если вы следите за выступлениями и новостями разработчиков AWS, то слышали про обходной способ — использование Athena и UDF вместе с AWS Lambda, благодаря которым можно стучаться в uber H3. О пространственном индексе H3 можно почитать здесь. H3 — это и есть гексагоны, просто они по-другому сконфигурированы в пространстве, что позволяет заполнить всю поверхность земли. 

Мы решили что сможет решить проблему этим способом. Но на простых юзкейсах столкнулись с массой проблем.

Aurora PostgreSQL позволяет объединить геометрию в поле. Это здорово помогает, когда делаешь аналитику, связанную с доступностью. Например, мы хотим посмотреть, насколько наши парковки охватывают точки притяжения — ТРЦ. Мы строим изохроны доступности от каждой парковки и объединяем их на уровне всего города. На выходе получается один большой полигон доступности — тут мы смотрим как много ТРЦ попадает в него.

В Redshift нельзя объединять геометрию, находящуюся в одной колонке. Надо либо расширять таблицу (записывать геометрию в каждой новой колонке и уже так объединять её), либо использовать похожие функции. Например, функция st_collect запихивает всю геометрию в одну строку — так происходит наложение данных и, соответственно, задваивание чисел. 

На этой картинке пример наложения полигонов доступности и, соответственно, задваивание чисел. Чтобы объединять геометрию, мы используем либо Python либо продолжаем делать определённый анализ на Авроре в PostGIS.
На этой картинке пример наложения полигонов доступности и, соответственно, задваивание чисел. Чтобы объединять геометрию, мы используем либо Python либо продолжаем делать определённый анализ на Авроре в PostGIS.
Тут - про возможные форматы гео-данных. Будем ссылаться потом на них далее по тексту

Геометрию можно представлять в нескольких форматах. Самый распространенный — Geometry/Geography. Первый — растягивание нашего земного шара на 2D-пространство, второй  — проекция земного шара на эту сферу. 

В этом случае мы используем самую простую систему координат — ESPG:4326 или WGS 84 с количеством знаков после запятой в долготе или широте, равной шести, чтобы получить точность на уровне одного человека. Так геометрия записывается, как некий мультиполигон.

Есть текстовый формат WKT (Well-Known Text), который по стилю записи похож на Geometry/Geography, но в формате varchar. Мы получаем запись о том, что есть мультиполигон и в нём такой-то набор координат. Тоже понятный способ, но стоит помнить, что это просто буквы в ячейке. 

Следующий формат — Well-KnownBinary или EWKB (Extended Well-Known Binary). Здесь можно уместить больше геометрии, занимая меньшее количество байтов. EWKB — формат, где хранится система координат, тип геометрии, долгота и широта объекта.

Вы видите геометрию Москвы. Всё, что начинается после единички — это геометрия, а шестерка нам говорит о том, что это мультиполигон.
Вы видите геометрию Москвы. Всё, что начинается после единички — это геометрия, а шестерка нам говорит о том, что это мультиполигон.

Это классный формат, но в него довольно сложно переводить данные из текста. Для таких задач есть Hex-encoded EWKB, который использует чуть больше символов, но даже вместительнее, потому что напоминает геохеш. Тут мы записываем не определённые вершины геометрии, а гексагончики, где лежат наши данные.

У Redshift в работе с гео есть несколько особенностей. Например, в ячейку влезают данные размером около миллиона байтов, если это геометрия, и всего 65 тысяч байтов (2^16), если это текст. И многие инструменты для переноса данных, в том числе пространственных, например, логическая репликация и DMS, не могут напрямую переносить геометрию — они сначала переводят её в текст. 

Здесь мы поняли, что 10% сырых полигональных данных, которые мы получаем с прода от разработки, не поместятся в одну ячейку, Redshift будет её обрезать. Аналитику на таком уровне делать нельзя, особенно геоаналитику — те же зоны доступности (зоны, где самокату разрешено ездить, а где должно быть ограничение) просто не влезали бы.

Какие были варианты? Мы могли попробовать переносить геометрию без трансформации в текст. Или попробовать различные циклы, которые упрощали бы нашу геометрию на основе её веса, чтобы она могла влезть в хранилище. Ни в первом, ни во втором варианте мы бы не справились без разработки. 

И мы нашли третий вариант. Из Aurora Prod мы льём данные в S3 в формате Hex-encoded EWKB. Стоит заметить, что Аврора, и PostgreSQL, и S3 с Athena, и Redshift немного по-разному записывают Hex-encoded EWKB, и там неоднородные форматы. Поэтому мы написали dbt-макрос, который каждый раз немножко изменял эту запись и удалял ненужные части, портящие всю картину потом. 

Далее из S3 мы забираем данные в Redshift, переводя их в WKB, и уже дальше храним все данные в этом формате. А для аналитиков переводим геометрию в Geometry/Geography через простую функцию ST_GeomFromEWKB.

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

Что ещё нам дают несколько вариантов работы с данными. На картинках вы видите, что с 2020-го по 2021 год у нас значительно выросли поездки в Санкт-Петербурге.

Плотность поездок в Санкт-Петербурге в 2020 году
Плотность поездок в Санкт-Петербурге в 2020 году
Плотность поездок в Санкт-Петербурге в 2021 году
Плотность поездок в Санкт-Петербурге в 2021 году

Проблема была в том, что с ростом количества поездок (и, соответственно, данных) и несмотря на оптимизацию (пространственные индексы), подсчёт в Авроре занимал несколько часов. Из-за этого мы тратили много денег и иногда затрагивали успешное выполнение других процессов. Здесь нам на помощь пришли две вещи:

— партицирование — иерархическая организация на основе метаданных. По сути это сохранение данных в отдельных файликах. Мы нарезаем все наши поездки по дням — это отдельные файлы, а потом на их основе строим аналитику.

— dbt utils = python + скорость Athena. Очень удобно, что у нас именно dbt и язык Jinja, на котором к нему можно обращаться. Даже базовые dbt-утилиты позволяют использовать питоновский код. Так мы можем делать простые циклы, где используем айдишник города и строим все поездки по нему отдельно, не перегружая при этом Athena и не вылезая за её ограничения.

Почему дата-инженеру важно помнить школьную математику

Мы часто отвечаем бизнесу на вопрос, к какой парковке относится самокат. Представим, что у нас несколько парковок рядом. Одна находится через дорогу. Нам нужно точно понимать, где самокат. Эта привязка влияет не только на опыт пользователя, но и на работу сервисной команды, которая производит ребаланс, заряжает, забирает самокаты на ремонт. Представим, что сотрудник сервиса едет на машине, ему важно точно понимать, где устройство, чтобы не попасть в ситуацию, когда между ним и парковкой — дорога с двойной сплошной и надо делать крюк. 

В Aurora мы бы использовали функцию сross join lateral. С помощью cross join мы получаем это произведение, перемножение строк, а функция lateral позволяет использовать результаты подзапроса в дальнейших вычислениях. Что это означает? Если мы нашли парковку для какого-то самоката, то мы не будем перебирать их дальше, а остановимся на этом результате. Соответственно, мы оптимизируем работу сross join-а. В Redshift такой функции нет. 

Что же делать? Мы подумали: широта и долгота — это же ведь десятичные числа. А что, если мы домножим до тысячи, округлим до целого и просуммируем полученные значения по широте и долготе? Таким образом, мы получим некий псевдоайдишник самоката и псевдоайдишник парковки. Дальше нам нужен inner join.

Здесь надо понимать, что возникает ошибка возможного округления (как в большую, так и в меньшую сторону). Нужно сделать пять таких джойнов, а потом соединить результаты через Union All. Да, на первый взгляд, выглядит, как костыль. Но, сравнив эти подходы между собой (cross join lateral и наш «pseudo-id»), мы увидели, что скорость выполнения запросов выросла на порядок. А когда сравнили с питоновской библиотекой — эталоном в нашем понимании, — то выяснилось, что у неё под капотом ровно такой же подход. Мы дошли до него самостоятельно, нам показалось любопытным то, что школьной математикой и базовым пониманием того, как работают джойны, можно решить такую задачку для бизнеса.

Вернёмся к примеру. Мы видим, что псевдо-айдишник нашего самоката — 84082, парковки №1 — 84080, для парковки №2 — 84082. Из-за корректировки ошибки округления парковка №1 тоже попадёт в конечную выборку списка парковок-самокатов (хотя фактическое расстояние между двумя точками больше километра). А дальше по каждой такой паре будет рассчитано расстояние стандартными способами. Нам остается для каждой такой пары самокат-парковка найти такую парковку, где расстояние минимально. Так мы скажем, что самокат находится на парковке №2.

Как устроена обработка запросов в Redshift

Ещё немного расскажу про особенности работы с Редшифтом. Напомню, что у него есть проблема с короткими запросами. Разберёмся, как в нём происходит обработка запроса. 

Первый шаг — парсинг. Запрос распарсивается на то, чтобы создать логическое дерево запроса. Второй шаг — логическая структура запроса для машины поступает в оптимизатор. Оптимизатор состоит из двух частей — это третьи и четвёртые шаги. Логическая трансформация, которая разбивает один запрос на несколько маленьких. И физическое планирование. Здесь определяется, какие ресурсы нужны, сколько памяти задействовать, где находятся нужные таблицы. Когда мы пишем EXPLAIN, то получаем на выходе план запроса после оптимизатора.

Пятый шаг: план запроса отправляется на бэкенд и там происходит компиляция кода. Получаем инструкцию, как выполнить запрос на наших узлах кластера, которая включает три части: шаг, сегменты, стрим. Шаг — это атомарная единица операции для создания запроса. Из шагов формируются сегменты. Из сегментов состоит стрим. Шестой шаг: код исполняется на узлах кластера. И результат возвращается на мастер-ноду, где происходит финальная агрегация результата, сортировка, если необходимо.

Обработка запроса с первого по пятый шаг затратна по времени. В сумме — больше 5 секунд. Поэтому Redshift старается лишний раз не проделывать работу до пятого шага. Если вы дадите ему одинаковый запрос дважды, то во второй раз он сразу отправит его на шестой пункт. А если у вас ещё и исходные данные не поменялись, то он и шестой шаг делать не станет, а возьмёт их из горячего кэша.

В чем тут проблема? Если идут одинаковые запросы, но с разной переменной, например: «Сделай селект чего-либо по городу Х», а потом город меняется, то с точки зрения Редшифта — это уже другой запрос, поэтому он проведёт его с первого по пятый шаг. У нас 52 локации, и если все операционные управляющие захотят посмотреть информацию по городам, Редшифт им спасибо не скажет. В моменте это не сильно влияет на производительность, но мы понимаем, что может возникнуть очередь из запросов.

Возьмем за пример запрос #84536314, в котором участвуют 2 таблицы по 150 тыс. строк (1-2 Мб) и 1 left join. Время исполнения ~5,6 секунд. Посмотрим на загрузку: в сумме требуется просканировать больше 5 Гб данных (по полю "workmem")
Возьмем за пример запрос #84536314, в котором участвуют 2 таблицы по 150 тыс. строк (1-2 Мб) и 1 left join. Время исполнения ~5,6 секунд. Посмотрим на загрузку: в сумме требуется просканировать больше 5 Гб данных (по полю "workmem")

Если вы захотите сделать BI или Grafana на базе Редшифта, то лучше не пускать в него абы какого пользователя, который будет обращаться к нему через прямые запросы. Можно через оркестратор сделать обновление таблицы и выдачу результата, который уже не будет меняться, а BI или Grafana будут гораздо быстрее переходить к конечному результату.

DMS для миграции данных. Ожидание...

DMS, по сути, виртуальная машина, администрируемая Амазоном, которая заточена под одну задачу: перенести данные из одного источника в другой. Вообще-то классный инструмент, преимуществ у неё больше, чем недостатков, поэтому мы до сих пор ей пользуемся. Но, наверное, в будущем поменяем. 

Что понравилось:

  • сильно облегчила нам жизнь по переносу таблиц в режиме «онлайн + CDC». 

  • Не затрагивает мощности исходной базы (за исключением времени, когда требуется Full Load при включенном CDC) 

  • Простой пользовательский путь - настраивается все за пару кликов мышкой

Как это работает? Представим, что у нас есть временной ряд наполнения wal-логов (wal-логи и транзакционные логи — одно и то же) на исходной базе данных. DMS говорит: «Ага, ты хочешь таблицу из этой базы данных. Давай-ка я сделаю чекпоинт, а потом зафиксируюсь, чтобы выбирать информацию о том, что изменилось с этой таблицей». Мы, грубо говоря, фиксируемся, откуда потом будем менять информацию для CDC. Если CDC не требуется, чекпоинты тоже не нужны. А под капотом DMS вычитывает и конвертирует бинарные логи в обычные SQL-команды, которые затем применяет в целевой базе данных. 

Пока у вас перекачивается вся таблица, а сначала нужно скачать всё, а потом делать с ней CDC, растёт число транзакционных логов на исходной базе данных. Этого, к сожалению, не избежать (нужно будет по ним пробежаться, найти то, что соответствует реплицируемой таблице и перенести в целевую базу данных). Да, это сначала аффектит исходную базу данных, но когда включается фаза CDC, wal-логи достаточно оперативно сходят на нет, и она почти не замечает присутствие DMS.

Казалось бы, инструмент надёжный, как часы.

.. и реальность

DMS врёт про типы данных. Если она не видит подходящего типа данных в целевом хранилище для типа данных, полученном с источника, то просто конвертирует их в текст. Вспоминаем, что в ячейку Redshift не влезает значений больше 2^16 символов текста.

Наша исходная база данных — OLTP, если внутри запускается процедура begin end, а внутри begin end находился какой-нибудь alter table, то DMS такую операцию проигнорирует и засунет в отдельную таблицу исключений. С ней потом нужно будет повзаимодействовать для того, чтобы результат перенести в хранилище. 

И больше всего нас расстроило то, что с последним обновлением сломался параллелизм при заполнении данных в Редшифте. В июле DMS обновилась на версию 3.5.1, а до этого была 3.4.7. Прежняя версия умела параллельно заливать данные в Redshift, а новая — почему-то не любит этого делать. Теперь мы должны все наши данные заполнять последовательно.

Самое главное — это ограниченные функции по отладке ошибок. Если CDC встанет, то начнут расти транзакционные логи на исходной базе данных. Единственное, что можно сделать — перезапустить переливку данных, потому что у PostgreSQL нет временного wal-лога, а только численно-буквенный чекпоинт, который ещё надо сопоставить с версией таблицы (и непонятно, как она соотнесётся с вашей). Не страшно, если таблица скромных размеров. Но если у нас, допустим, таблица весит 154 гб, а репликация сломалась, то это беда. Запуск DMS с нуля означает, что запущена транзакция, что в свою очередь означает, что начинают копиться dead rows — в конечном счете, если за этим не следить, то мы положим базу,  так как её процессор перестанет вывозить сканирование такого количества dead rows в поисках нужной строки.

Справедливости ради, это не проблема конкретно DMS. Скорее, это вопросы к Aurora-PostgreSQL, так как сама DMS умеет вставать на временной чекпоинт таблицы (который есть, например, у Оракла), и откопировать только те изменения, которые прошли с того момента времени. Не будет необходимости перекачивать таблицу заново.

Что мы получили в итоге?

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

Новая архитектура теперь выглядит вот так
Новая архитектура теперь выглядит вот так

Мы выстроили процессы дата-пайплайна. Теперь любой дата-инженер, который приходит к нам в команду, понимает, как выстроен процесс от источника до отчёта и как с этим работать (отчасти спасибо dbt за его функциональность и в особенности за dbt-docs).

Мы сильно ускорили работу всей отчётности и доставке инсайтов бизнесу. И оптимизировали затраты на хранилище. 

Моя команда выросла в компетенциях работы c AWS, ведь до этого у нас был классический стек инхаус-разработки. 

Что больше всего радует — мы получили гибкое и масштабируемое архитектурное решение. Недавно Whoosh вышел на рынок одежды и аксессуаров, однако на процессы с данными это никак не повлияло. И аренда самокатов, и продажа одежды с точки зрения потока данных выстроена точно также.
Смекаете?

Бесконечность - не предел

Куда пойдем дальше?

  • Хотим попробовать сделать DataQuality с помощью машинного обучения;

  • Возможно, заменить PostGIS на Python Lambda;

  • Перейти на Geoparquet — интересная, интригующая идея, но под вопросом; 

  • Есть желание всё-таки попробовать универсальное решение под одной крышей — LakeHouse;

  • Продолжим растить компетенции команды в направлении аналитик-инженеров. 


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

Лучше один раз сделать, чем много раз спорить

С ветерком, Whoosh!

Напишите, пожалуйста, в комментариях, о чем было бы интересно прочитать в будущих статьях. Насколько интересна тема гео-аналитики? Стоит ли рассказать про наш опыт с LakeHouse? Возможно, стоит больше рассказать, как мы выстраиваем процессы?

Источник: https://habr.com/ru/companies/whoosh/articles/781406/


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

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

В марте 2022 года американская компания Microsoft объявила о постепенном сворачивании бизнеса в России. Легальное использование ее главного продукта — операционной системы Windows — было поставлено по...
Привет! Меня зовут Андрей Шмиг, я основатель и разработчик платформы DataHub - Crowd Data Sourcing at Hand, своего рода GitHub для данных. В этой статье покажу, каким образом можно монетизировать...
Карты microSD удобно использовать для расширения памяти и хранения файлов на смартфонах и в разных гаджетах, а в Raspberry Pi и других одноплатниках (SBC) у них ещё более ответственная задача: это...
В 2021 году мы запустили собственный сервис внешней аналитики SellerFox. Мы начинали с аналитики всего 4 отечественных маркетплейсов, но за год увеличили их число до 6. По сути, сегодня мы не анализир...
Коллекцию DataArt пополнил уникальный экспонат — тестовый образец персонального компьютера «Радио-86РК», предназначенного для сборки радиолюбителями. Мы попросили Глеба Ницмана — идеолога наш...