OpenVPN על TCP: rto_min לא פותר את החסימה בראש התור
זרם בתים חיצוני מספק לפי הסדר, ולכן סגמנט חסר אחד יכול לעצור כל זרם פנימי. טיימר פנימי ארוך יותר משנה שעון, לא את המגבלה הזו.
מעטפת אינטראקטיבית מעל המנהרה קופאת לכמה שניות בזמן שהעתקת קובץ רצה, ואז ממשיכה כאילו דבר לא קרה. רק ההורדה סובלת מבעיה, אבל גם המעטפת ממתינה, ושתיהן מתאוששות באותו רגע. התבנית הזו היא החתימה של זרם אמין אחד שנושא שיחות רבות, והיא שורדת כמעט כל שינוי כיוונון שאנשים מנסים קודם.
כדאי לקרוא את התסמין בזהירות כי הוא מצביע על מנגנון ולא על הגדרה: ההפסקה משותפת, ולכן הסיבה משותפת, והאובייקט המשותף הוא החיבור החיצוני. כל השאר ביומן נראה תקין בזמן שזה קורה.
התסמין: הכול קופא בבת אחת
שני חיבורים פנימיים שנתקעים יחד הם הרמז. הורדה איטית ומעטפת איטית הן שיחות TCP נפרדות עם מספרי רצף נפרדים ומצב גודש נפרד, ולכן תקלה באחת לא אמורה לגעת בשנייה. כשהן קופאות בקצב זהה, משהו מעליהן מסדר את הבתים שלהן.
המשהו הזה הוא שכבת התעבורה של המנהרה. במצב TCP, OpenVPN כותב את המנות העטופות לחיבור TCP אחד אל העמית, והחוזה של TCP הוא מסירה לפי הסדר של זרם בתים אחד. בתים מאוחרים יותר לא נמסרים ליישום לפני החסרים, לא משנה לאיזו שיחה פנימית הם שייכים.
שימו לב למה התסמין לא אומר. הוא לא אומר שהנתיב שבור, שהשרת עמוס מדי או שהצפנה איטית. אלה מייצרים צורות אחרות: תפוקה נמוכה באופן אחיד, פסקי זמן או כשלים שפוגעים בכיוון אחד. הפסקה משותפת שמסתיימת כשסגמנט שאבד נשלח מחדש היא בדיוק אפקט של סדר.
זרם בתים חיצוני אחד, שיחות פנימיות רבות
דמיינו את המנהרה נושאת הורדה ומעטפת בעת ובעונה אחת. סגמנט של החיבור החיצוני הולך לאיבוד. הסגמנטים הבאים יכולים להגיע למארח המקבל, ומחסנית ה-TCP המקבלת תאשר אותם, אבל היא לא יכולה למסור אותם למעלה עד שהפער יתמלא. לכן ל-OpenVPN אין מה לקרוא עבור המעטפת, למרות שהבתים של המעטפת עצמה הגיעו מזמן.
זו חסימה בראש התור בתוך חיבור אחד, והיא לא דורשת ששתי היישומים יחלקו חיבור פנימי. הם חולקים את החיצוני, וזה מספיק.
אפקט מסדר שני מחמיר את המצב. שולח פנימי שלא רואה אישורים עשוי לשלוח מחדש נתונים שכבת החוץ כבר מנסה למסור. השליחה מחדש הזו הופכת לתעבורת מנהרה נוספת בנתיב שהוכיח זה עתה שהוא יכול לאבד סגמנטים, ולכן עבודת ההתאוששות מתחרה בהתאוששות שחשובה.
שום דבר מזה לא מבטיח קריסה. בנתיב נקי עם זמני הליכה חזרה קצרים ותורים נדיבים, הסידור יכול להיות שמיש לחלוטין במשך שנים. הכשל מופיע כשאבידה, השהיה ועומק תור מתייצבים יחד, ולכן הדיווחים מתקבצים סביב קישורים עולים צפופים וקישורים סלולריים ולא סביב משרדים מצוידים היטב.
למה טיימר פנימי ארוך יותר לא יכול לעזור
העלאת זמן השליחה מחדש המינימלי בנתיב, עם rto_min, דוחה שליחה מחדש מבוססת טיימר עבור תעבורת TCP שנוצרת מקומית ומשתמשת בנתיב הזה. זהו כפתור אמיתי עם השפעה אמיתית, והוא מטפל בבעיה מסדר שני ולא בראשונה.
ip route show
ip route show cache 2>/dev/null | head
ss -tin dst 10.70.0.1
מה שהוא לא יכול לעשות: לשחרר בתים חיצוניים שלא לפי הסדר, להתאושש מהסגמנט החסר מהר יותר, או לגרום לשרת היישום המרוחק לאמץ את אותו טיימר. מחסנית הקליטה עדיין מוסרת לפי הסדר, והשולח בקצה הרחוק עדיין משתמש במדידות הליכה חזרה שלו.
הוא גם לא מכבה כל מנגנון התאוששות אחר מאבידה. כשאבידה אמיתית דורשת התאוששות מבוססת טיימר, מינימום גדול יותר גורם ליישום להמתין יותר לפני שהוא פועל, שזה ההפך ממה שסשן רגיש להשהיה צריך.
רצפה קבועה של שתים עשרה שניות מופיעה בעצות בנושא כאילו קיומה של מנהרת TCP מחייב אותה. טיימר השליחה מחדש המתואר ב-RFC 6298 נגזר מזמן הליכה חזרה נמדד עם נסיגה מעריכית, ולכן רצפה קבועה היא בחירת מדיניות לנתיב מסוים ולא דרישת פרוטוקול. כשהוא מיושם באופן רחב, הוא מסתיר את התסמין ובמקביל משנה את ההתאוששות של כל זרם שהנתיב מכסה.
מה tcp-nodelay עושה ומה לא
אפשרות המנהרה שמכבה חוצץ בסגנון Nagle על שקע התעבורה יש לה מטרה צרה: היא מונעת מכתיבות קטנות להמתין לנתונים נוספים לפני השידור. זה עוזר לתעבורה אינטראקטיבית להימנע מהשהיה נוספת בנתיב שבריאותו אחרת תקינה.
היא לא הופכת את התעבורה ללא מסודרת, והיא לא מקצרת את ההתאוששות אחרי סגמנט שאבד. מעטפת שקופאת בגלל פסק זמן שליחה מחדש תקפא גם עם האפשרות מופעלת, כי האפשרות מעולם לא נגעה בכלל הסדר.
קל לבלבל בין השניים כי שניהם מתוארים כתיקוני השהיה. אחד מסיר המתנה מלאכותית שהכניס החוצץ, השני היה דורש לשנות את מה שהתעבורה מבטיחה, ואף אפשרות שקע לא עושה זאת.
איך להשוות תעבורה בצורה הוגנת
אם המפעיל מתיר ושתי השירותים קיימים, השוו שירות UDP ושירות TCP שנושאים את אותו עומס באותו נתיב גישה לאותו שרת. החלפת מדינה, ספק או דור שרת בין שתי הבדיקות מכניסה משתנה שישלוט בתוצאה.
התבוננו בהפסקה המשותפת ולא במספר תפוקה. כמה יישומים פנימיים שנתקעים יחד בזמן אבידה חיצונית היא התצפית שמזהה חסימה בראש התור; מספר בדיקת מהירות נמוך אחד לא עושה זאת.
לכידות מורשות בממשק המנהרה ובממשק הפיזי מפרידות את שתי השכבות בצורה נקייה, כי שליחות חוזרות פנימיות וחיצוניות שייכות לחיבורים שונים עם מרחבי רצף שונים. לראות שליחות חוזרות פנימיות כשהזרם החיצוני מראה פער היא ההוכחה הישירה.
מעבר ל-UDP מסיר את הזרם האמין הנוסף ומשאיר ל-TCP הפנימי לעשות את ההתאוששות שלו. הוא לא מבטיח אפס אבידות או השהיה נמוכה, וקישור עולה רווי, תורים גדולים מדי או בעיית MTU עדיין יצטרכו תיקון. תעבורה מחוץ למנהרה לא נעצרת מאחורי פער הרצף של המנהרה, אם כי היא עדיין עשויה לסבול כששני נתיבים חולקים קישור צפוף אחד, וצוואר הבקבוק המשותף הזה ראוי לחקירה משלו ולא לקיפול לתוך זו.