שגיאות ב-Google Distributed Cloud במודל Air-gapped

בפרק הזה מוסבר על מודל השגיאות של ממשקי ה-API של Google Distributed Cloud במודל Air-gapped‏ (GDC). בנוסף, הוא מספק הנחיות כלליות למפתחים לגבי יצירה נכונה של שגיאות וטיפול בהן.

ממשקי ה-API של GDC משתמשים במודל שגיאות פשוט שלא תלוי בפרוטוקול, מה שמאפשר לנו לספק חוויה עקבית בממשקי API שונים, בפרוטוקולי API שונים (כמו gRPC או HTTP) ובהקשרים שונים של שגיאות (כמו שגיאות אסינכרוניות, שגיאות של קבוצות או שגיאות של תהליכי עבודה).

מודל שגיאות

מודל השגיאות של GDC APIs מוגדר באופן לוגי על ידי google.rpc.Status, שמוחזר ללקוח כשמתרחשת שגיאת API. קטע הקוד הבא מציג את העיצוב הכללי של מודל השגיאות:

package google.rpc;

// The `Status` type defines a logical error model that is suitable for
// different programming environments, including REST APIs and RPC APIs.
message Status {
  // A simple error code that can be easily handled by the client. The
  // actual error code is defined by `google.rpc.Code`.
  int32 code = 1;

  // A developer-facing human-readable error message in English. It should
  // both explain the error and offer an actionable resolution to it.
  string message = 2;

  // Additional error information that the client code can use to handle
  // the error, such as retry info or a help link.
  repeated google.protobuf.Any details = 3;
}

מכיוון שרוב ממשקי ה-API של Google משתמשים בעיצוב API ממוקד משאבים, הטיפול בשגיאות מתבסס על אותו עיקרון עיצוב, באמצעות קבוצה קטנה של שגיאות סטנדרטיות עם מספר גדול של משאבים. לדוגמה, במקום להגדיר סוגים שונים של שגיאות 'לא נמצא', השרת משתמש בקוד שגיאה סטנדרטי google.rpc.Code.NOT_FOUNDומציין ללקוח איזה משאב ספציפי לא נמצא. הצמצום של מרחב השגיאות מפשט את התיעוד, מאפשר מיפויים אידיומטיים טובים יותר בספריות הלקוח ומפחית את מורכבות הלוגיקה של הלקוח, בלי להגביל את הכללת המידע שניתן לפעול לפיו.

קודי שגיאה

ממשקי GDC API חייבים להשתמש בקודי השגיאה הקנוניים שמוגדרים על ידי google.rpc.Code. ממשקי API נפרדים לא יכולים להגדיר קודי שגיאה נוספים, כי הסיכוי שמפתחים יכתבו לוגיקה לטיפול במספר גדול של קודי שגיאה הוא נמוך מאוד. לשם השוואה, טיפול בממוצע של שלושה קודי שגיאה לכל קריאה ל-API יגרום לכך שרוב הלוגיקה של האפליקציה תהיה מיועדת לטיפול בשגיאות, וזה לא יהיה נוח למפתחים.

הודעות שגיאה

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

  • אל תניחו שהמשתמש הוא מומחה בשימוש ב-API שלכם. המשתמשים יכולים להיות מפתחי לקוחות, אנשי תפעול, צוות IT או משתמשי קצה של אפליקציות.
  • אל תניחו שהמשתמש יודע משהו על הטמעת השירות שלכם או שהוא מכיר את ההקשר של השגיאות (למשל, ניתוח יומנים).
  • כשאפשר, צריך ליצור את הודעות השגיאה כך שמשתמש טכני (אבל לא בהכרח מפתח של ה-API) יוכל להגיב לשגיאה ולתקן אותה.
  • הודעת השגיאה צריכה להיות קצרה. במקרה הצורך, כדאי לספק קישור שבו קורא מבולבל יכול לשאול שאלות, לתת משוב או לקבל מידע נוסף שלא מתאים להודעת שגיאה. אחרת, צריך להשתמש בשדה הפרטים כדי להרחיב.

פרטי שגיאה

ממשקי GDC API מגדירים קבוצה של מטען ייעודי (payload) שגיאות סטנדרטי לפרטי שגיאות, שאפשר למצוא ב-google/rpc/error_details.proto. הם כוללים את הצרכים הנפוצים ביותר לשגיאות API, כמו חריגה מהמכסה ופרמטרים לא תקינים. בדומה לקודי שגיאה, מפתחים צריכים להשתמש במטענים הייעודיים (payloads) האלה כשהדבר אפשרי.

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

