יחידת לימוד 6: מעבר מ-Cloud Datastore ל-Cloud Firestore

1. סקירה כללית

סדרת שיעורי ה-Codelab (הדרכות מעשיות בקצב אישי) הזו נועדה לעזור למפתחים של Google App Engine (Standard) לחדש את האפליקציות שלהם באמצעות סדרה של העברות. ברוב המקרים, ההעברות האלה כוללות מעבר משירותים מקוריים שצורפו לזמן הריצה, כי זמני הריצה מהדור הבא גמישים יותר ומאפשרים למשתמשים לבחור מתוך מגוון רחב יותר של אפשרויות שירות. דרך נוספת למודרניזציה של אפליקציה היא שדרוג למוצר חדש יותר, וזה הנושא של ה-codelab הזה.

משתמשי App Engine שניגשים ל-Datastore באמצעות ספריות הלקוח Cloud NDB או Cloud Datastore לא צריכים לבצע מיגרציה נוספת. עם זאת, Cloud Firestore הוא מאגר הנתונים העדכני ביותר, ניתן להרחבה, בעל זמינות גבוהה, לא יחסי (NoSQL) וכולל תכונות מ-Firebase Realtime Database.

אם אתם מפתחים שרוצים להשתמש ב-Firestore כדי ליהנות מהתכונות שלו, או לפחות רוצים לדעת מה כולל המעבר, הגעתם למקום הנכון. במדריך הזה נלמד איך להעביר אפליקציית App Engine שמשתמשת ב-Cloud Datastore אל Cloud Firestore.

מה תלמדו

  • הסבר על ההבדלים בין Datastore לבין Firestore
  • העברה מ-Cloud Datastore ל-Cloud Firestore

מה תצטרכו

סקר

איך תשתמשו ב-Codelab הזה?

רק קוראים את הטקסט קוראים את הטקסט ומבצעים את התרגילים

2. רקע

‫Datastore של App Engine הפך למוצר עצמאי בשנת 2013, Google Cloud Datastore, ועכשיו מפתחים מחוץ ל-App Engine יכולים לגשת אליו. בשנה שלאחר מכן, Google רכשה את Firebase. באותה תקופה, היא הייתה ידועה בזכות מסד הנתונים בזמן אמת שלה.

במהלך השנים הבאות, הצוותים של Firebase ו-Cloud Datastore עבדו על שילוב חלק מהתכונות של Firebase ב-Datastore. כתוצאה מכך, בשנת 2017 השקנו את הדור הבא של Cloud Datastore. כדי לשקף את הירושה של חלק מהתכונות של Firebase, הוא עבר מיתוג מחדש כ-Cloud Firestore.

מנגנון האחסון NoSQL של Cloud Firestore הפך לברירת המחדל בפרויקטים של Google Cloud. אפליקציות חדשות יכולות להשתמש ב-Cloud Firestore באופן מקורי, ומסדי נתונים קיימים ב-Datastore הומרו ל-Firestore מאחורי הקלעים, ועכשיו הם פועלים כ-Firestore במצב Datastore כדי לשמור על תאימות לפעולות Datastore. כתוצאה מכך, אפליקציות יכולות להפעיל את Cloud Firestore רק באחד מהמצבים האלה, ואחרי שמגדירים את המצב אי אפשר לשנות אותו.

בשלב הזה, כשמשתמשים יוצרים פרויקטים חדשים ובוחרים פתרון NoSQL, הם מתבקשים לבחור בין Firestore במצב Datastore לבין Firestore במצב מקורי. אחרי שהמשתמשים מוסיפים ישויות Datastore, הם לא יכולים לעבור ל-Firestore. באופן דומה, אחרי שהם בוחרים במצב Datastore של Firestore, הם לא יכולים לחזור ל-Datastore (או ליתר דיוק, ל-Firestore במצב Datastore). פרטים נוספים זמינים במאמרי העזרה בחירה בין Cloud Firestore במצב Datastore לבין מצב Firestore מקורי. כדי להעביר אפליקציה ל-Firestore, צריך ליצור פרויקט חדש, לייצא את Datastore ואז לייבא אותו ל-Firestore. המדריך הזה נועד לתת למפתחים מושג לגבי ההבדלים בין שימוש ב-Cloud Datastore לבין שימוש ב-Cloud Firestore.

