שאלת ראיונות עבודה: רקורסיה ב Node.JS
נתונה הפונקציה ב TypeScript:
export const generateId = async (domain_id: number = null) => {
const address = generate(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
env.LINK_LENGTH
);
const link = await query.link.find({ address, domain_id });
if (!link) return address;
return generateId(domain_id);
};
-
מה הפונקציה עושה?
-
האם יש בעיה בקוד?
-
האם הבעיה צפויה לגרום לנזק בעולם האמיתי?
-
מה הייתם עושים אחרת?
1. תשובות
נתחיל במבט רק על הפונקציה בלי להכיר את הקונטקסט מסביב: הפונקציה מקבלת מספר בפרמטר שנקרא domain_id
ומפעילה פונקציית עזר בשם generate. מאחר ו generate מקבלת פרמטר ראשון שנראה כמו רשימת תווים אפשריים, ופרמטר שני בשם שכולל את המילה אורך, אני מתאר לעצמי שהפונקציה generate מגרילה מחרוזת אקראית מרשימת התווים האפשריים ובאורך שעבר בפרמטר השני. ממשיכים לחיפוש (אולי בבסיס נתונים?) אוביקט שמתאים למחרוזת שיצרנו, אם קיים אחד מחזירים אותו ואם לא קיים מנסים להגריל מחרוזת חדשה, עד שנמצא מחרוזת שלא קיימת במאגר.
עד לפה תיאור הפונקציה בלי שום קונטקסט חיצוני, ואני חושב שיש פה מספיק מידע כדי לענות גם על שלושת השאלות הבאות.
בעיה ראשונה בקוד היא הרקורסיה. מאחר ו Node.JS לא תומך באופטימיזציית זנב הרקורסיה אנחנו מסתכנים בלולאה שתיצור עוד ועוד פריימים על המחסנית עד שבסוף תיכשל בגלל Stack Overflow. הסבירות לנזק בעולם האמיתי נמוכה כי בסיטואציה בה אנחנו מגרילים מזהה ורוצים שיהיה ייחודי בדרך כלל אנחנו בוחרים מראש אורך שימזער את הסיכוי להתנגשויות, ולכן אפילו כניסה אחת לתוך הרקורסיה הזאת אמורה להיות נדירה.
בעיה שניה בקוד קשורה לקונטקסט שקצת חסר לנו כאן אבל אפשר לדמיין. הפונקציה נקראת generateId ולכן היא כנראה אמורה להחזיר מזהה ייחודי של משהו. אנחנו רואים גם שהיא מתאמצת למצוא מזהה ייחודי לפני שמחזירה אותו. אבל מרגע שהיא "מצאה" מזהה היא פשוט מחזירה אותו וסומכת על זה שהמזהה יישאר ייחודי בקוד שצריך אותו. אבל אין שום דבר בקוד שמבטיח את זה. נדמיין מצב שהקוד רץ מתוך שרת ושני גולשים מבצעים פעולה בדיוק באותו זמן. במצב כזה שני קטעי הקוד שמריצים את generateId
עשויים לקבל את אותו המזהה ואחד מהם ייכשל בכתיבה לבסיס הנתונים.
גישה טובה יותר תהיה להזיז את הבדיקה פנימה כמה שיותר קרוב לכתיבה לבסיס הנתונים: מספיק היה להשתמש במחרוזת הראשונה שהוגרלה בתור המזהה ואותה להחזיר, ולוותר על הבדיקה שאולי זה כבר קיים. אם בכתיבה לבסיס הנתונים נקבל הפרה של אינדקס ייחודי על העמודה אז נתפוס את השגיאה וננסה להגריל מזהה חדש.