Блог

Сигнали, които се задействат веднъж, а не веднъж на всеки сървър

Един от оперативните ни сигнали — „ниски кредити“ за публикувано приложение на строител — трябва да стига до нас най-много веднъж на ден. Един следобед пристигна пет пъти. Причината: всяко копие на нашия API помнеше в собствената си памет кои сигнали вече е изпратило, така че два сървъра го изпращаха два пъти, а всяко внедряване изтриваше тази памет и позволяваше ново изпращане. По същия модел фонова задача вършеше работата си два пъти. Сега и двете се координират чрез споделено хранилище, а при недостъпност на хранилището и двете се провалят в безопасната посока.

Симптомът

Mythex изпраща сигнали към екипа, когато нещо изисква внимание: услуга е спряла, задача е заседнала, баланс на строител е на изчерпване, докато приложението му е публикувано. Всеки сигнал има прозорец за дедупликация — период, в който същият сигнал се изпраща само веднъж. При сигнала за ниски кредити този прозорец е 24 часа.

Пет копия на един и същи сигнал за един следобед означаваха, че прозорецът не държи. По-лошото беше, че дойдоха по двойки, на около 11 минути едно от друго.

Защо се случи

Пакетът ни за сигнали пазеше дедупликацията в паметта: таблица „ключ на сигнала → кога е изпратен за последно“, вътре в работещия процес. Това работи, когато има един процес, който никога не се рестартира. Продукционната среда няма нито едно от двете:

  • Два API реплики. Продукцията работи с две копия на API. Всяко имаше собствена таблица и затова всяко изпращаше сигнала веднъж. Ето защо се появяваха по двойки.
  • Внедряванията. Всяко внедряване заменя и двете реплики с нови процеси и празни таблици. След внедряване сигналът излизаше отново, сякаш никога не е бил изпращан.

Неравномерните ~11 минути между двата сигнала във всяка двойка идваха от задачата, която създава този сигнал. Тя работи на двете реплики и взима заключване, така че репликите го печелят на свой ред и всяка го вдига, когато му дойде редът.

Имаше и втори, по-тих бъг в същия код. За да не расте таблицата безкрайно, тя се почистваше сама, когато достигне 500 ключа — изтриваше всеки запис, по-стар от 15 минути, независимо от прозореца му. Под натоварване дедупликация от 24 часа можеше да трае една четвърт час.

Корекцията

Един споделен запис за всеки сигнал

Преди API да изпрати сигнал, той сега заявява ключа за дедупликация в Redis — споделено хранилище в паметта, с което и двете реплики комуникират и което не се рестартира, когато се рестартира API. Заявката е една операция: „задай този ключ само ако не съществува и го изтрий след прозореца“. Която реплика заяви първа, изпраща сигнала; другата вижда, че ключът е зает, и мълчи. Нов процес след внедряване намира ключа все още там.

Ключът и прозорецът идват от същото правило, което пакетът за сигнали използва, сега споделено между двата слоя, така че те не могат да се разминат за това какво е „същият сигнал“.

Отказ в отворен режим, а не в затворен

Най-важното решение беше какво да правим, когато Redis е недостъпен. Избрахме да изпратим сигнала въпреки всичко. Ако Redis е спрял или не отговори в рамките на 1,5 секунди, сигналът излиза. Дублиран сигнал е шум. Пропуснат сигнал означава, че някой не е бил уведомен.

Най-сериозните сигнали, P0, изобщо не се спират от дедупликацията.

Всеки запис пази собствения си прозорец

В собствената дедупликация в паметта на пакета за сигнали всеки запис сега съхранява кога свършва прозорецът му, а почистването премахва само записите, чийто прозорец е изтекъл. Ключ от 24 часа остава 24 часа, колкото и други сигнали да пристигнат.

Същият бъг на друго място

Същият модел беше удвоил и част от фоновата работа: задача за съгласуване, която поддържа записа ни за статуса на всяко работно пространство в съответствие с доставчика на нашата средата. Всеки проход изброява всяка среда в акаунта — хиляди в продукцията, около пет секунди — плюс всеки работещ или паузиран проект. И двете реплики я пускаха на всеки две минути.

Нейните записи са идемпотентни — изпълнението им два пъти дава същия резултат като веднъж — така че нищо не се счупи. Просто беше двойна работа. Сега всеки двуминутен прозорец се заявява в Redis по същия начин, по който нашите измервания на използването вече заявяваха своите, и само репликата, която заяви прозореца, пуска прохода. Ако Redis е недостъпен, проходът пак се пуска: изпълнението на работата два пъти е безвредно, а неизпълнението ѝ — не.

Как тествахме

Тестовете моделират реалната настройка. Зареждат модула за сигнали два пъти, като две отделни „реплики“, сочат и двете към едно споделено хранилище, повдигат един и същи сигнал от всяка, после зареждат трето, ново копие, което замества „след внедряване“, и го повдигат отново. Изпраща се точно един сигнал. Други тестове проверяват, че сигнал за друг строител все още излиза, че P0 се изпраща от двете реплики и че сигналът се изпраща и когато хранилището е спряно или закъснява над лимита. Почистването има собствен тест: ключ от 24 часа оцелява, след като пристигнат 600 други сигнала 20 минути по-късно.

За задачата за съгласуване две реплики, които тикват едновременно, плюс трети тик в същия прозорец, дават един списък.

Какво взехме от това

  • Състоянието в паметта е за всеки процес поотделно. Всичко, което трябва да е вярно между сървърите — „изпратено веднъж“, „изпълнено веднъж“ — трябва да живее там, където те го споделят.
  • Внедряванията са рестартирания. Състояние, което оцелява само до следващото рестартиране, не оцелява един обикновен работен ден.
  • Решете в коя посока да се провалят нещата. За сигналите недостъпно споделено хранилище трябва да означава повече сигнали, а не по-малко.
  • Търсете модела, не само конкретния случай. Сигналът беше шумният случай; удвоената задача за съгласуване беше същият бъг, който тихо струваше работа.

Нищо от това не се вижда отвътре в Mythex. За повече за това как поддържаме платформата стабилна вижте как стигнахме до нула 502 грешки при внедряване и липсващия swap файл. Ако си нов в идеята за работа с повече от едно копие на услуга, нашето ръководство за какво е сървър обяснява основите.

Mythex · Pricing · Създай всичко с ИИ · Шаблони за уебсайтове и приложения · Mythex в сравнение · Речник на създаването на приложения с ИИ · Безплатни инструменти за създаване на приложения с ИИ · English

English አማርኛ العربية বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)