אנחנו לא מצפים שהמשתמשים יבצעו את ההעברה הזו, ולכן היא אופציונלית. יש יתרונות ברורים לשימוש ב-Cloud Firestore באופן מקורי, כמו אימות לקוח, שילוב של כללי Firebase וכמובן, מסד הנתונים בזמן אמת של Firebase, אבל שלבי ההעברה הם "לא נוחים":

  • חובה להשתמש בפרויקט אחר מהפרויקט של האפליקציה הנוכחית.
  • אי אפשר להעביר פרויקט שבו נוספו ישויות Datastore לאפליקציה ל-Firestore במצב Native
  • באופן דומה, בפרויקט שבו נבחר Firestore במצב Native, אי אפשר לחזור ל-Firestore במצב Datastore.
  • אין כלי להעברת נתונים שיכול להזרים נתונים מפרויקט אחד לפרויקט אחר.
  • חלק מהתכונות החשובות של Datastore, כולל מרחבי שמות וקצב העברת נתונים גבוה יותר לכתיבה (>10k/s), לא זמינות מ-Firestore.
  • הכלים לייצוא ולייבוא הם 'פרימיטיביים' ופועלים לפי תרחישי 'הכול או כלום'.
    • אם באפליקציה שלכם יש הרבה ישויות Datastore, יכול להיות שייקח הרבה שעות לייצא אותן ואז לייבא אותן ל-Firestore.
    • במהלך התקופה הזו, האפליקציה או השירות לא יוכלו לכתוב או לעדכן נתונים.
    • פעולות ההעברה נספרות כשימוש רגיל, ולכן כדאי לפרוס אותן (במסגרת המכסות היומיות, אם אפשר) כדי לצמצם את העלויות.
    • מכיוון שהשירות החדש פועל בפרויקט אחר, צריך להמתין עד שהעדכונים ב-DNS יתעדכנו.
  • ל-Datastore ול-Firestore יש מודלים דומים אבל שונים של נתונים, ולכן כדי לבצע מיגרציה צריך לעדכן את אופן הפעולה של האפליקציה או השירות
    • שאילתות לגבי ישות אב מ-Datastore הן עכשיו שאילתות של אוספים ב-Firestore (ברירת המחדל)
    • שאילתות רחבות מ-Datastore הן שאילתות של קבוצת אוספים ב-Firestore
    • המדדים והטיפול שונים וכו'.

אחרי כל ההקדמה הזו, אם יש לכם אפליקציה פשוטה יחסית שאתם רוצים להעביר, אם אתם מתכוננים להדמיה של העברה כזו או אם אתם פשוט רוצים ללמוד על Datastore לעומת Firestore, אתם מוזמנים להמשיך לקרוא.

משתמשי Python 2: ה-codelab הזה להעברה הוא אופציונלי ומוצג רק ב-Python 3. עם זאת, מכיוון ש-Cloud Firestore תומך גם בגרסה 2.x, המשתמשים יכולים להסיק את ההבדלים בשימוש. לדוגמה, רשומות של Firestore משתמשות במחרוזות Unicode (במקום במחרוזות של בייטים), ולכן נדרש u'' אינדיקטור מוביל store_visit() עבור מחרוזות מילוליות ב-Python 2. פונקציה ב-Python 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. הגדרת פרויקט

מומלץ להשתמש מחדש באותו פרויקט שבו השתמשתם כדי להשלים את ה-Codelab של מודול 3. אפשר גם ליצור פרויקט חדש לגמרי או להשתמש בפרויקט קיים אחר. מוודאים שלפרויקט יש חשבון פעיל לחיוב ושהשירות App Engine (אפליקציה) מופעל.

2. קבלת אפליקציה לדוגמה של ערך בסיס

אחד מהתנאים המוקדמים ל-codelab הזה הוא שיש לכם אפליקציית דוגמה תקינה של מודול 3. אם אין לכם כזו, אתם צריכים להשלים את המדריך של מודול 3 (הקישור מופיע למעלה) לפני שתמשיכו כאן. אם אתם כבר מכירים את התוכן, אתם יכולים פשוט להעתיק את הקוד של מודול 3 שבהמשך.

בין אם משתמשים בקוד שלכם או בקוד שלנו, נתחיל עם הקוד של מודול 3. ב-codelab של מודול 6 מוסבר כל שלב, וכשתסיימו, הקוד אמור להיות דומה לקוד בנקודת הסיום. (המדריך הזה זמין רק ל-Python 3).

הספרייה של קובצי מודול 3 (שלכם או שלנו) אמורה להיראות כך:

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

3. (Re)Deploy Module 3 app

השלבים הנותרים שצריך לבצע עכשיו:

  1. כדאי לעיין שוב בכלי gcloud של שורת הפקודה (אם צריך).
  2. (Re)deploy the Module 3 code to App Engine (if nec.)

אחרי שתבצעו את השלבים האלה ותאשרו שההגדרה פועלת, נמשיך במדריך הזה, החל מקובצי ההגדרה.

