Upgrade to Pro — share decks privately, control downloads, hide ads and more …

От фикстур до декларативного описания трансформ...

От фикстур до декларативного описания трансформаций данных с лайва. Формируем данные для стейдж-окружений Django приложений.

Сергей Захарченко (Destiny Production, Системный Архитектор) @ Moscow Python Meetup 73 (Online)

"Стейдж-окружение требует данных, которые обладают характеристикам данных продакшена. В идеале это семплированная и санитаризированная копия БД приложения с лайва — без ПД пользователей и критичной бизнес-информации.

Скрипты санитаризации дампов очень быстро превращаются в плоходокументируемое императивное спагетти, которое никто не любит поддерживать. Я расскажу подход, который основан на декларативном описании трансформаций данных прямо в классе моделей, dumpdata на стероидах, а также то как же этот дамп накатить на стейдж-окружение".

Видео: http://www.moscowpython.ru/meetup/73/from-fixtures-to-declarative-description/

Moscow Python Meetup

May 28, 2020
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. ГОТОВИМ СТЕЙДЖ ГОТОВИМ СТЕЙДЖ От фикстур до декларативного описания трансформаций

    данных с лайва. Формируем данные для стейжинг-окружений Django приложений. Сергей Захарченко, 28 мая 2020
  2. DESTINY.GAMES / THE ABYSS DESTINY.GAMES / THE ABYSS Игровая платформа

    (веб и десктоп игры) Издатели и пользователи Монетизация (фиат и [роскомнадзор]) Контент издателей - Объемные игры, патчи, ... Пользовательский контент - Аватарки, облачные сохранения ...
  3. 1. А НУЖЕН ЛИ ВАМ 1. А НУЖЕН ЛИ ВАМ

    СТЕЙДЖ? СТЕЙДЖ? Наброс
  4. Если у вас настроены частичные выкатки новых фич на %

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

    деплой или потюнить CI Если хочется провести стресс-тест но не хочется ронять прод Если есть лишние ресурсы ... то возможно ответ -- да
  6. С вами интегрируются другие сервисы и вы проводите оплаты в

    реальных деньгах (наш кейс) ... то ответ скорее всего -- да
  7. 2. ПРОБЛЕМЫ 2. ПРОБЛЕМЫ Окружение сделали, код выкатили, но... Нужно

    как-то копировать огромный объем контента между бакетами и так чтобы это занимало немного времени и ресурсов Нужно как-то подготавливать данные из db
  8. ДАННЫЕ ДАННЫЕ ФАЙЛОХРАНИЛИЩЕ ФАЙЛОХРАНИЛИЩЕ Разные версии игр, патчи, пользовательский контент

    (аватарки, облачные сохранения ...) Все что есть на проде - должно быть доступно на стейдже. Все изменения на стейдже должны остаться там и не утечь на прод Минимизируем затраты (как облачные так и по времени на разворачивание)
  9. ГИБРИДНЫЙ COPY-ON-WRITE СТОРАДЖ ДЛЯ ДЖАНГО ГИБРИДНЫЙ COPY-ON-WRITE СТОРАДЖ ДЛЯ ДЖАНГО

    Чтение: Если ресурс есть в бакете стейджа - отдаем его оттуда Если IOError, обращаемся к низлежащему ReadOnly стораджу на прод (ACL- урезанные креды) Запись: Пишем в бакет стейджа Резет: Нюкаем бакет
  10. DB DB POSTGRES POSTGRES JSON/SQL/... ФИКСТУРА JSON/SQL/... ФИКСТУРА (+) Удобно

    накатывать (-) Устаревает (меняется схема и констрейны, никто не любит их обновлять) (-) Не показывает всей картины того как пользуются вашим сервисом на проде ГЕНЕРАТОРЫ ДАННЫХ ГЕНЕРАТОРЫ ДАННЫХ (+) Не устаревают (-) Черные лебеди. Не показывает всей картины того как пользуются вашим сервисом реальные пользователи. ДАННЫЕ С ЛАЙВА ДАННЫЕ С ЛАЙВА (+) Идеально, но... (-) Персональные данные (-) Финансовые данные (-) Расхождения между данными завязанными на внешние сервисы
  11. КАК АНОНИМИЗИРОВАТЬ КАК АНОНИМИЗИРОВАТЬ ДАННЫЕ? ДАННЫЕ? Чувствительные данные не должны

    уехать за пределы продакшена Желательно либо через расширение SQL (иначе будет очень много Update) либо на лету при дампе Как именно анонимизировать? (Маскирование, добавление шума, псевдонимизация, замена на random-but-plausible)
  12. EXPORTSTRATEGY EXPORTSTRATEGY Псевдокод class Profile(models.Model): first_name = models.CharField() last_name =

    models.CharField() email = models.CharField() user = models.ForeignKey(User) export_strategy = ExportStrategy( is_sensitive = False, override_queryset = lambda cls: cls.objects.exclude(email__icontains='@qa.myservice.com') override_natural_key = { 'user': '[email protected]' } sanitize_fields = { 'first_name': Anonymize.fake_first_name, 'last_name': Anonymize.fake_last_name, 'email': Anonymize.fake_email } )
  13. СХЕМА МОДЕЛЕЙ СХЕМА МОДЕЛЕЙ Модели зависят друг от друга через

    FK. Если в дампе не окажется зависимости, то импорт упадет.
  14. DUMPDATA DUMPDATA 1. Собирает модели 2. Сортирует зависимости 3. Использует

    сериализатор нужного формата, трансформирующий Model в json/xml/...
  15. DUMPDATA+ DUMPDATA+ 1. Собирает модели, 2. Строит граф и отбрасывает

    связные подграфы в которых встречается is_sensitive = True и нет переопределения 3. Сортирует зависимости 4. Использует сериализатор, трансформирующий Model в json/xml/..., предварительно пропустив поля в sanitize_ elds через обработчики class Profile(models.Model): first_name = models.CharField() last_name = models.CharField() email = models.CharField() user = models.ForeignKey(User) export_strategy = ExportStrategy( is_sensitive = False, override_queryset = lambda cls: cls.objects.exclude(email__icontains='@qa.myservice.com') override_natural_key = { 'user': '[email protected]' } sanitize_fields = { 'first_name': Anonymize.fake_first_name, 'last_name': Anonymize.fake_last_name, 'email': Anonymize.fake_email } )
  16. POSTGRESQL_ANONYMIZER POSTGRESQL_ANONYMIZER https://postgresql-anonymizer.readthedocs.io/en/latest/ SELECT anon.fake_first_name(), anon.fake_last_name(), anon.fake_email(); fake_first_name | fake_last_name

    | fake_email -----------------+----------------+----------------------- marcel | Dodgson | [email protected] SELECT * FROM patient; ssn | firstname | zipcode | birth | disease -------------+-----------+---------+------------+--------------- 253-51-6170 | Alice | 47012 | 1989-12-29 | Heart Disease 091-20-0543 | Bob | 42678 | 1979-03-22 | Allergy 565-94-1926 | Caroline | 42678 | 1971-07-22 | Heart Disease 510-56-7882 | Eleanor | 47909 | 1989-12-15 | Acne CREATE MATERIALIZED VIEW generalized_patient AS SELECT 'REDACTED'::TEXT AS firstname, anon.generalize_int4range(zipcode,1000) AS zipcode, anon.generalize_daterange(birth,'decade') AS birth, disease FROM patient; firstname | zipcode | birth | disease -----------+---------------+-------------------------+--------------- REDACTED | [47000,48000) | [1980-01-01,1990-01-01) | Heart Disease REDACTED | [42000,43000) | [1970-01-01,1980-01-01) | Allergy REDACTED | [42000,43000) | [1970-01-01,1980-01-01) | Heart Disease REDACTED | [47000,48000) | [1980-01-01,1990-01-01) | Acne
  17. FLOW FLOW ВЫГРУЗКА ВЫГРУЗКА 1. В кодовой базе определяем ExportStrategy

    для моделей, правила анонимизации и те модели которые не должны попасть в выгрузку 2. Запускаем расширенный вариант dumpdata. НАКАТ НА СТЕЙДЖ НАКАТ НА СТЕЙДЖ 1. Очищаем БД 2. Применяем миграции 3. Накатываем очищенные и анонимизированные данные с прода 4. Делаем провижн, связанный с бизнес-логикой приложения. В нашем случае Заводим аккаунты для QA, начисляем вирутальной валюты ... Заводим модели необходимые для связи со внешними сервисами Прочее