בפרק הזה מוסבר על מודל השגיאות של ממשקי ה-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 |