הגדרת איגום חיבורים ל-PostgreSQL באמצעות Odyssey

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

Odyssey PostgreSQL connection pooling: היכן נשברת הבריכה

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

ברגע שהבריכה מוצתה, בקשות חדשות עומדות בתור לחיבור, ממתינות ונופלות בגלל timeout — בעוד מסד הנתונים עצמו יכול להיות עמוס למחצה.

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

Odyssey PostgreSQL connection pooling — שכבה מפרידה בין האפליקציה למסד הנתונים: השירות מחזיק בריכה משלו, והלקוחות לוקחים חיבור ממנה, במקום לפתוח חיבור חדש ב-PostgreSQL.

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

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

בריכת חיבורים מנוהלת: פחות כשלים והשהיה אחידה

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

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

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

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

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

מצבי עבודה של הבריכה: סשנים, טרנזקציות, בקשות בודדות

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

בוחרים אותו לא לפי המלצה כללית, אלא לפי האם האפליקציה שומרת מצב בין בקשות: ביטויים מוכנים, advisory-נעילות, טבלאות זמניות ו-LISTEN/NOTIFY קשורים לסשן שרת מסוים.

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

מצב מתי מתאים מגבלות
סשני אפליקציות עם מצב סשן: ביטויים מוכנים, advisory-נעילות, LISTEN/NOTIFY, טבלאות זמניות, חיבורים מנהליים ארוכים החיבור מוצמד ללקוח עד לניתוק — אין חיסכון בסשנים לא פעילים
טרנזקציוני העומס העיקרי: טרנזקציות קצרות, מקביליות גבוהה, שירותי ווב ותורים אסור להחזיק מצב סשן בין בקשות; פעולה מרובת שלבים חייבת להסתיים בטרנזקציה אחת
בקשות בודדות בחירות בודדות ללא טרנזקציות, החלקת פרצי חיבורים אין טרנזקציות, מצב בין בקשות לא נשמר

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

כיצד מתבצעת ההטמעה: שלבי העבודה ההנדסית

סדר העבודות כאן אינו פורמליות: מגבלות בריכה שהוגדרו לפי הנחות גורמות לכשלים בשיאים או משאירות את מסד הנתונים בלי תהליכים פנויים. אנו הולכים מפרופיל החיבורים בפועל לבדיקה תחת עומס — כל שלב נשען על הנתונים של הקודם.

  1. ביקורת חיבורים למסד הנתונים. אנו סופרים כמה חיבורים פותח כל שירות, אילו מחזיק באופן קבוע, מה עושות משימות הרקע והתורים. בפלט — מפת צרכנים עם ערכי שיא וממוצע.
  2. תרשים ניתוב חיבורים. מפצלים את התעבורה לנקודות כניסה: שירותי כתיבה, קריאה, אנליטיקה וסקריפטים שירותיים בנפרד, כדי שדוח כבד לא יתפוס מקום בבריכה הכללית.
  3. תכנון תצורת Odyssey. בוחרים מצב בריכה, מגבלות למשתמש ולמסד נתונים, זמן חיים של חיבור והתנהגות בעת ניתוק — כך שהאפליקציה לא תבחין בשכבה.
  4. פריסה במערך שלכם. הבריכה ממוקמת ליד מסד הנתונים או כשכבה נפרדת, עם יתירות ובדיקות זמינות; התצורה נשמרת במערכת ניהול גרסאות.
  5. העברת תעבורה הדרגתית. מעבירים תחילה שירות אחד, משווים את השגיאות והשהיות שלו למצב הקודם; חזרה לאחור — על ידי עריכת תצורה, בלי להפעיל מחדש את מסד הנתונים.
  6. בדיקה תחת עומס. מריצים פרופיל אמיתי: שעות שיא, משימות אצווה, כשל של צומת אחד. רואים אם הבריכה מחזיקה את התור ומה קורה לאפליקציה כאשר המגבלה נגמרת.
  7. מסירה לתפעול. מוסרים תצורה, תרשים ניתוב, מדדים ותקנון שינויים — המגבלות הצוות שלכם עורך בעצמו.