ריכזנו כאן כמה דוגמאות של מטען ייעודי (payload) של error_details:

  • ErrorInfo: מספק מידע מובנה על שגיאות שהוא גם יציב וגם ניתן להרחבה.
  • RetryInfo: מתאר מתי לקוחות יכולים לנסות שוב בקשה שנכשלה. יכול להיות שיוחזר ב-Code.UNAVAILABLE או ב-Code.ABORTED
  • QuotaFailure: תיאור של כשל בבדיקת מכסה. יכול להיות שיוחזר ב-Code.RESOURCE_EXHAUSTED
  • BadRequest: מתאר הפרות בבקשת לקוח, יכול להיות שיוחזר ב-Code.INVALID_ARGUMENT

פרטי השגיאה

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

ב-GDC APIs, הדומיין הראשי של השגיאות הוא googleapis.com, והסיבות לשגיאות התואמות מוגדרות על ידי enum‏ google.api.ErrorReason. מידע נוסף זמין בהגדרה של google.api.ErrorReason.

לוקליזציה של שגיאות

השדה message ב-google.rpc.Status מיועד למפתחים וחייב להיות באנגלית.

אם צריך להציג למשתמש הודעת שגיאה, צריך להשתמש בערך google.rpc.LocalizedMessage בשדה הפרטים. אפשר לתרגם את שדה ההודעה ב-google.rpc.LocalizedMessage, אבל חשוב לוודא ששדה ההודעה ב-google.rpc.Status הוא באנגלית.

כברירת מחדל, שירות ה-API צריך להשתמש בלוקאל של המשתמש המאומת או בכותרת ה-HTTP ‏Accept-Language או בפרמטר language_code בבקשה כדי לקבוע את השפה ללוקליזציה.

מיפוי שגיאות

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

מיפוי HTTP

למרות שלהודעות proto3 יש קידוד JSON מקורי, פלטפורמת GDC API משתמשת בסכימת שגיאות שונה עבור GDC JSON HTTP APIs מסיבות של תאימות לאחור.

סכימה:

// This message defines the error schema for GDC JSON HTTP APIs.
message Error {
  // Deprecated. This message is only used by error format v1.
  message ErrorProto {}
  // This message has the same semantics as `google.rpc.Status`. It uses HTTP
  // status code instead of gRPC status code. It has extra fields `status` and
  // `errors` for backward compatibility with [GDC API Client
  // Libraries](https://developers.google.com/api-client-library).
  message Status {
    // The HTTP status code that corresponds to `google.rpc.Status.code`.
    int32 code = 1;
    // This corresponds to `google.rpc.Status.message`.
    string message = 2;
    // Deprecated. This field is only used by error format v1.
    repeated ErrorProto errors = 3;
    // This is the enum version for `google.rpc.Status.code`.
    google.rpc.Code status = 4;
    // This corresponds to `google.rpc.Status.details`.
    repeated google.protobuf.Any details = 5;
  }
  // The actual error payload. The nested message structure is for backward
  // compatibility with [GDC API Client
  // Libraries](https://developers.google.com/api-client-library). It also
  // makes the error more readable to developers.
  Status error = 1;
}

דוגמה:

{
  "error": {
    "code": 400,
    "message": "API key not valid. Please pass a valid API key.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "API_KEY_INVALID",
        "domain": "googleapis.com",
        "metadata": {
          "service": "translate.googleapis.com"
        }
      }
    ]
  }
}

מיפוי gRPC

פרוטוקולי RPC שונים ממפים את מודל השגיאות בצורה שונה. ב-gRPC, מודל השגיאות נתמך באופן מובנה על ידי הקוד שנוצר וספריית זמן הריצה בכל שפה נתמכת. מידע נוסף זמין במאמרי העזרה של gRPC API. לדוגמה, אפשר לעיין ב-io.grpc.Status של gRPC Java.

מיפוי של ספריית לקוח

ספריות לקוח של GDC עשויות להציג שגיאות בצורה שונה בכל שפה כדי לשמור על עקביות עם ניבים מקובלים. לדוגמה, הספרייה google-cloud-go תחזיר שגיאה שמיישמת את אותו ממשק כמו google.rpc.Status, ואילו הספרייה google-cloud-java תעלה חריגה.

טיפול בשגיאות

