המפרש החדש שכתבתי ל Redis

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

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

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

אז איך הרעיון של המפרש החדש בנוי, אתם בטח שואלים ?

parser_visual

look in the middle of the graph

חילקתי את המפרש לתתי פונקציות אשר למרות שהן nested functions (תכונה שיש בשפת פסקל, אבל אינה קיימת במרבית שפות התכנות הסטטיות, בעוד שבשפות כדוגמת פרל ואפילו javascript מאוד נוח לעובד איתן),

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

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

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

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

הבאג שהיה, דיבר על רשימות מקוננות, אשר תמיד נעצר לקראת הסוף של הרשימה וסיים את הריצה בהצלחה, בלי לרוץ על כל האיברים. אבל זה היה רק בפונקציות מקוננות עצמן, אשר מורכבות ביותר מאשר קינון בודד. מסתבר שהיה לי off by two. הבעיה היתה שב99% מהפעמים הקוד עשה בדיוק מה שצריך, והייתי צריך להבין מה גורם ל off by two באחוז הנותר. מצאתי שהבאג עצמו נמצא בלולאה של הרשימה בעצם. הוא בד"כ מנסה לספור עוד 2 בשביל לדלג על סיום ה CRLF האחרונים של האיבר. אבל יש מצבים שזה לא נדרש. אז התיקון היה לגלות האם יש לכך דרישה או לא, ואז הכל חזר לעבוד ממש יפה.

מה שנוצר בעצם הוא פונקציות רקורסיביות הרצות בצורה לינארית בפירוש ה buffer.

בניגוד למספר מפרשים של Redis, אני מתייחס ל buffer תמיד כמחרוזת ולא כרשימת בתים, וזה למרות שRedis אמור להיות מסד נתונים המאפשר לנהל גם בתים, ולא בהכרח מחרוזות.

עכשיו אני מקווה שאחרי השכתוב הזה, הכל ילך חלק יותר.

להשאיר תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s