გაფრთხილებები, რომლებიც ერთხელ იგზავნება და არა სერვერზე ერთხელ
ერთ-ერთი ოპერაციული გაფრთხილება — „დაბალი კრედიტები“ ბილდერის გამოქვეყნებული აპისთვის — იმისთვის არის გათვლილი, რომ დღეში ერთხელ მაქსიმუმ მოგვაღწიოს. ერთ შუადღეს ის ხუთჯერ მოვიდა. მიზეზი: 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 წუთის შემდეგ მოვიდა.
შეთანხმების სამუშაოსთვის ორი რეპლიკა ერთდროულად რომ ტიკავს, პლუს მესამე ტიკი იმავე ფანჯარაში, ერთ listing-ს იძლევა.
რა ვისწავლეთ
- მეხსიერებაში მდგომარეობა პროცესზეა მიბმული. ყველაფერი, რაც სერვერებს შორის უნდა იყოს ჭეშმარიტი — „გაიგზავნა ერთხელ“, „გაეშვა ერთხელ“ — იმ ადგილას უნდა ინახებოდეს, რომელსაც ისინი იზიარებენ.
- დეპლოები რესტარტებია. მდგომარეობა, რომელიც მხოლოდ შემდეგ რესტარტამდე გრძელდება, ჩვეულებრივ სამუშაო დღესაც ვერ ძლებს.
- გადაწყვიტე, რომელი მხრივ ჩაიშალოს. გაფრთხილებებისთვის საერთო მაღაზიის ჩავარდნამ მეტი გაფრთხილება უნდა ნიშნავდეს, და არა ნაკლები.
- ეძებე ნიმუში და არა მხოლოდ კონკრეტული შემთხვევა. გაფრთხილება იყო ხმაურიანი შემთხვევა; ორმაგი შეთანხმების სამუშაო იგივე შეცდომა იყო, რომელიც უხმაუროდ ზრდიდა ხარჯს.
ამ ყველაფერს Mythex-ის შიგნიდან არ ხედავ. იმისთვის, თუ როგორ ვინარჩუნებთ პლატფორმას სტაბილურად, იხილე როგორ მივიყვანეთ დეპლოები ნულ 502 შეცდომამდე და გამოტოვებული swap ფაილი. თუ ახალი ხარ იდეასთან, რომ სერვისის რამდენიმე ასლი უნდა გაუშვა, ჩვენი გზამკვლევი რა არის სერვერი საფუძვლებს გაჩვენებს.