בטבלה הבאה מופיעים כל קודי השגיאה של gRPC שמוגדרים ב-google.rpc.Code ותיאור קצר של הסיבה לשגיאה. כדי לטפל בשגיאה, אפשר לבדוק את התיאור של קוד הסטטוס שהוחזר ולשנות את הקריאה בהתאם.

HTTP gRPC תיאור
200 OK אין שגיאה.
400 INVALID_ARGUMENT הלקוח ציין ארגומנט לא חוקי. כדי לקבל מידע נוסף, אפשר לקרוא את הודעת השגיאה ואת פרטי השגיאה.
400 FAILED_PRECONDITION לא ניתן לבצע את הבקשה במצב הנוכחי של המערכת, למשל, מחיקת ספרייה לא ריקה.
400 OUT_OF_RANGE הלקוח ציין טווח לא חוקי.
401 UNAUTHENTICATED הבקשה לא אומתה עקב טוקן OAuth חסר, לא חוקי או שתוקפו פג.
403 PERMISSION_DENIED ללקוח אין הרשאה מספיקה. הסיבה לכך יכולה להיות שלטוקן ה-OAuth אין את ההיקפים הנכונים, ללקוח אין הרשאה או שה-API לא הופעל.
404 NOT_FOUND המשאב שצוין לא נמצא.
409 ABORTED התנגשות מסוג בו-זמניות (Concurrency), כגון התנגשות בפעולה read-modify-write (קריאה-שינוי-כתיבה).
409 ALREADY_EXISTS המשאב שהלקוח ניסה ליצור כבר קיים.
429 RESOURCE_EXHAUSTED מכסת המשאבים מוצתה או שהגבלת הקצב של יצירת הבקשות תמוצה בקרוב. כדי לקבל מידע נוסף, הלקוח צריך לחפש את פרטי השגיאה google.rpc.QuotaFailure.
499 CANCELLED הבקשה בוטלה על ידי הלקוח.
500 DATA_LOSS פגם בנתונים או אובדן נתונים שלא ניתן לשחזר. הלקוח צריך לדווח על השגיאה למשתמש.
500 UNKNOWN שגיאת שרת לא ידועה. בדרך כלל באג בשרת.
500 INTERNAL שגיאת שרת פנימית. בדרך כלל באג בשרת.
501 NOT_IMPLEMENTED שיטת ה-API לא הוטמעה על ידי השרת.
502 לא רלוונטי אירעה שגיאת רשת לפני ההגעה לשרת. בדרך כלל מדובר בהפסקת חשמל ברשת או בהגדרה שגויה.
503 UNAVAILABLE השירות לא זמין. בדרך כלל השרת מושבת.
504 DEADLINE_EXCEEDED המועד האחרון לשליחת הבקשה חלף. המצב הזה יקרה רק אם המתקשר יגדיר מועד אחרון קצר יותר מהמועד האחרון שמוגדר כברירת מחדל לשיטה (כלומר, המועד האחרון שצוין לא מספיק לשרת כדי לעבד את הבקשה), והבקשה לא הסתיימה לפני המועד האחרון.

ניסיון חוזר לתיקון השגיאות

לקוחות יכולים לנסות לשלוח שוב בקשות שמתקבלות עבורן שגיאות מסוג 503 UNAVAILABLE עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff). ההשהיה המינימלית צריכה להיות שנייה אחת, אלא אם צוין אחרת במסמכים. ברירת המחדל של מספר הניסיונות החוזרים צריכה להיות אחד, אלא אם צוין אחרת במסמכים.

במקרה של שגיאות 429 RESOURCE_EXHAUSTED, הלקוח יכול לנסות שוב ברמה גבוהה יותר עם עיכוב מינימלי של 30 שניות. ניסיונות חוזרים כאלה שימושיים רק למשימות רקע ארוכות.

במקרה של כל שאר השגיאות, יכול להיות שניסיון חוזר לא יעזור. קודם כול, מוודאים שהבקשה היא אידמפוטנטית. google.rpc.RetryInfo

הפצת שגיאות

אם שירות ה-API שלכם תלוי בשירותים אחרים, אל תעבירו באופן אוטומטי שגיאות מהשירותים האלה ללקוחות שלכם. כשמתרגמים שגיאות, מומלץ:

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

שחזור שגיאות

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