מה נשאר אצלכם: תצורות, תרשימים ותקנונים

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

  • תצורת הבריכה: סטים של חיבורים לפי זוגות «מסד נתונים/משתמש», מגבלות לכל אחד, timeout של התקנה, המתנה וחוסר פעילות, מצב ניתוב טרנזקציות.
  • תרשימי חיבור של אפליקציות: אילו שירותים עוברים דרך Odyssey ולאיזה סט, אילו נשארים ישירים — אנליטיקה, רפליקציה, מיגרציות.
  • מדיניות גישה: סט תפקידים, כללי הרשאה לפי כתובות ושיטות אימות, הפרדת הרשאות של האפליקציה ושל מנהל מסד הנתונים.
  • תקנון תפעול: סדר עריכת תצורה, הפעלה מחדש בלי לקטוע סשנים פעילים, חלונות מתוכננים, פעולות בעת מיצוי סט החיבורים.
  • ניטור והתראות: אילו מדדים נלקחים, ערכי סף, בדיקות זמינות, חיבור למערך ההתראות שלכם.
  • סדר פעולות בתקלות אופייניות: גידול תור ההמתנה, דליפת חיבורים, הפעלה מחדש של צומת הבריכה, חזרה לגרסת תצורה קודמת.
  • מפת זרימות חיבורים ואחראים: מי מנפיק הרשאות, כיצד מתבצעת רוטציה של סודות, למי לפנות בעת שינוי סכימת הנתונים.

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

מקרה בוחן: הסרנו כשלי חיבורים בשירות עמוס

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

מה הגדרנו

ניהול החיבורים הועבר לשכבה נפרדת — Odyssey בין האפליקציה למסד הנתונים. הפרדנו בריכות: טרנזקציות קצרות של API ובחירות רקע ארוכות כבר לא מתחרות על סט משבצות אחד, חיבור השרת מוחזר לבריכה מיד לאחר commit, וגודל הבריכה מוגדר באופן קשיח — מסד הנתונים רואה מספר צפוי של לקוחות בכל פרץ.

מדדים לפני ואחרי

שיעור הבקשות הלא מוצלחות בשיא ירד מ-4–6% ל-0.1%; חיבורים בו-זמנית למסד הנתונים — כ-120 במקום 900. כשלים מסוג «מגבלת חיבורים מוצתה» נעלמו לחלוטין.

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

אפקט נפרד — זמן ההתאוששות. בעבר, ניתוק גרם למפולת של חיבורים מחדש מכל זרמי העבודה, והשירות חזר לשגרה תוך כ-40 שניות. כעת, את הניתוק מטפלת הבריכה: בודקת את חיות החיבורים, מעבירה בקשות רק לעובדים ומחזירה סט מלא של משבצות תוך 3–5 שניות — עבור הלקוח זה בלתי מורגש.

במה נבדלת בריכה חיצונית מבריכת חיבורים מובנית של האפליקציה

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

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

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

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

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

שאלות טכניות נפוצות בנושא בריכת חיבורים

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

האם שאילתות מוכנות יפסיקו לעבוד?

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

האם יהיה צורך להפעיל מחדש את הבריכה ומה יקרה לחיבורים?

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

מה נשבר עם טרנזקציות ונעילות?

טרנזקציה מחזיקה חיבור שרת אחד לחלוטין, לכן כל מה שחי מחוץ לגבולותיה עובר באופן גרוע: advisory-נעילות סשן, LISTEN/NOTIFY, קורסורים עם החזקה, טבלאות זמניות. הכלל פשוט — מצב לא צריך לשרוד את הטרנזקציה; היכן שזה בלתי אפשרי, מוציאים חיבורים כאלה לבריכה נפרדת עם סשן קבוע.

איך להבין שהבריכה הפכה לצוואר בקבוק?

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

נדון במשימה ונחזור עם הערכה ותוכנית הטמעה

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

מה כדאי לאסוף לפני השיחה:

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

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

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