Модуль 6. Миграция из Cloud Datastore в Cloud Firestore

1. Обзор

Эта серия практических занятий (в удобном для вас темпе) призвана помочь разработчикам Google App Engine (Standard) модернизировать свои приложения, проведя их через ряд миграций. Большинство таких миграций связаны с отказом от встроенных сервисов оригинальной среды выполнения, поскольку среды выполнения следующего поколения более гибкие, предоставляя пользователям большее разнообразие вариантов сервисов. Другой способ модернизации приложения — это обновление до более новой версии, и именно этому посвящено данное практическое занятие.

Пользователи App Engine, обращающиеся к Datastore с помощью клиентских библиотек Cloud NDB или Cloud Datastore, могут сразу же перейти на другую платформу и не нуждаются в дальнейшей миграции. Однако Cloud Firestore представляет собой новейшее масштабируемое, высокодоступное хранилище данных NoSQL с функциями базы данных реального времени Firebase .

Если вы разработчик, который хочет использовать Firestore, чтобы воспользоваться его возможностями, или хотя бы заинтересован в изучении процесса миграции, то вы попали по адресу. В этом руководстве вы узнаете, как перенести приложение App Engine, использующее Cloud Datastore, в Cloud Firestore.

Вы узнаете, как

  • Разберитесь в различиях между Datastore и Firestore.
  • Переход с Cloud Datastore на Cloud Firestore

Что вам понадобится

Опрос

Как вы будете использовать этот практический урок?

Прочитайте только до конца. Прочитайте текст и выполните упражнения.

2. Предыстория

В 2013 году хранилище данных App Engine стало самостоятельным продуктом — Google Cloud Datastore, — и теперь доступно разработчикам за пределами App Engine. В следующем году компания Firebase была приобретена Google. В то время она была известна своей базой данных реального времени.

В течение следующих нескольких лет команды Firebase и Cloud Datastore работали над интеграцией некоторых функций Firebase в Datastore. В результате в 2017 году было выпущено следующее поколение Cloud Datastore. Чтобы отразить унаследованные функции Firebase, оно было переименовано в Cloud Firestore .

Cloud Firestore стал стандартным механизмом хранения NoSQL-данных для проектов Google Cloud. Новые приложения могут использовать Cloud Firestore напрямую, в то время как существующие базы данных Datastore были преобразованы в Firestore и теперь работают в режиме «Firestore в Datastore» для сохранения совместимости с операциями Datastore. В результате приложения могут использовать Cloud Firestore только в одном из этих режимов, и после установки его нельзя изменить.

В настоящее время при создании новых проектов и выборе решения NoSQL пользователям предлагается выбрать либо режим Firestore в Datastore, либо режим Firestore в нативном режиме. После добавления сущностей Datastore пользователи не могут переключиться на Firestore, и аналогично, после выбора нативного режима Firestore они больше не могут вернуться к Datastore (или, точнее, к Firestore в режиме Datastore). Более подробную информацию можно найти на странице документации, посвященной выбору между режимом Cloud Firestore в Datastore и нативным режимом Firestore . Для миграции приложения в Firestore необходимо создать новый проект, экспортировать Datastore, а затем импортировать его в Firestore. Цель этого руководства — дать разработчикам представление о различиях между использованием Cloud Datastore и Cloud Firestore.