מומלץ להשתמש ב-oauth2l, ב-curl -v ובפרמטרים של המערכת כדי לשחזר שגיאות בממשקי GDC API. יחד הן יכולות לשחזר כמעט את כל הבקשות ל-API של GDC ולספק לכם מידע מפורט לניפוי באגים. מידע נוסף זמין בדפי התיעוד של ממשקי ה-API שאתם קוראים להם.

יצירת שגיאות

מפתחי שרתים צריכים ליצור שגיאות עם מספיק מידע כדי לעזור למפתחי לקוחות להבין את הבעיה ולפתור אותה. במקביל, חשוב לשים לב לאבטחה ולפרטיות של נתוני המשתמשים, ולהימנע מחשיפת מידע רגיש בהודעת השגיאה ובפרטי השגיאה, כי לעיתים קרובות השגיאות נרשמות ביומן וייתכן שאנשים אחרים יוכלו לגשת אליהן. לדוגמה, הודעת שגיאה כמו "כתובת ה-IP של הלקוח לא נמצאת ברשימת ההיתרים 128.0.0.0/8" חושפת מידע על המדיניות בצד השרת, שאולי לא נגיש למשתמש שיש לו גישה ליומנים.

כדי ליצור שגיאות מתאימות, קודם צריך להכיר את google.rpc.Code כדי לבחור את קוד השגיאה המתאים ביותר לכל תנאי שגיאה. יכול להיות שאפליקציית שרת תבדוק כמה תנאי שגיאה במקביל, ותחזיר את הראשון מביניהם.

בטבלה הבאה מפורטים קודי השגיאה ודוגמה להודעת שגיאה טובה.

HTTP gRPC הודעת שגיאה לדוגמה
400 INVALID_ARGUMENT השדה x.y.z בבקשה הוא xxx, המערכת ציפתה לאחת מהאפשרויות [yyy, zzz].
400 FAILED_PRECONDITION המשאב xxx הוא ספרייה לא ריקה, ולכן אי אפשר למחוק אותו.
400 OUT_OF_RANGE הערך של הפרמטר 'גיל' לא נמצא בטווח [0, 125].
401 UNAUTHENTICATED פרטי האימות לא תקינים.
403 PERMISSION_DENIED ההרשאה 'xxx' נדחתה במשאב 'yyy'.
404 NOT_FOUND המשאב 'xxx' לא נמצא.
409 ABORTED לא הייתה אפשרות לקבל נעילה של המשאב'xxx '.
409 ALREADY_EXISTS המשאב 'xxx' כבר קיים.
429 RESOURCE_EXHAUSTED חריגה ממגבלת המכסה 'xxx'.
499 CANCELLED הבקשה בוטלה על ידי הלקוח.
500 DATA_LOSS ראו הערה.
500 UNKNOWN ראו הערה.
500 INTERNAL ראו הערה.
501 NOT_IMPLEMENTED השיטה 'xxx' לא הוטמעה.
503 UNAVAILABLE ראו הערה.
504 DEADLINE_EXCEEDED ראו הערה.

מטעני שגיאה

החבילה google.rpc מגדירה קבוצה של מטענים ייעודיים (payloads) של שגיאות סטנדרטיות, שעדיפים על מטענים ייעודיים של שגיאות בהתאמה אישית. בטבלה הבאה מפורטים כל קודי השגיאה ומטען השגיאה התקני התואם, אם יש כזה. מומלץ לאפליקציות מתקדמות לחפש את מטען השגיאות האלה ב-google.rpc.Status כשהן מטפלות בשגיאות.

HTTP gRPC פרטי השגיאה המומלצים
400 INVALID_ARGUMENT google.rpc.BadRequest
400 FAILED_PRECONDITION google.rpc.PreconditionFailure
400 OUT_OF_RANGE google.rpc.BadRequest
401 UNAUTHENTICATED google.rpc.ErrorInfo
403 PERMISSION_DENIED google.rpc.ErrorInfo
404 NOT_FOUND google.rpc.ResourceInfo
409 ABORTED google.rpc.ErrorInfo
409 ALREADY_EXISTS google.rpc.ResourceInfo
429 RESOURCE_EXHAUSTED google.rpc.QuotaFailure
499 CANCELLED
500 DATA_LOSS google.rpc.DebugInfo
500 UNKNOWN google.rpc.DebugInfo
500 INTERNAL google.rpc.DebugInfo
501 NOT_IMPLEMENTED
503 UNAVAILABLE google.rpc.DebugInfo
504 DEADLINE_EXCEEDED google.rpc.DebugInfo