אז Node או Elixir?

27/07/2019

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

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

defmodule HelloWorldWeb.HomeController do
  use HelloWorldWeb, :controller

  def count_lines_in_file(conn, _params) do
    { _, count } = File.stream!("/etc/shells")
                   |> Stream.with_index
                   |> Enum.at(-1)

    json(conn, %{ res: count + 1 })
  end
end

יש פה בסך הכל שלוש שורות של לוגיקה - פתח קובץ, תוסיף לכל שורה אינדקס ותן לי את האינדקס של השורה האחרונה. פנטסטי.

בשביל השוואה אותו הקוד ב Node.JS יראה בערך כך:


function countLinesInFile() {
  return new Promise((resolve, reject) => {
    try {
      let count = 0;
      const rl = readline.createInterface({
        input: fs.createReadStream('/etc/shells'),
        console: false
      });

      rl.on('line', (input) => {
        count += 1;
      });
      rl.on('close', (input) => {
        resolve(count);
      });
    } catch (err) {
      reject(err);
    }
  });
}

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

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

קוד ה Node.JS התמודד הרבה יותר טוב עם אותה בדיקה וענה לכל הבקשות תוך 4 שניות, ואף בקשה לא חיכתה יותר מכמה עשרות מילי-שניות עד לקבלת תשובה.

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

https://stressgrid.com/blog/benchmarkinggovsnodevs_elixir/

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

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