הדרישות של Python 2

  • מוודאים ש-app.yaml (עדיין) מפנה לחבילות של צד שלישי: grpcio ו-setuptools.
  • מוודאים ש-appengine_config.py עדיין משתמש ב-pkg_resources וב-google.appengine.ext.vendor כדי להפנות את האפליקציה למשאבים של צד שלישי.
  • בקטע הבא בנושא עדכון requirements.txt, צריך להשתמש ב-requirements.txt כי זו הגרסה האחרונה של ספריית הלקוח Python Firestore שתואמת לגרסה 2.x.google-cloud-firestore==1.9.0
    • אם בקובץ 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, נריץ שאילתה כדי למצוא את 10 ישויות 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 ומאשרים שהאפליקציה פועלת. הקוד שלכם צריך להיות זהה לקוד במאגר Module 6 (או בגרסה 2.x אם זו הייתה ההעדפה שלכם).

אם התחלתם את סדרת המאמרים הזו בלי לבצע את כל המעבדות הקודמות, האפליקציה עצמה לא משתנה. היא רושמת את כל הביקורים בדף האינטרנט הראשי (/) ונראית כך אחרי שביקרתם באתר מספיק פעמים:

אפליקציית visitme, אפליקציית ויזיטמי

מזל טוב על השלמת ההעברה האופציונלית של מודול 6. סביר להניח שזו אחת מההעברות האחרונות שתוכלו לבצע, אם לא האחרונה, בכל מה שקשור לאחסון נתונים ב-App Engine. אפשרות נוספת להעברה שכדאי לשקול היא להכניס את האפליקציה לקונטיינר ל-Cloud Run, אם עדיין לא עשיתם את זה (ראו מודולים 4 ו-5, codelabs שמקושרים בהמשך).

אופציונלי: ניקוי

מה לגבי ניקוי כדי להימנע מחיוב עד שתהיו מוכנים לעבור אל סדנת ה-codelab הבאה בנושא העברה? אם אתם מפתחים קיימים, סביר להניח שכבר קראתם את פרטי התמחור של App Engine.

אופציונלי: השבתת האפליקציה

אם אתם עדיין לא מוכנים לעבור למדריך הבא, אתם יכולים להשבית את האפליקציה כדי להימנע מחיובים. כשרוצים לעבור אל ה-codelab הבא, אפשר להפעיל אותו מחדש. בזמן שהאפליקציה מושבתת, היא לא מקבלת תנועה ולכן לא נוצרים חיובים. עם זאת, יכול להיות שתחויבו על השימוש ב-Firestore אם הוא חורג ממכסת השימוש בחינם. לכן, כדאי למחוק מספיק נתונים כדי שהשימוש יהיה במסגרת המכסה.

מצד שני, אם אתם לא מתכוונים להמשיך בהעברות ורוצים למחוק הכול לגמרי, אתם יכולים להשבית את הפרויקט.

השלבים הבאים

בנוסף למדריך הזה, יש כמה מודולים נוספים של Codelab להעברה שכדאי לכם לעיין בהם:

  • Module 7: App Engine Push Task Queues (required if you use [push] Task Queues)
    • הוספת משימות push של App Engine לאפליקציה Module 1taskqueue
    • הכנת המשתמשים להעברה אל Cloud Tasks במודול 8
  • מודול 4: מעבר ל-Cloud Run באמצעות Docker
    • העברת האפליקציה לקונטיינר להפעלה ב-Cloud Run באמצעות Docker
    • ההעברה הזו מאפשרת לכם להמשיך להשתמש ב-Python 2.
  • מודול 5: מעבר ל-Cloud Run באמצעות Cloud Buildpacks
    • יצירת קונטיינר לאפליקציה כדי להפעיל אותה ב-Cloud Run באמצעות Cloud Buildpacks
    • לא צריך לדעת שום דבר על Docker, על קונטיינרים או על Dockerfiles.
    • האפליקציה כבר צריכה להיות מועברת ל-Python 3 (Buildpacks לא תומך ב-Python 2)

7. מקורות מידע נוספים

App Engine migration module codelabs issues/feedback

אם נתקלתם בבעיות ב-codelab הזה, כדאי לחפש את הבעיה לפני ששולחים דיווח. קישורים לחיפוש וליצירה של בעיות חדשות:

מקורות מידע על העברת נתונים

בטבלה שלמטה מופיעים קישורים לתיקיות של מאגר המידע עבור מודול 3 (התחלה) ומודול 6 (סיום). אפשר גם לגשת אליהם ממאגר המידע של כל ההעברות של App Engine, שאפשר לשכפל או להוריד כקובץ ZIP.

Codelab

Python 2

Python 3

Module 3

(קוד)

קוד

מודול 6

(n/a)

קוד

משאבי App Engine

למטה מופיעים מקורות מידע נוספים לגבי ההעברה הספציפית הזו: