>_ DevTrendspl

Język

Strona główna

Języki

Sekcje

Frontend Backend Mobilne DevOps AI / ML GameDev Blockchain Systemy wbudowane Bezpieczeństwo
Rust

Jak sprawić, by PostgreSQL dokańczał zadania w tle nawet po ponownym uruchomieniu

Wyobraź sobie taki scenariusz: piszesz złożony przepływ pracy do przetwarzania danych. Musisz pobrać rekordy, wysłać je do zewnętrznego API, zaktualizować status w bazie danych, a następnie uruchomić generowanie raportu. Jeśli baza danych "ulegnie awarii" lub serwer zostanie ponownie uruchomiony w połowie tego procesu, standardowa procedura PL/pgSQL po prostu przerwie działanie. Rezultatem są "wiszące" dane i ból głowy: jak ustalić, na którym etapie wszystko się zatrzymało i jak bezpiecznie wznowić proces?

Zazwyczaj rozwiązujemy to obejściami. Konfigurujemy pg_cron, tworzymy tabele z kolejkami zadań, piszemy workery w Pythonie lub Go, które ciągle odpytują bazę danych. W najgorszym przypadku wciągamy do projektu ciężkie potwory jak Temporal czy Airflow. Ale inżynierowie z Microsoft postanowili, że cała ta złożoność jest niepotrzebna i wydali rozszerzenie pg_durable.

Dlaczego programiści tego potrzebują

Główna idea projektu to nadanie PostgreSQL zdolności do wykonywania długotrwałych funkcji, które są odporne na awarie. W terminologii autorów nazywa się to "Durable Execution" (trwałe wykonywanie).

pg_ durable logo

Jeśli Twój przepływ pracy jest zdefiniowany za pomocą pg_durable, baza danych zapisze swój stan po każdym kroku. Jeśli serwer zostanie ponownie uruchomiony dokładnie w trakcie wykonywania ciężkiego zapytania lub wywołania API, rozszerzenie wznowi zadanie od ostatniego pomyślnego punktu kontrolnego. Nie musisz już sklejać ze sobą zadań cron, workerów i tabel statusu.

Jak to działa pod maską

Projekt jest napisany w Rust z użyciem pgrx. Architektonicznie to nie tylko wrapper, ale pełnowartościowe środowisko wykonawcze wewnątrz bazy danych. Składa się z kilku warstw:

  1. SQL DSL: zestaw operatorów do opisywania grafu zadań.
  2. Background Worker: proces działający w tle wewnątrz PostgreSQL, który zarządza wykonywaniem.
  3. Duroxide: silnik orkiestracji (również development Microsoft), który obsługuje deterministyczne powtórki i punkty kontrolne.

Ciekawe jest to, że autorzy wybrali podejście "natywne dla SQL". Logikę opisujesz bezpośrednio w konsoli lub migracji, używając specjalnych operatorów jak ~> czy |=>.

Workflow graph example

Kluczowe funkcje

Oto trzy rzeczy, które naprawdę upraszczają życie.

Odporność na awarie bez zewnętrznych usług

Nie potrzebujesz Redis do kolejek ani osobnych instancji Temporal. Wszystko żyje bezpośrednio w tabelach df.* i duroxide.*. Dane i logika sterowania znajdują się w tym samym środowisku transakcyjnym. Eliminuje to klasyczny problem systemów rozproszonych, gdzie zadanie istnieje w kolejce, ale zmiany w bazie danych nie zostały jeszcze zatwierdzone.

Równoległe wykonywanie i scalanie

Używając operatorów, możesz łatwo "rozgałęzić" wykonywanie zadań na wiele równoległych strumieni, a następnie poczekać na ich zakończenie. W README jest jasny przykład: jednocześnie zliczaj użytkowników, zamówienia i przychody, a następnie konsoliduj wszystko w jednym kroku raportowania.

Integracja z systemami zewnętrznymi

Rozszerzenie ma funkcję df.http(). Oznacza to, że możesz wywoływać zewnętrzne mikrousługi lub API sieci neuronowych bezpośrednio z długotrwałego procesu. Jeśli API zwróci błąd 500, pg_durable może poczekać i ponowić próbę bez blokowania całej operacji bazy danych.

Przykład kodu

Oto jak wygląda tworzenie prostego zadania bezpośrednio w SQL:

-- Запускаем процесс: берем 100 необработанных документов и обновляем их статус
SELECT df.start(
    'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
    ~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
);

Ten kod utworzy instancję Workflow, która jest gwarantowana do wykonania. Jeśli UPDATE nie powiedzie się, system będzie dokładnie wiedział, który batch danych próbował przetworzyć.

Gdzie to się przyda

Widzę kilka scenariuszy, w których pg_durable zaoszczędzi Ci mnóstwo czasu.

Po pierwsze, potoki AI. Jeśli musisz przepuścić tysiące wierszy przez embeddingi i zapisać je do pgvector, to idealne narzędzie. Dzielenie tekstu, wywołania API OpenAI i upserty do bazy danych są zapakowane w jedną niezawodną potok.

Po drugie, przetwarzanie danych na dużą skalę (ETL). Zamiast pisać potworne procedury PL/pgSQL, które padają, gdy kończy się WAL, możesz podzielić pracę na małe kroki z punktami kontrolnymi.

Po trzecie, automacja administracji. Na przykład sprawdzanie fragmentacji tabel, wysyłanie powiadomienia i czekanie na zatwierdzenie — to wszystko można opisać jako Durable Function.

Niuanse i ograniczenia

Projekt jest w statusie Preview. Oznacza to, że jeszcze za wcześnie, żeby wypuszczać go na produkcję, ale idealnie nadaje się do narzędzi wewnętrznych.

Ważne ograniczenia: potrzebujesz PostgreSQL 17 lub 18. Jeśli masz starsze wersje, będziesz musiał zaktualizować. Kolejna kwestia to bezpieczeństwo. Domyślnie wszystkie funkcje w shared_preload_libraries wymagają praw superusera do konfiguracji, choć deweloperzy udostępnili system uprawnień przez df.grant_usage() dla zwykłych ról.

System jest zoptymalizowany pod kątem SQL. Jeśli potrzebujesz złożonej logiki biznesowej z skomplikowanymi pętlami w Pythonie lub Node.js, lepiej użyć silnika Duroxide bezpośrednio z kodu aplikacji. pg_durable dotyczy konkretnie trzymania obliczeń jak najbliżej danych.

Microsoft aktywnie inwestuje w Postgres (pamiętaj o przejęciu Citus), a pg_durable to kolejny krok w kierunku przekształcenia bazy danych w pełnowartościową platformę aplikacyjną. Jeśli masz dość tego, że Twoje zadania w tle "znikają" w najbardziej nieodpowiednich momentach, albo jeśli masz już dość konfigurowania zewnętrznych orkiestratorów dla prostych potoków, koniecznie sprawdź to repozytorium. Aby zacząć, wystarczy prosty kontener Docker lub Codespaces, które są już skonfigurowane w zakładce Development w repozytorium.

Powiązane projekty