單元 6:從 Cloud Datastore 遷移至 Cloud Firestore

1. 總覽

本系列的程式碼研究室 (自助教學和實作教學課程) 旨在引導 Google App Engine (Standard) 開發人員透過一系列遷移作業,協助他們翻新自家應用程式。這類遷移作業大多涉及捨棄原始執行階段套裝組合服務,因為新一代執行階段更有彈性,為使用者提供更多種服務選項。另一種翻新應用程式的方式包括升級至較新的產品,這就是本程式碼研究室的主題。

App Engine 使用者可透過 Cloud NDBCloud Datastore 用戶端程式庫存取 Datastore,無需進一步遷移。不過,Cloud Firestore 代表的是最新、可擴充、高可用性的 NoSQL 資料儲存庫,並採用 Firebase 即時資料庫提供的功能。

如果您是開發人員,且有意運用 Firestore 以充分運用這項服務的功能,或至少有興趣瞭解遷移作業帶來的好處,那麼來這裡就對了!本教學課程說明如何使用 Cloud Datastore 將 App Engine 應用程式遷移至 Cloud Firestore。

你將瞭解如何

  • 瞭解 Datastore 和 Firestore 之間的差異
  • 從 Cloud Datastore 遷移至 Cloud Firestore

軟硬體需求

問卷調查

您會如何使用本程式碼研究室?

只能閱讀 閱讀並完成練習

2. 背景

App Engine 的資料儲存庫自 2013 年開始已成為 Google Cloud Datastore 的產品,目前已可供 App Engine「以外」的開發人員存取。隔年由 Google 收購 Firebase。當時,它是以即時資料庫聞名。

在接下來的幾年中,Firebase 和 Cloud Datastore 團隊將部分 Firebase 功能整合到 Datastore。因此,我們在 2017 年推出了新一代的 Cloud Datastore。為了反映沿用部分 Firebase 功能,我們已將該功能重新命名為 Cloud Firestore

Cloud Firestore 已成為 Google Cloud 專案的預設 NoSQL 儲存機制。新應用程式可以原生使用 Cloud Firestore,而現有的 Datastore 資料庫則在系統內轉換為 Firestore,現在則以「Datastore 模式的 Firestore」運作,以維持與 Datastore 作業的相容性。因此,應用程式只能在其中一種模式下運作 Cloud Firestore,設定後即無法變更。

目前當使用者建立新專案並選取 NoSQL 解決方案時,系統會提示使用者選取 Datastore 模式的 Firestore 或原生模式的 Firestore。使用者新增 Datastore 實體後,就無法變更為 Firestore。此外,選擇 Firestore 原生模式後,使用者就無法再切換回 Datastore (或是 Datastore 模式的 Firestore)。詳情請參閱說明文件中的選擇使用 Cloud Firestore (Datastore 模式) 或原生 Firestore 模式頁面。如要將應用程式遷移至 Firestore,您必須先建立新的專案,將 Datastore 匯出並匯入 Firestore。本教學課程的目的是讓開發人員瞭解使用 Cloud Datastore 和 Cloud Firestore 之間的差異。

我們預期使用者不會執行這項遷移作業,因此可以選擇是否要進行遷移。雖然以原生方式使用 Cloud Firestore 有明顯的優勢,例如用戶端驗證、Firebase 規則整合。當然,Firebase 即時資料庫當然也讓遷移步驟變得「不方便」:

  • 必須使用與目前應用程式專案不同的專案。
  • 如果專案已新增應用程式,就無法切換至原生模式的 Firestore
  • 同樣地,選取原生模式的 Firestore 專案也無法還原至 Datastore 模式的 Firestore。
  • 沒有遷移工具可將某專案的資料串流至另一項專案。
  • Firestore 不支援某些重要的 Datastore 功能,包括命名空間和更高的寫入處理量 (>10k/秒)。
  • 匯出和匯入工具是「原始」以及「全部或不要」情境
    • 如果應用程式含有許多 Datastore 實體,系統可能需要數小時才能匯出並匯入 Firestore。
    • 在這段期間內,應用程式/服務將無法寫入/更新資料。
    • 遷移活動會計入正常用量;可以分散 (如果可能的話,跨越每日配額) 以盡可能降低成本。
    • 由於您的新服務是在其他專案中執行,因此 DNS 更新作業必須有窗口。
  • Datastore 和 Firestore 中的資料模型相似但不同,因此遷移時必須更新應用程式/服務的運作方式。
    • Datastore 的祖系查詢現在是 Firestore 集合查詢 (預設值)
    • Datastore 的廣泛類型查詢為 Firestore 集合群組查詢
    • 索引和處理方式不同等。

即便如此,如果您有應用程式要考慮遷移、準備模擬這類遷移作業,或單純在這裡瞭解 Datastore 與 Firestore,請繼續進行後續步驟。

Python 2 使用者:本選用的遷移程式碼研究室僅以 Python 3 提供,不過 Cloud Firestore 也支援 2.x,因此使用者可以插入使用的差異。舉例來說,Firestore 記錄使用 Unicode 字串 (而非位元組字串),因此 Python 2 字串常值需要使用 u'' 前置指標,代表 2.x store_visit() 函式應如下所示:

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),
    })

除此之外,用戶端程式庫應以類似方式運作。另一個需要考量的問題是 2.x Cloud Firestore 程式庫處於「凍結」狀態時至今日,3.x Firestore 用戶端程式庫會提供越來越多/較新的功能。

後續遷移步驟如下:

  1. 設定/事前作業
  2. 新增 Cloud Firestore 程式庫
  3. 更新應用程式檔案

3. 設定/事前作業

在開始教學課程的主要部分之前,我們先設定專案、取得程式碼,然後部署基準應用程式,以便瞭解要開始使用有效的程式碼。

1. 設定專案

建議您重複使用先前完成單元 3 程式碼研究室的專案。或者,您可以建立新的專案,或是重複使用其他現有專案。請確認專案具備有效的帳單帳戶,且 App Engine (應用程式) 已啟用。

2. 取得基準範例應用程式

本程式碼研究室的必要條件之一,是擁有正常運作的模組 3 範例應用程式。如果沒有單元,請先完成第 3 單元教學課程 (上方連結),再繼續操作。如果您熟悉其中的內容,可以直接擷取下方的單元 3 程式碼。

無論您使用自有還是我們的模組,我們都會開始使用單元 3 程式碼。本單元單元 6 會逐步引導您完成每個步驟,完成後應會類似於 FINISH 點的程式碼。(本教學課程僅適用於 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 (仍在) 參照第三方套裝套件:grpciosetuptools
  • 確保 appengine_config.py 仍會使用 pkg_resourcesgoogle.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 檔案中微幅套件替換,所以現在開始吧。

requirements.txt 中的 google-cloud-datastore 行替換為 google-cloud-firestore,如下所示:

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

建議使用每個程式庫的最新版本。以上都是本文撰寫時的最新最新版本。FINISH 存放區資料夾中的程式碼較常更新,且可能有較新版本。

沒有其他設定變更,因此 app.yamltemplates/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_clientfs_client) 和匯入項目:

from datetime import datetime
from firestore.Query import DESCENDING

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

針對 Datastore,我們要按遞減順序查詢 2020 年 10 月 1 日之後的十個 Visit 實體:

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)。

選用:清除

在準備進入下一個遷移程式碼研究室之前,您該如何清除所用資源,以免產生費用?身為現有開發人員,您可能已經能快速瞭解 App Engine 的定價資訊

選用:停用應用程式

如果尚未準備好進行下一個教學課程,請停用應用程式,以免產生費用。當您準備好前往下一個程式碼研究室時,即可重新啟用。停用的應用程式不會有任何流量而產生費用,不過,如果應用程式超過免費配額,我們就會向您收取 Firestore 用量的費用,因此刪除該用量即可低於限製配額。

另一方面,如果不想繼續進行遷移作業,想要完全刪除所有項目,請關閉專案

後續步驟

除了本教學課程之外,您還可以考慮下列幾個遷移模組程式碼研究室:

  • 單元 7:App Engine 推送工作佇列 (如果您使用 [push] 工作佇列,則為必要項目)
    • 將 App Engine taskqueue 推送工作新增至模組 1 應用程式
    • 協助使用者做好準備,以便遷移至 Cloud Tasks
  • 模組 4:透過 Docker 遷移至 Cloud Run
    • 將應用程式容器化,讓應用程式透過 Docker 在 Cloud Run 中執行
    • 這項遷移作業可讓您繼續使用 Python 2。
  • 單元 5:遷移至 Cloud Run 與 Cloud Buildpacks
    • 將應用程式容器化,讓應用程式透過 Cloud Buildpacks 在 Cloud Run 中執行
    • 您不需要瞭解 Docker、容器或 Dockerfile 的任何資訊。
    • 需要將應用程式遷移至 Python 3 (Buildpacks 不支援 Python 2)

7. 其他資源

App Engine 遷移模組程式碼研究室問題/意見回饋

如果您在本程式碼研究室中發現任何問題,請先搜尋您的問題再提出申請。搜尋及建立新問題的連結:

遷移資源

下表提供模組 3 (START) 和單元 6 (FINISH) 的存放區資料夾連結。您也可以透過所有 App Engine 遷移作業的存放區存取這些檔案,複製或下載 ZIP 檔案。

Codelab

Python 2

Python 3

Module 3

(程式碼)

程式碼

Module 6

(不適用)

程式碼

App Engine 資源

以下是這項特定遷移作業的其他資源: