Strona główna / Artykuły

Wewnątrz usługi powiadamiania wielu dostawców: kolejki, rozwiązania awaryjne, zaufanie

Przewodnik projektowy niezawodnej usługi powiadamień: przechowywanie danych uwierzytelniających, plan awaryjny dostawcy, ponawiane próby wysyłki przez BullMQ oraz listy oczekujących na obsługę, sprawiedliwość wobec użytkowników oraz weryfikacja podpisu webhooka.

Wydawca: REACTAPP.TOP12 min czytania2659 słów

Źródła: Building NotifyKit: The Engineering Behind Reliable Notifications

Nadawanie e-maila lub SMS wydaje się prostym zadaniem: wystarczy skorzystać z API dostawcy i to wszystko. W warunkach produkcyjnych ta pojedyncza funkcja send() potajemnie zamienia się w problem związanym z systemami rozproszonymi, ponieważ dostawcy mogą wywołać timeout, stosować ograniczenia liczby prób, przestać działać, dostarczać wiadomości w niewłaściwej kolejności, a ponawiane próby mogą prowadzić do powstania duplikatów. Ten artykuł analizuje architekturę usługi powiadamień open-source przeznaczonej dla wielu użytkowników, zbudowanej na bazie Node.js, BullMQ i Redis, omawiając siedem obszarów: tajne dane, awarie dostawców, projektowanie kolejek, ustalanie kolejności, możliwość dostarczenia wiadomości, sprawiedliwość wobec użytkowników oraz weryfikacja webhooków. Dla każdego z tych obszarów przedstawiono decyzję, uzasadnienie jej oraz ograniczenia świadomie przyjęte, abyś mógł wykorzystać to rozumowanie we własnych systemach.

Dwa rodzaje tajnych danych, dwie strategie przechowywania

Pliattaforma do powiadamień przechowuje kilka danych uwierzytelniających, a decydujące pytanie w przypadku każdego z nich jest proste: czy system kiedykolwiek będzie potrzebował oryginalnej wartości?

Klucze dostawcy: szyfruj, ponieważ muszą zostać odzyskane

Klienci dostarczają własne klucze API swojego dostawcy usług e-mail, a platforma musi przedstawiać te klucze dostawcy przy każdej wysyłce. To wyklucza użycie hashowania – wartość musi być możliwa do odzyskania. W procesie projektowym najpierw sprawdza się każdy klucz pod kątem jego dostawcy, a następnie szyfruje go za pomocą AES-256-GCM przed przechowywaniem. GCM to tryb z autoryzacją, więc oprócz zachowania poufności wykrywa również modyfikacje: szyfrogram, który został zmieniony, nie daje się rozszyfrować, zamiast dostarczyć wiarygodnych, lecz błędnych danych uwierzytelniających.

Tekst w formie prostego tekstu nigdy nie dotyka bazy danych, dzienników ani pamięci cache. Jest dekodowany dokładnie w dwóch miejscach: aby wyświetlić zamaskowaną wersję w panelu sterowania oraz w procesorze wysyłania tuż przed złożeniem żądania do usługodawcy. Ograniczenie procesu dekodowania do kilku dobrze zdefiniowanych ścieżek kodowych znacznie ułatwia audyt w przypadku przypadkowego wycieku danych.

Klucze API wydawane przez platformę: hash, ponieważ wystarczy je zweryfikować

Klucze API, które platforma wydaje swoim klientom, działają według odwrotnej logiki. System nie musi ich odzyskiwać, a jedynie potwierdzić, że przedstawiony klucz jest autentyczny. Dlatego klucz jest wyświetlany raz podczas tworzenia, a przechowywany jest tylko jego hash SHA-256. Przy każdym żądaniu przychodzący klucz jest poddawany hashowaniu i porównywany z przechowywanym digestem. Utracony klucz nie może zostać odzyskany i musi zostać ponownie wygenerowany.

Tutaj odpowiedni jest szybki hash typu SHA-256, ponieważ klucze to długie, losowe ciągi znaków o wysokiej entropii. Ta sama opcja byłaby błędna w przypadku haseł używanych przez ludzi, które wymagają celowo wolnego algorytmu, takiego jak bcrypt, scrypt lub Argon2, aby oprzeć się próbom odgadnięcia.

Tajemnice służące do podpisu webhooków: coś pomiędzy