Мы не ожидаем, что пользователи будут выполнять эту миграцию , поэтому она является необязательной. Хотя использование Cloud Firestore в собственном виде имеет очевидные преимущества, такие как аутентификация клиента, интеграция с правилами Firebase и, конечно же, база данных Firebase в реальном времени, этапы миграции являются «неудобными»:

  • Вам необходимо использовать другой проект, отличный от проекта вашего текущего приложения.
  • В проекте, где приложение добавило сущности Datastore, переключение на Firestore в нативном режиме невозможно.
  • Аналогичным образом, проект, в котором выбран Firestore в нативном режиме, не может вернуться к использованию Firestore в режиме хранилища данных.
  • Не существует инструмента миграции, который мог бы передавать данные из одного проекта в другой.
  • Некоторые важные функции Datastore, включая пространства имен и более высокую пропускную способность записи (>10 тыс. записей в секунду), недоступны в Firestore.
  • Инструменты экспорта и импорта являются «примитивными» и работают по принципу «всё или ничего».
    • Если ваше приложение содержит много сущностей из Datastore, экспорт и импорт в Firestore может занять много часов.
    • В течение этого времени ваше приложение/сервис не сможет записывать/обновлять данные.
    • Миграционные мероприятия учитываются в рамках обычного использования ресурсов; возможно, вам стоит распределить их (по возможности, по суточным квотам), чтобы минимизировать затраты.
    • Поскольку ваш новый сервис работает в другом проекте, вам потребуется время для распространения обновлений DNS.
  • Datastore и Firestore имеют схожие, но разные модели данных, поэтому для миграции потребуется обновить принцип работы приложения/сервиса.
    • Запросы к родительским элементам из Datastore теперь являются запросами к коллекциям Firestore (по умолчанию).
    • Запросы широкого типа из хранилища данных — это запросы к группам коллекций Firestore.
    • Индексы и обработка данных различаются и т.д.

Учитывая все вышесказанное, если у вас есть достаточно простое приложение, которое вы планируете перенести, вы готовитесь к имитации такой миграции или просто хотите узнать больше о Datastore и Firestore, то, пожалуйста, продолжайте!

Пользователи Python 2: Этот необязательный практический пример по миграции представлен только для Python 3, однако, поскольку Cloud Firestore также поддерживает версию 2.x, пользователи могут интерполировать различия в использовании. Например, записи Firestore используют строки Unicode (вместо строк байтов), поэтому для строковых литералов Python 2 требуется ведущий индикатор u'' , то есть функция store_visit() в версии 2.x будет выглядеть следующим образом:

def store_visit(remote_addr, user_agent):
    doc_ref = fs_client.collection(u'Visit')
    doc_ref.add({
        u'timestamp': datetime.now(),
        u'visitor': u'{}: {}'.format(remote_addr, user_agent),
    })

В остальном, клиентская библиотека должна работать аналогично. Единственный другой момент, который следует учитывать, заключается в том, что разработка библиотеки Cloud Firestore версии 2.x "заморожена", поэтому все больше и больше новых функций будут доступны только в клиентской библиотеке Firestore версии 3.x.

Для продолжения миграции выполните следующие основные шаги, описанные в этом руководстве:

  1. Подготовка/Настройка
  2. Добавить библиотеку Cloud Firestore
  3. Обновите файлы приложения

3. Подготовка/Предварительные работы

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

1. Настройка проекта

Мы рекомендуем использовать тот же проект, что и для выполнения практического задания модуля 3. В качестве альтернативы вы можете создать совершенно новый проект или использовать уже существующий. Убедитесь, что у проекта есть активный платежный аккаунт и включена поддержка App Engine (приложения).

2. Получите базовый образец приложения.

Одно из предварительных условий для выполнения этого практического задания — наличие работающего примера приложения из Модуля 3. Если у вас его нет, пройдите руководство по Модулю 3 (ссылка выше), прежде чем переходить к следующему шагу. В противном случае, если вы уже знакомы с его содержанием, вы можете просто начать, скачав код Модуля 3 ниже.

Независимо от того, используете ли вы свой или наш код, мы НАЧНЕМ с кода Модуля 3. Этот практический урок по Модулю 6 шаг за шагом проведет вас через каждый этап, и по завершении он должен выглядеть как код в КОНЦЕ. (Этот урок доступен только для Python 3.)

Структура каталога файлов Модуля 3 (ваших или наших) должна выглядеть следующим образом:

$ ls
README.md               main.py                 templates
app.yaml                requirements.txt

3. (Повторное) развертывание приложения модуля 3.

Осталось выполнить следующие подготовительные шаги:

  1. При необходимости ознакомьтесь с инструментом командной строки gcloud .
  2. (При необходимости) повторно разверните код Модуля 3 в App Engine.

После успешного выполнения этих шагов и подтверждения работоспособности системы, мы перейдем к следующему этапу этого руководства, начиная с конфигурационных файлов.

Требования к Python 2

  • Убедитесь, что app.yaml (по-прежнему) содержит ссылки на сторонние пакеты: grpcio и setuptools .
  • Убедитесь, что appengine_config.py по-прежнему использует pkg_resources и google.appengine.ext.vendor для указания приложению на ресурсы сторонних разработчиков.
  • В следующем разделе, при обновлении файла requirements.txt , необходимо использовать google-cloud-firestore==1.9.0 поскольку это последняя версия клиентской библиотеки Python Firestore, совместимая с версией 2.x.
    • Если в вашем requirements.txt есть запись для google-cloud-core , оставьте её как есть.
    • Удалите lib и переустановите её с помощью pip install -t lib -r requirements.txt .`.

4. Обновите конфигурационные файлы (добавьте библиотеку Cloud Firestore).

После настройки следующими шагами являются обновление конфигурации, а затем и файлов приложения. В первом случае единственное изменение конфигурации — это небольшая замена пакетов в файле requirements.txt , поэтому давайте сделаем это сейчас.

Замените строку google-cloud-datastore на google-cloud-firestore в requirements.txt , чтобы получилось следующее:

Flask==1.1.2
google-cloud-firestore==2.0.2

Мы рекомендуем использовать последние версии каждой библиотеки; указанные выше номера версий являются актуальными на момент написания этого текста. Код в папке репозитория FINISH обновляется чаще и может содержать более новую версию.

Никаких других изменений в конфигурации не производилось, поэтому файлы app.yaml и templates/index.html остаются без изменений.

5. Обновите файлы приложения.

Приложение состоит всего из одного файла — main.py , поэтому все изменения в этом разделе затрагивают только этот файл.

1. Импорт

Переход на другой способ импорта пакетов — это незначительное изменение, заключающееся в замене datastore на firestore :

  • ДО:
from google.cloud import datastore
  • ПОСЛЕ:
from google.cloud import firestore

2. Доступ к Firestore

После инициализации Flask создайте клиент Firestore. Внесите аналогичные изменения, но для инициализации клиента:

  • ДО:
app = Flask(__name__)
ds_client = datastore.Client()
  • ПОСЛЕ:
app = Flask(__name__)
fs_client = firestore.Client()

Выполнив миграцию с Cloud NDB на Cloud Datastore, вы уже проделали основную работу по переходу на Cloud Firestore. В Datastore вы создаете записи данных в виде сущностей, состоящих из общих свойств, и группируете их по ключам . Записи данных в Firestore представляют собой документы, состоящие из пар ключ-значение и сгруппированные в коллекции . Миграция с Datastore требует учета этих различий, поскольку они проявятся как при создании записей данных, так и при выполнении запросов к ним. Результаты могут различаться в зависимости от сложности вашего кода для Datastore.

В Datastore запросы выполняются на основе типа сущности, а также критериев фильтрации и сортировки . В Firestore запросы к данным аналогичны. Рассмотрим простой пример, предполагая следующие значения запроса, клиентов ( ds_client или fs_client соответственно) и импорты:

from datetime import datetime
from firestore.Query import DESCENDING

OCT1 = datetime(2020, 10, 1)
LIMIT = 10

Для Datastore выполним запрос к десяти самым последним записям Visit датированным позже 1 октября 2020 года, в порядке убывания:

query = ds_client.query(kind='Visit')
query.add_filter('timestamp', '>=', datetime(2020, 10, 1))
query.order = ['-timestamp']
return query.fetch(limit=LIMIT)

То же самое я делаю для Firestore из коллекции Visit :

query = fs_client.collection('Visit')
query.where('timestamp', '>=', datetime(2020, 10, 1))
query.order_by('timestamp', direction=DESCENDING)
return query.limit(LIMIT).stream()

Пример запроса к приложению проще (без условия "WHERE"). Для повторения, вот код Cloud Datastore:

  • ДО:
def store_visit(remote_addr, user_agent):
    entity = datastore.Entity(key=ds_client.key('Visit'))
    entity.update({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })
    ds_client.put(entity)

def fetch_visits(limit):
    query = ds_client.query(kind='Visit')
    query.order = ['-timestamp']
    return query.fetch(limit=limit)

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

  • ПОСЛЕ:
def store_visit(remote_addr, user_agent):
    doc_ref = fs_client.collection('Visit')
    doc_ref.add({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })

def fetch_visits(limit):
    visits_ref = fs_client.collection('Visit')
    visits = (v.to_dict() for v in visits_ref.order_by('timestamp',
            direction=firestore.Query.DESCENDING).limit(limit).stream())
    return visits

Основная функция root() остается неизменной, как и файл шаблона index.html . Дважды проверьте изменения, сохраните, разверните и проверьте.

6. Подведение итогов/Завершение

Развертывание приложения

Переразверните приложение с помощью gcloud app deploy и убедитесь, что оно работает. Теперь ваш код должен соответствовать коду из репозитория модуля 6 (или версии 2.x, если вы предпочитаете именно её).

Если вы начали этот цикл, не выполнив ни одного из предыдущих практических заданий, само приложение не изменится; оно регистрирует все посещения главной веб-страницы ( / ) и выглядит так после того, как вы достаточное количество раз посетите сайт:

приложение visitme

Поздравляем с завершением этой необязательной миграции в Модуле 6. Вероятно, это одна из последних, если не последняя, ​​миграция, которую вы можете осуществить в отношении хранилища данных App Engine. В качестве альтернативы вы можете рассмотреть контейнеризацию вашего приложения для Cloud Run, если вы еще этого не сделали (см. Модули 4 и 5, ссылки на практические задания ниже).

Необязательно: Уборка

Как насчет того, чтобы навести порядок и избежать выставления счетов до тех пор, пока вы не будете готовы перейти к следующему этапу миграции на CodeLab? Как опытные разработчики, вы, вероятно, уже знакомы с информацией о ценах App Engine .

(Необязательно): Отключить приложение

Если вы ещё не готовы перейти к следующему уроку, отключите своё приложение, чтобы избежать списания средств. Когда вы будете готовы перейти к следующему практическому занятию, вы можете снова включить его. Пока ваше приложение отключено, оно не будет получать трафик, за который могли бы взиматься плата, однако с вас могут взиматься дополнительные платежи за использование Firestore , если оно превысит бесплатную квоту , поэтому удалите достаточное количество приложений, чтобы оно не превышало этот лимит.

С другой стороны, если вы не собираетесь продолжать миграции и хотите полностью удалить все, вы можете закрыть свой проект .

Следующие шаги

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

  • Модуль 7: Очереди задач App Engine Push (необходим, если вы используете очереди задач [push])
    • Добавляет задачи из очереди taskqueue App Engine в приложение Модуля 1.
    • В модуле 8 осуществляется подготовка пользователей к миграции в облако.
  • Модуль 4: Миграция в облако. Запуск с помощью Docker.
    • Создайте контейнер для запуска вашего приложения в Cloud Run с помощью Docker.
    • Эта миграция позволит вам остаться на Python 2.
  • Модуль 5: Миграция в облако. Запуск с помощью Cloud Buildpacks.
    • Создайте контейнер для запуска вашего приложения в Cloud Run с помощью Cloud Buildpacks.
    • Вам не нужно ничего знать о Docker, контейнерах или Dockerfile .
    • Для корректной работы требуется, чтобы ваше приложение уже было переведено на Python 3 (Buildpacks не поддерживает Python 2).

7. Дополнительные ресурсы

Проблемы/обратная связь по модулю миграции App Engine на Codelabs

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

Миграционные ресурсы

Ссылки на папки репозитория для Модуля 3 (НАЧАЛО) и Модуля 6 (ЗАВЕРШЕНИЕ) можно найти в таблице ниже. К ним также можно получить доступ из репозитория для всех миграций App Engine , который можно клонировать или загрузить в виде ZIP-файла.

Кодлаб

Python 2

Python 3

Модуль 3

( код )

код

Модуль 6

(н/д)

код

Ресурсы App Engine

Ниже приведены дополнительные ресурсы, касающиеся данной конкретной миграции: