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

1. 總覽

本系列程式碼研究室 (可自行調整步調的實作教學課程) 旨在協助 Google App Engine (標準) 開發人員完成一系列遷移作業,進而將應用程式現代化。這類遷移作業大多是為了捨棄原始的執行階段組合服務,因為新一代執行階段更具彈性,可為使用者提供更多服務選項。升級至新版產品也是應用程式現代化的方式之一,而這正是本程式碼研究室的主題。

如果 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 的 Datastore 在 2013 年成為獨立產品,也就是 Google Cloud Datastore,現在 App Engine 以外的開發人員也能存取。隔年,Google 收購了 Firebase。當時,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)。如需更多詳細資料,請參閱說明文件中的「選擇使用 Datastore 模式的 Cloud Firestore 或原生 Firestore 模式」頁面。如要將應用程式遷移至 Firestore,必須建立新專案、匯出 Datastore,然後匯入 Firestore。本教學課程旨在讓開發人員瞭解使用 Cloud Datastore 和 Cloud Firestore 的差異。

這項遷移作業並非使用者必須執行,因此屬於選用性質。雖然使用 Cloud Firestore 原生服務有明顯優勢,例如用戶端驗證、Firebase 規則整合,以及 Firebase 即時資料庫,但遷移步驟「不方便」:

  • 必須使用與目前應用程式專案不同的專案。
  • 如果應用程式已在專案中新增 Datastore 實體,則無法切換為原生模式的 Firestore
  • 同樣地,選取原生模式 Firestore 的專案也無法還原為 Datastore 模式的 Firestore。
  • 沒有任何遷移工具可將資料從一個專案串流至另一個專案。
  • Firestore 支援部分重要的 Datastore 功能,包括命名空間和更高的寫入輸送量 (超過 10,000 次/秒)。
  • 匯出和匯入工具屬於「原始」工具,而且只能「全有或全無」。
    • 如果應用程式有大量 Datastore 實體,匯出並匯入 Firestore 可能需要數小時。
    • 在這段期間,您的應用程式/服務將無法寫入/更新資料。
    • 遷移活動會計入正常用量,建議您盡可能分散遷移作業 (如果每日配額允許),以盡量降低費用。
    • 由於新服務是在不同專案中執行,因此需要一段時間才能傳播 DNS 更新。
  • Datastore 和 Firestore 的資料模型類似但不同,因此遷移時需要更新應用程式/服務的運作方式。
    • Datastore 的祖先查詢現在是 Firestore 的集合查詢 (預設)
    • Datastore 的廣泛類型查詢是 Firestore 的集合群組查詢
    • 索引和處理方式不同等。

不過,如果您要遷移的應用程式相當簡單,或是準備模擬遷移作業,或只是想瞭解 Datastore 與 Firestore 的差異,請繼續閱讀!

Python 2 使用者:這個選用的遷移 Codelab 僅以 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
  • AFTER:
from google.cloud import firestore

2. Firestore 存取權

初始化 Flask 後,請建立 Firestore 用戶端。進行與上述類似的變更,但這次是針對用戶端初始化:

  • 更新前:
app = Flask(__name__)
ds_client = datastore.Client()
  • AFTER:
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 日之後的最新 10 個 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 collection

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 後,您會發現建立新文件與實體類似,查詢方式也如先前所示。

  • AFTER:
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 (請參閱下方連結的 Codelab 模組 4 和 5)。

選用:清除

在準備進行下一個遷移程式碼研究室之前,請先清除資源,以免產生帳單費用。身為現有開發人員,您可能已熟悉 App Engine 的價格資訊

選用:停用應用程式

如果還沒準備好進行下一個教學課程,請停用應用程式,以免產生費用。準備好進行下一個程式碼研究室時,可以重新啟用這項功能。應用程式停用後,不會產生任何流量,因此不會產生費用。不過,如果 Firestore 用量超出免費配額,您仍須支付相關費用,因此請刪除足夠的資料,確保用量低於上限。

另一方面,如果您不打算繼續遷移,並想完全刪除所有內容,可以關閉專案

後續步驟

除了本教學課程,您也可以考慮其他遷移模組程式碼研究室:

  • 單元 7:App Engine 發送工作佇列 (如果您使用 [發送] 工作佇列,則為必填)
    • 將 App Engine taskqueue 推送工作新增至模組 1 應用程式
    • 為第 8 單元中遷移至 Cloud Tasks 的作業做好準備
  • 單元 4:使用 Docker 遷移至 Cloud Run
    • 使用 Docker 將應用程式容器化,以便在 Cloud Run 上執行
    • 遷移後,您仍可使用 Python 2。
  • 單元 5:使用 Cloud Buildpacks 遷移至 Cloud Run
    • 使用 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

(代碼)

code

Module 6

(不適用)

code

App Engine 資源

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