Tajemnica używana do podpisu wysyłanych webhooków jest generowana przez platformę, podobnie jak klucz API, ale jej surowa wartość jest potrzebna za każdym razem, gdy webhook jest podpisywany, podobnie jak klucz dostawcy. Dlatego w stanie spoczynku jest szyfrowana za pomocą AES-256-GCM, dekodowana tuż przed każdą operacją podpisu i przechowywana wyłącznie w pamięci podczas użycia.

Przetrwanie z niewiarygodnymi dostawcami dzięki uporządkowanym alternatywom

Każdy system powiązany z jednym dostawcą odziedzicza jego przerwy w działaniu, ograniczenia przepustowości oraz czas oczekiwania. Usługa zmniejsza to ryzyko dzięki liście priorytetów dla poszczególnych klientów. Klient może zarejestrować kilku dostawców, na przykład SendGrid, Resend i Postmark, oraz ustalić dla nich kolejność priorytetów. Gdy żądanie wysyłki nie wskazuje konkretnego dostawcy, lista prób jest tworzona na podstawie tej kolejności, zaczynając od najwyższego priorytetu.

System przechodzi przez listę dostawców. Jeśli któryś z nich zawiedzie – ze względu na przerwę w działaniu, ograniczenia przepustowości lub zbyt wolną pracę – błąd jest zapisywany, a próbowany zostaje następny dostawca. Pierwszy udany próbę zakończa proces, a klient nie zauważy mechanizmu awaryjnego, chyba że wszyscy dostawcy zawiodą.

Klienci mogą również przypisać wiadomość do jednego dostawcy, opcjonalnie z określonym dostawcą awaryjnym; w takim przypadku próbowany jest tylko ten parzysty zestaw. Nie każdy błąd jest traktowany jednakowo:

  • Błędy trwałe, takie jak odrzucone dane uwierzytelniające lub błędnie sformatowana prośba, powodują natychmiastową awarię, ponieważ ponawianie prób nie może ich naprawić.
  • Błędy tymczasowe, takie jak wygaśnięcie czasu oczekiwania, limity częstotliwości oraz odpowiedzi typu 5xx od dostawcy, są ponawiane z opóźnieniem.

Gdy cała lista doświadcza awarii, błąd trafia do kolejki, której własna polityka ponawiania prób przejmuje kontrolę. Rozwiązanie awaryjne nie sprawia, że błędy dostawcy znikną; daje każdej wiadomości więcej szans na pomyślne wykonanie, zanim błąd stanie się widoczny dla klienta.

Jedna subtelność, na którą warto zwrócić uwagę podczas planowania: timeout nie oznacza, że dostawca odrzucił wiadomość. Dostawca może zaakceptować żądanie, a następnie nie odpowiedzieć na czas, co może skutkować wysłaniem tej samej notyfikacji dwukrotnie. Powszechnymi sposobami radzenia sobie z tym problemem jest przekazywanie klucza idempotencji, jeśli dostawca go obsługuje, lub akceptowanie sporadycznych duplikatów w przypadku wiadomości o niskim znaczeniu. Nasz artykuł na temat kluczy idempotencji dla punktów końcowych Node.js POST szczegółowo omawia ten mechanizm.

Architektura kolejki

Wysyłanie powiadomienia w ramach żądania API, które je uruchomiło, łączy opóźnienie po stronie klienta z naj wolniejszym dostawcą. Powolny lub niedostępny dostawca sprawia, że żądanie HTTP klienta pozostaje w oczekiwaniu, aż w końcu się powiedzie lub wygaśnie termin. Usługa unika tego, rozdzielając akceptację od dostarczania.

Rozdzielenie żądania od dostarczania

API jedynie weryfikuje żądanie, tworzy rekord Job w bazie danych i umieszcza zadanie do obsługi. BullMQ na Redis zajmuje się planowaniem, przy czym istnieje kolejka dla każdego rodzaju zadania:

  • notifications-email dla e-maili klientów
  • notifications-webhook dla webhooków klientów
  • platform-email dla wewnętrznych alertów samej usługi
  • notifications-failed jako kolejka dla zadań, które wyczerpały liczbę prób

Odrębne kolejki oznaczają, że jeden typ zadań może ulegać opóźnieniom lub być dostosowywany bez wpływu na pozostałe. Aby zapoznać się z bardziej szczegółowym omówieniem projektowania zadań w BullMQ, zapoznaj się z budową niezawodnych systemów zadań w tle za pomocą BullMQ i Redis.

Liczba prób odnosi się do całych przebiegów, a nie do dostawców

Każde zadanie ma trzy próby z eksponencjalnym opóźnieniem, a kluczowym elementem jest to, co oznacza „próba”. Nie chodzi tu o jedną próbę u każdego dostawcy. Jedna próba to pełne przetworzenie dostawców klienta w kolejności priorytetowej, z zatrzymaniem się przy pierwszym sukcesie. Gdy wszyscy dostawcy w danej serii zawiodą, zadanie zużyje jedną próbę, a kolejna próba rozpocznie się ponownie od dostawcy o najwyższym priorytecie. Dopiero po trzech nieudanych próbach zadanie wraz z błędem jest przenoszone do kolejki wiadomości błędowych, a oryginał oznaczany jest jako FAILED.

Baza danych jest źródłem prawdy, nawet gdy sama kolejka zawodzi. Zlecenie klienta Job lub PlatformEmailLog dla korespondencji wewnętrznej jest zapisywane ze statusem PENDING przed umieszczeniem go w kolejce. Jeśli do Redis nie można się połączyć lub dodanie zlecenia powoduje błąd, błąd jest zapisywany, o niepowodzeniu jest powiadamiane i wiersz jest aktualizowany na FAILED z podaniem przyczyny. Żadna prośba nie znika bez śladu pomiędzy API a kolejką.

Dla czego służy kolejka wiadomości błędowych

Gdy zlecenie wyczerpie trzy próby, powstaje kopia w folderze notifications-failed, która zawiera pełny treść zlecenia, błąd oraz datę i godzinę, podczas gdy rekord w bazie danych jest ustawiany na FAILED z tą samą przyczyną. Podwójne zapisywanie informacji o niepowodzeniu jest celowe, ponieważ każdy rekord służy innym grupom odbiorców:

  • Wiersz bazy danych jest przeznaczony dla klienta i obsługuje żądanie GET /jobs/:id.
  • Wpis w DLQ jest przeznaczony dla operatora – to jednorazowa kopia, której celem jest monitorowanie i podjęcie działań.

DLQ ma wartość tylko wtedy, gdy jest monitorowane. Każde nadejście zlecenia wyzwala alert w Slacku, korzystając z już utworzonej ścieżki powiadamiania w przypadku błędów e-mailowych platformy. Dzięki temu kolejka staje się sygnałem wczesnego ostrzegania: nagły wzrost liczby zleceń trafiłych do DLQ w krótkim czasie zazwyczaj wskazuje na problem systemowy, taki jak awaria dostawcy lub błąd podczas wdrażania.

Nic nie opuszcza automatycznie DLQ. Zlecenie trafiłe do tej kategorii jest próbowane ponownie tylko wtedy, gdy klient wyśle żądanie POST /jobs/:id/retry, co resetuje jego status na PENDING i restartuje proces.

Priorytet i równoczesność

Zadania mają jedną z trzech priorytetów: CRITICAL, NORMAL lub LOW, które są mapowane na wbudowaną obsługę priorytetów w BullMQ. Ruch w planie darmowym jest zawsze przymuszany do priorytetu LOW, aby nie konkurował z ruchem płatnym. E-maile wysyłane przez platformę całkowicie pomijają tę logikę – zawsze są umieszczane w kolejce jako CRITICAL i wykorzystują krótszy czas oczekiwania.

Pule robotów ciągle pobierają zadania, przy czym każda z nich obsługuje pięć zadań jednocześnie, dzięki czemu w każdej puli może być przetwarzanych maksymalnie pięć zadań naraz.

Kolejność: celowe rozdzielenie

Sługa nie gwarantuje realizacji zleceń dla poszczególnych klientów. Zgodność działania i priorytety są dostosowywane pod kątem wydajności, a dzięki pięciu równoległym procesom wraz z próbami ponownymi, dwa wiadomości wysłane kolejno mogą być łatwo dostarczone w odwrotnej kolejności. Dla większości powiadomień jest to nieszkodliwe, ponieważ są one niezależne. Problem pojawia się tylko w przypadku par powiązanych przyczynowo, gdzie kolejność ma znaczenie, np. gdy jedna wiadomość powinna logicznie następować po drugiej.

Istnieją dwa ogólne rozwiązania, z których każde ma swoją cenę:

  • Serializuj zlecenia dla każdego klienta. Kieruj zadania danego klienta przez pojedynczą ścieżkę, aby zapewnić właściwą kolejność, kosztem możliwości równoległej obsługi tego klienta.
  • Zrób wiadomości odpornymi na zmianę kolejności. Dołącz daty i pozwól odbiorcom samodzielnie interpretować kolejność, co przenosi odpowiedzialność na sposób, w jaki klienci odczytują dostarczone treści.

To ograniczenie jest znane i zostało zaakceptowane ze względu na wydajność. Obecne zużycie jeszcze nie usprawiedliwia żadnych kosztów, dlatego decyzja jest odkładana do chwili, gdy rzeczywiste dane klientów pokażą, jaka kompromisowa opcja będzie lepsza. Jasne udokumentowanie takiej luki jest często lepsze niż jej częściowe rozwiązanie.

Dostarczalność: zapobieganie błędom przed i po wysyłce

E-maile mogą zostać odrzucone, trafić do skrzynki z spamem lub zniknąć w zależności od reputacji nadawcy oraz sygnałów gromadzących się z upływem czasu. Usługa radzi sobie z tym na dwa sposoby.

Przed wysyłką: weryfikacja domeny

Gdy klient chce wysyłać wiadomości z własnego domeny, usługa rejestruje tę domenę u wybranego przez klienta dostawcy i otrzymuje zapisy DNS potwierdzające jej własność. Zapisy te różnią się w zależności od dostawcy, więc są pokazywane klientowi w celu dodania ich do jego hosta DNS. Wysyłanie z tej domeny pozostaje zablokowane, dopóki nie zostanie przeprowadzona weryfikacja.

Po wysyłce: reakcja na zdarzenia dostawcy

Dostawcy zgłaszają informacje o doręczeniach, otwarciach, kliknięciach, odrzuceniach oraz skargach na spam za pomocą webhooków. Usługa przechowuje te zdarzenia i reaguje zgodnie z ich typem:

  • Odrzucenie bez możliwości ponownej próby (adres nie istnieje lub jest trwale odrzucony) automatycznie blokuje adresata, a kolejne próby wysłania wiadomości do tego adresu są blokowane przed wezwaniem dostawcy.
  • Skarga na spam powoduje takie samo zablokowanie.
  • Miękki odrzut, taki jak pełny skrzynka pocztowa lub tymczasowo niedostępny serwer, jest rejestrowany tylko wtedy, gdy problem jest prawdopodobnie tymczasowy.
  • Zasada jest taka, że tylko sygnały wskazujące na trwały problem powodują zablokowanie adresu. To nie rozwiązuje w pełni problemu dostarczalności, ponieważ reputacja rośnie i spada w zależności od czynników wykraczających poza pojedynczą wiadomość, ale zapobiega to temu, by system wielokrotnie wysyłał wiadomości do adresów, o których wiadomo, że zawodzą – co samo w sobie jest jednym z czynników szkodzących reputacji.

    Zapobieganie wzajemnemu szkodzeniu się użytkowników

    Pierwsza bariera: ograniczenia szybkości dla poszczególnych klientów w API

    Zanim żądanie stanie się zleceniem, przechodzi kontrolę limitu szybkości obsługi opartą na Redis dla każdego klienta w oknie 60 sekund, którego wielkość zależy od planu:

    • FREE: 5 żądań na minutę
    • INDIE: 50 żądań na minutę
    • STARTUP: 200 żądań na minutę

    Nagły wzrost liczby żądań z jednego konta jest odrzucany na wczesnym etapie i nigdy nie powoduje przepełnienia kolejki.

    Brama druga: priorytet po przyjęciu

    Zlecenia, które zostały przyjęte, są przetwarzane według priorytetu, a zlecenia z planu FREE mają zawsze priorytet LOW. Procesory zawsze najpierw zajmują się zleceniami o wyższym priorytecie, więc nawet jeśli konto bezpłatne ma zaległości, powiadomienia klientów płatnych są przetwarzane przed nimi.

    Brama trzecia: kwoty miesięczne

    W dłuższej perspektywie kwoty ograniczają objętość przesyłanych danych. Plan FREE umożliwia 100 łącznych wysyłek e-maili i webhooków miesięcznie. Plany płatne ograniczają tylko webhooki; wysyłka e-maili jest dla nich nieograniczona.

    Czego jeszcze brakuje

    Nie ma ścisłej izolacji zasobów: nie ma dedykowanych procesorów dla każdego klienta. Dwóch klientów z tej samej kategorii priorytetu dzieli tę samą przepustowość, a w ramach danej kategorii zlecenia są obsługiwane mniej więcej według zasady „kto pierwszy, ten lepszy”. Przy obecnym natężeniu ruchu nie ma to znaczenia, ponieważ obciążenie mieści się w granicach możliwości wspólnej infrastruktury, a żaden klient nie spowalnia znacząco pracy innego. W większym skali konieczna stałaby się izolacja na poziomie poszczególnych klientów lub sprawiedliwy schemat planowania (na przykład rotacyjny podział zadań między klientami w ramach jednego poziomu priorytetu).

    Weryfikacja webhooków w obu kierunkach

    Każdy integrator wysyła zdarzenia z powrotem za pomocą webhooków: potwierdzenia dostawy, informacje o odrzuceniu wiadomości, zgłoszenia spamu oraz zmiany statusu płatności. Każdy punkt końcowy odbiorczy to publiczny adres URL. Bez weryfikacji nadawcy każdy może opublikować fałszywe zdarzenie „dostarczone”, sfałszować informację o odrzuceniu, aby ukryć adres kogoś innego, lub – co jest najbardziej szkodliwe – sfabrykować zdarzenie płatności, aby uznać subskrypcję za opłaconą.

    Dostawy: jeden schemat na integratora

    Nie istnieje żaden powszechny standard, więc każda integracja stanowi odrębną czynność związaną z bezpieczeństwem:

    • SendGrid podpisuje treść wiadomości za pomocą ECDSA, a podpis jest sprawdzany względem publicznego klucza SendGrid przed zaufaniem do tej treści.
    • Resend dostarcza webhooki przez Svix, który wykorzystuje schemat HMAC z datą czasową, chroniący przed modyfikacjami oraz powtórnym wysyłaniem wiadomości.
  • Postmark nie podpisuje webhooków, więc jego punkt końcowy polega na autoryzacji HTTP Basic Auth z tajemnicą przypisaną każdemu klientowi.
  • Webhooki płatności Paystack są weryfikowane za pomocą HMAC z użyciem SHA-512.
  • Webhooki płatności Polar są walidowane przy użyciu biblioteki standardwebhooks.
  • Przypadek Postmark zawiera pułapkę łatwą do przeoczenia: tajemnicę należy porównywać w czasie stałym, na przykład za pomocą funkcji crypto.timingSafeEqual w Node, a nie zwykłym porównaniem ciągów znaków. Zwykłe porównanie może zakończyć się już przy pierwszej różnicy między znakami, a te różnice czasowe mogą umożliwić atakownikowi odzyskanie tajemnicy krok po kroku, zamiast próbować siłą odgadnąć całą wartość.

    Żaden z tych schematów nie jest wymienny, a każdą integrację trzeba nauczyć się i zaimplementować od zera. Niezmiennikiem we wszystkich przypadkach jest to, że nic, co dociera do punktu końcowego webhooka, nie jest przetwarzane, dopóki nie zostanie zweryfikowane jego pochodzenie.

    Wysyłanie: budowanie zaufania klientów do platformy

    To samo problemy występują w odwrotnej sytuacji, gdy usługa dostarcza webhooki do punktów końcowych klientów. Jeśli klient skonfigurował tajemnicę podpisywania, każda dostawa zawiera nagłówek X-Webhook-Timestamp oraz nagłówek X-Webhook-Signature. Podpis to HMAC-SHA256 obliczony na podstawie ciągu znaków timestamp.payload, co łączy treść z konkretnym momentem. Odbiorca ponownie oblicza podpis i odrzuca również zbyt stare daty, co zapobiega ponownemu wysłaniu przechwyconej prośby później.

    Klienci mają pełną kontrolę nad swoim sekretem: mogą go wygenerować, sprawdzić jego stan, zmienić lub usunąć. Bez skonfigurowanego sekretu webhooki nadal są dostarczane, tylko bez podpisu. Sprzyja to zgodności wstecznej, ale oznacza również, że odbiorcy muszą aktywnie zatwierdzić weryfikację, aby być chronionymi. Aby uzyskać uzupełniający punkt widzenia z perspektywy odbiorcy, zapoznaj się z Zrozumienie kluczy idempotentności w punktach końcowych POST w Node.js.

    Minimalny fallback, potem sprawdzenie podpisu

    deliver przechodzi dostawców po kolei i zwraca pierwszego, który przyjął wiadomość. Ponowienie całego zadania należy do kolejki.

    type Provider = {
      name: string;
      send: (to: string, body: string) => Promise<void>;
    };
    
    const deliver = async (providers: Provider[], to: string, body: string) => {
      const errors: string[] = [];
      for (const provider of providers) {
        try {
          await provider.send(to, body);
          return provider.name;
        } catch (err) {
          errors.push(`${provider.name}: ${(err as Error).message}`);
        }
      }
      throw new Error(`all providers failed\n${errors.join('\n')}`);
    };

    Podpis obejmuje timestamp.payload. Odbiorca odrzuca zły MAC i znacznik starszy niż pięć minut.

    import { createHmac, timingSafeEqual } from 'node:crypto';
    
    const sign = (secret: string, timestamp: string, payload: string) =>
      createHmac('sha256', secret).update(`${timestamp}.${payload}`).digest('hex');
    
    const verify = (
      secret: string,
      timestamp: string,
      payload: string,
      signature: string,
    ) => {
      const ageMs = Math.abs(Date.now() - Number(timestamp));
      if (!Number.isFinite(ageMs) || ageMs > 5 * 60 * 1000) return false;
      const expected = Buffer.from(sign(secret, timestamp, payload));
      const received = Buffer.from(signature);
      if (expected.length !== received.length) return false;
      return timingSafeEqual(expected, received);
    };

    Główne wnioski

    • Decyduj między szyfrowaniem a haszowaniem, pytając się, czy oryginalna wartość musi kiedykolwiek zostać odzyskana.
    • Traktuj alternatywy dostawcy oraz ponowne próby w kolejce jako odrębne warstwy i precyzyjnie zdefiniuj, co oznacza jedna „próba”.
    • Zapisz trwałą rekord w bazie danych przed umieszczeniem w kolejce, aby awaria kolejki nie spowodowała utraty żądania.
  • Kolejka wiadomości nieodebranych jest użyteczna tylko wtedy, gdy uruchamiają się dla niej alerty i istnieje jasna ścieżka ponownej próby.
  • Tłumacz funkcję tylko w przypadku trwałych sygnałów, takich jak odrzucenia bez możliwości dostarczenia wiadomości i skargi na spam.
  • Zastosuj kontrole sprawiedliwości (ograniczenia szybkości, priorytety, kwoty) i jasno określ, jakich mechanizmów izolacji jeszcze nie zapewniasz.
  • Weryfikuj każdy przychodzący webhook za pomocą własnego schematu dostawcy, używaj porównań w czasie stałym oraz podpisuj wychodzące webhooki datą i godziną, aby zapobiec ich ponownemu wysłaniu.
  • Nazwij swoje znane ograniczenia, takie jak kwestia kolejności przetwarzania, i odłóż drogie naprawy do momentu, aż dane pokażą, jaka kompromis jest najodpowiedniejszy.
  • Literatura pokrewna

  • Projektowanie pipeline'u uwag bez duplikatów w SQS, SES i Redis — Jak projekt z dwoma kolejkami w SQS, alternatywa SES-to-SendGrid oraz atomowe zamykanie w Redis zapewniają, że dokumenty biznesowe trafiają do odbiorców dokładnie raz, nawet przy dużym obciążeniu.
  • Sześć styli API w porównaniu: REST, GraphQL, WebSockets, Webhooks, gRPC, SOAP — Dowiedz się, w jaki sposób REST, GraphQL, WebSockets, webhooks, gRPC i SOAP rozwiązują różne problemy wymiany danych, a także przegląd decyzji pomocny w wyborze odpowiedniego rozwiązania.
  • Od Modulo do Hash Ring: skalowanie floty pamięci cache w Node.js bez problemów z obciążeniem — Dowiedz się, dlaczego sharding typu hash-mod-N powoduje problemy w bazach danych przy zmianie węzłów, jak porównują się metody hashingu rendezvous, jump i ring, oraz jak stworzyć zrównoważony pierścień ważony w Node.js.
  • Wysyłanie i odbieranie SMS-ów w Node.js za pomocą Twilio Webhooks i Express — Stwórz mały serwer Express, który wysyła teksty przez Twilio, odbiera odpowiedzi za pośrednictwem webhooku udostępnionego przez ngrok i automatycznie odpowiada na nie za pomocą TwiML.
  • REST kontra GraphQL: rzeczywiste kompromisy stojące za każdą z tych architektur — Wyjaśnia konkretne problemy, które rozwiązują REST i GraphQL, ich wewnętrzną mechanikę oraz ukryte kompromisy, które należy wziąć pod uwagę przed wyborem jednego z nich do swojej API.
  • Powiadomienia w React Native: uprawnienia, kanały i cykl życia FCM — Dowiedz się, jak uprawnienia, kanały Androida, tokeny FCM oraz mechanizmy obsługi stanów pierwszego planu, tła i zakończenia działania współpracują ze sobą w systemie powiadomień React Native z Notifee.