• בלוג
  • ארבעה חסרונות מורגשים של neo4j (אחרי 4 חודשים של עבודה)

ארבעה חסרונות מורגשים של neo4j (אחרי 4 חודשים של עבודה)

16/08/2023

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

בינתיים קבלו ארבעה חסרונות של neo4j שיכולים ממש להרגיז בזמן פיתוח-

1. רק בסיס נתונים אחד ברישיון החופשי

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

כשנשאלו לגבי הפיצ'ר התגובה של neo4j היתה-

Presently there are no plans to move the multi-DB feature into community edition. Usually that feature is used by SaaS customers who are Enterprise users. There's good product-market fit there.

2. אין דרך קלה לזכור שינויים

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

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

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

3. שגיאות כתיב בשאילתות יוצרות משתנים חדשים

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

MATCH (tom:Person{name:"Tom Hanks"})-[:ACTED_IN]-(movie:Movie)
RETURN movie;

ואפשר להמשיך ולהחזיר את שמות כל הבמאים של אותם סרטים:

MATCH (tom:Person{name:"Tom Hanks"})-[:ACTED_IN]-(movie:Movie)
MATCH (director:Person)-[:DIRECTED]-(movie)
RETURN director.name;

עכשיו שימו לב למה שמופיע בתוך הסוגריים העגולים:

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

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

ומה היה קורה אם היתה לי שגיאת כתיב בשם המשתנה בשורה השניה? כלומר אם הייתי כותב:

MATCH (tom:Person{name:"Tom Hanks"})-[:ACTED_IN]-(movie:Movie)
MATCH (director:Person)-[:DIRECTED]-(movei)
RETURN director.name;

עכשיו neo4j מתיחס למילה movei (עם שגיאת הכתיב) בתור שם של משתנה חדש, ויחזיר רשימה של כל הבמאים שביימו סרט כלשהו. שם הסרט יישמר במשתנה החדש movei, ותוצאת השאילתה תתעלם לחלוטין מהמשתנה movie שהוגדר בשורה הראשונה.

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

4. אין תמיכה בבסיס נתונים בזיכרון או גירסה ״קלה״ אחרת

אתגר אחרון בעבודה עם neo4j הוא בבדיקות ובפרט בהרצה מקבילית של בדיקות. אם הבדיקות שלי מכילות איזשהו קוד שקשור לבסיס נתונים הן צריכות להתחבר לבסיס נתונים אמיתי. אין גירסת In Memory או משהו שיישמר בקובץ בודד כמו sqlite.

לכן בשביל להריץ בדיקות במקביל ומהר נצטרך לכתוב את כל הקוד של המערכת כך שיוכל לעבוד בתוך טרנזאקציה למצב "בדיקות", להעביר מהבדיקה טרנזאקציה ובסוף הבדיקה לעשות Rollback. לא סוף העולם אבל היה נחמד אם היתה כבר תשתית שעושה את זה בתור פלאגין ל pytest. בינתיים אופציה יותר קלה שאני בחרתי היתה לאפס את כל ה DB בין בדיקות עם fixture אוטומטי. עובד אבל הרבה יותר לאט.

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