באג ריפורט: אזורי זמן

11/04/2021

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

1. המערכת

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

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

class CreateMeetingReminderJob < ApplicationJob
  def perform(meeting_id)
    meeting = Meeting.find(meeting_id)
    MESSAGING_SERVICE.meetings_queue.publish(
      { id: meeting.id, starts_at: meeting.starts_at }.to_json
    )
  end
end

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

    channel.consume(queue, async function(msg) {
      const data = JSON.parse(msg.content);
      const numRemoved = await agenda.cancel({ data: { id: data.id }});
      console.log(`canceled previous ${numRemoved} jobs`);
      agenda.schedule(data.starts_at, "send reminder", {
        id: data.id
      });
    }, { noAck: true })

2. מה המשתמשים ראו

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

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

3. איך זה קרה

אזור הזמן ב Node.JS נקבע לפי הגדרות המערכת. אפשר לראות אותו באמצעות הפעלת:

> new Date().toString()
'Sat Apr 10 2021 16:23:39 GMT+0300 (Israel Daylight Time)'

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

4. מה עושים בעתיד

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

$ tz=UTC node reminders_service.js