Nasz artykuł na temat kompatybilności z PostgREST opisuje szczegółowo, co serwer obejmuje funkcjonalnie (filtry, osadzanie, RPC, RLS) i co pozostawia Tobie. Ten pochodzi skądś indziej. Dokumentuje, korzystając z kodu Aurabase i oficjalnej dokumentacji PostgREST jako źródeł, gdzie i dlaczego PostgREST faktycznie osiąga plateau na dużą skalę. Nie odtwarzamy tutaj banku obciążenia, którego sami nie uruchomiliśmy. Nasza metodologia testów porównawczych wyjaśnia, dlaczego izolowana liczba, bez opublikowanego protokołu, nie wydaje nam się wiarygodna.
Najważniejsze
- Sam PostgREST jest lekki: od 50 do 250 milirdzeniowych procesorów, od 64 do 128 MB pamięci RAM na replikę w dedykowanych instancjach Aurabase. Surowa przepustowość HTTP prawie nigdy nie jest czynnikiem ograniczającym w produkcji.
- Prawdziwy pułap to budżet połączeń Postgres:
PGRST_DB_POOL× repliki. Zweryfikowano w kodzie Aurabase: 20 połączeń na projekt na poziomie dedykowanym (10×2), 4 na poziomie współdzielonym (2×2). Jest to celowy wybór, aby zmieścić więcej dzierżawców w tym samymmax_connections. Prefer: count=exactwymusza kosztowne skanowanie MVCC na dużych tabelach. PostgREST dokumentuje dwie tańsze alternatywy:count=plannedicount=estimated, których koszt jest przybliżony.- Pułap
db-max-rows(domyślnie 1000 linii w Aurabase) obcina odpowiedź BEZ zgłaszania tego wContent-Range(mierzone w warunkach rzeczywistych, szczegółowo opisane poniżej). - Po migracji DDL pamięć podręczna schematu PostgREST jest ładowana asynchronicznie. Brama Aurabase ponawia próbę do 8 razy (łącznie około 3,5 sekundy w najgorszym przypadku), zanim się poddaje, co jest zachowaniem udokumentowanym bezpośrednio w kodzie.
Co mierzy benchmark PostgREST, a czego nie mierzy
Test przepustowości HTTP na PostgREST mierzy głównie Postgres, rzadko PostgREST. Serwer to cienka warstwa tłumaczeń przed bazą. W zdecydowanej większości rzeczywistych obciążeń czas odpowiedzi jest zdominowany przez wykonane zapytanie SQL, a nie proces, który je wygenerował.
Projekt PostgREST utrzymuje dedykowane repozytorium dla tego tematu, PostgREST/postgrest-benchmark na GitHubie, które śledzi zmiany przepustowości od wydania do wydania, zamiast publikować izolowane dane marketingowe. Nie wykonaliśmy go tutaj ani nie opublikowaliśmy ponownie. Jego wyniki zależą od sprzętu, rozmiaru schematu i testowanego scenariusza, a dokładnie od zmiennych, które nasz własny protokół porównawczy wymaga udokumentowania przed podaniem liczby.
Poniżej PostgREST znajduje się pgbench, który mierzy warstwę, która naprawdę ma znaczenie: czas transakcji SQL przy jednoczesnym obciążeniu. To jest oficjalne narzędzie do testowania PostgreSQL (postgresql.org/docs/current/pgbench.html, dostęp: 24 sierpnia 2026). Zamiast reprodukować tutaj ten protokół, ten artykuł dokumentuje cztery konkretne ograniczenia architektoniczne PostgREST w produkcji, każde zweryfikowane w kodzie źródłowym Aurabase lub w oficjalnej dokumentacji projektu.
Rzeczywisty ślad instancji PostgREST w Aurabase
Każdy projekt silnika Aurabase Postgres otrzymuje dwie dedykowane repliki PostgREST, kolokowane z jego klastrem. Manifest Kubernetes, który je wdraża, ustawia skromne zasoby.
To, co te repliki tak naprawdę zużywają, to nie procesor: są to połączenia z podstawową bazą Postgres. Każda instancja PostgREST łączy się bezpośrednio z instancją podstawową (-rw), bez przechodzenia przez moduł puli PgBouncer wdrożony dla dzierżawcy. Wybór ten został już szczegółowo opisany w naszym artykule na temat kompatybilności PostgREST: mechanizm przeładowywania schematu LISTEN/NOTIFY wymaga trwałego połączenia, niekompatybilnego z modułem puli w trybie transakcyjnym. Co dodaje ten artykuł: ile to faktycznie kosztuje pod względem połączeń i gdzie osiąga szczyt.
Rozmiar tej puli na replikę (PGRST_DB_POOL) celowo różni się w zależności od poziomu projektu, zweryfikowanego w k8s_tenant.rs, funkcji budującej manifest PostgREST dla każdego projektu:
| Łożysko | PGRST_DB_POOL / replika | Repliki | Połączenia / przebudzony projekt |
|---|---|---|---|
| Dedykowane (premium, A1) | 10 (domyślnie PostgREST) | 2 | 20 |
| Udostępnione (flota, bezpłatna/pro/zespół) | 2 (domyślny Aurabase, obniżony) | 2 | 4 |
Na poziomie dedykowanym ograniczenie jest złagodzone: projekt ma własny klaster CNPG, a zatem własny max_connections, bez żadnych sąsiadów do stracenia. Na wspólnym poziomie kilka projektów z tej samej organizacji znajduje się w jednym klastrze: to właśnie ten kontekst decyduje o budżecie połączeń, co opisano w następnej sekcji.
Budżet połączeń decyduje o tym, ilu najemców jest uruchomionych w tym samym czasie
We współdzielonym klastrze Postgres to nie przepustowość HTTP ogranicza liczbę jednocześnie aktywnych projektów. Jest to liczba połączeń, które ich instancje PostgREST utrzymują otwarte na serwerze podstawowym, w porównaniu z dostępnym max_connections.
Aurabase oblicza ten budżet bezpośrednio z rzeczywistych limitów klastra, zaznaczonych w fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé, piętro na poziomie 1. Stała rezerwa wynosi 10 połączeń (superużytkownik, menedżer instancji CNPG, eksporter metryk, margines administratora dostawcy usług). W przypadku dostarczonych defektów (pula 2 na replikę, 2 repliki lub 4 połączenia na przebudzony projekt) obliczenia dają trzy różne budżety w zależności od poziomu rozmiaru klastra.
Źródło: na podstawie fleet.rs::derive_wake_budget i wake_budget_for_org_plan, kod Aurabase, ponownie przeczytany 24 sierpnia 2026 r.
Budżet ten nie jest kwotą posiadanych projektów: organizacja team może prowadzić 50 projektów, z których większość jest nieaktywna. Jest to ograniczenie współbieżności: liczby projektów, które mogą jednocześnie utrzymywać otwarte połączenia na serwerze podstawowym. Budzenie przekroczenia budżetu nie kończy się niepowodzeniem, jest odkładane do czasu, aż projekt rodzeństwa ponownie przejdzie w tryb uśpienia, zaewidencjonowany w tym samym pliku. Sam temat rozmiaru max_connections został rozwinięty w naszym artykule na temat dostrajania max_connectionsoraz dedykowanego/wspólnego kompromisu jako całości w dedykowanej i współdzielonej bazie.
Dlaczego wolisz: count=exact spowalnia zapytanie na dużej tabeli
Pytanie o dokładną sumę zmusza Postgres do zliczania widocznych wierszy przefiltrowanego wyniku przy każdym zapytaniu. Jest to koszt rosnący wraz z tabelą, a nie jest to bezpłatna operacja.
PostgreSQL nie obsługuje żadnych indeksowanych liczników wierszy po wyjęciu z pudełka. W MVCC widoczność wiersza zależy od transakcji, która go odczytuje. Dokładny COUNT(*) musi zatem odwiedzić wiersze kandydujące, a nie odczytywać wstępnie obliczoną wartość. Jest to dobrze udokumentowane ograniczenie strukturalne w ekosystemie Postgres, w tym dostawcy usług analitycznych, tacy jak ClickHouse, którzy porównują swoje własne przybliżone liczniki z zachowaniami transakcyjnymi Postgres.
PostgREST natywnie dokumentuje te trzy strategie liczenia (postgrest.org, dostęp: 24 sierpnia 2026 r.). Strategia exact gwarantuje sumę w cenie skanowania. planned zwraca prawie bezpłatną wycenę z narzędzia do planowania zapytań. estimated automatycznie przełącza się między nimi w oparciu o próg. Wybór nie jest kosmetyczny: paginacja wymagająca count=exact w tabeli zawierającej kilka milionów wierszy płaci za ten skan na każdej stronie, nawet jeśli użytkownik nigdy nie sprawdza ostatniej.
Obcięcie wierszy db-max jest niewidoczne bez count=exact
Zakończenie wiersza może obciąć odpowiedź PostgREST bez żadnych wskazówek w treści lub nagłówkach, chyba że wyraźnie zażądano dokładnej sumy. Zmierzyliśmy to w rzeczywistych warunkach na dedykowanej instancji Aurabase, a nie zakładaliśmy.
W 10-wierszowej tabeli testowej z PGRST_DB_MAX_ROWS=5PostgREST v12.2.3 renderuje dokładnie ten sam nagłówek Content-Range w dwóch bardzo różnych sytuacjach:
| Zapytanie | Renderowane linie | Zakres treści | meta (Aurabaza) |
|---|---|---|---|
| ?limit=50 (bez licznika) | 5/10, naprawdę | 0-4/* | {} |
| ?limit=50&count=dokładne | 5/10, naprawdę | 0-4/10 | {łącznie: 10} |
Bez count=exactodpowiedź składająca się z 5 wierszy jest nie do odróżnienia od tabeli, która tak naprawdę zawierałaby tylko 5: Content-Range: 0-4/* opisuje renderowane wiersze, a nie zastosowany limit. Rzeczywisty obowiązujący limit nie pojawia się w tym przypadku nigdzie, mierzony bezpośrednio na ścieżce PostgREST pakietu Aurabase SDK.
Jeśli wdrożenie PostgREST ustawi db-max-rows (domyślna wartość Aurabase to 1000), klient, który porównuje data.length z żądanym limitem w celu wykrycia pełnej strony, może się mylić. Błąd pojawia się, gdy sufit serwera spadnie poniżej tego limitu. Jedynym wiarygodnym sygnałem jest porównanie liczby odebranych linii z total zwróconym przez count=exact, co bezpośrednio uwzględnia kompromis kosztów opisany w poprzedniej sekcji.
Ponowne ładowanie pamięci podręcznej schematu po migracji
PostgREST przechowuje schemat Postgres w pamięci podczas uruchamiania. Po operacji DDL (utwórz tabelę, dodaj kolumnę) pamięć podręczną musi zostać ponownie załadowana, zanim nowa trasa odpowie, a to przeładowanie jest asynchroniczne.
Zapis docierający do tego okna może otrzymać przejściowy błąd 404 (pamięć podręczna nie jest jeszcze aktualna), mimo że tabela rzeczywiście istnieje po stronie Postgres. Bramka Aurabase absorbuje to za pomocą ograniczonej pętli ponownych prób, zweryfikowanej w postgrest_proxy.rs: do 8 prób, zwiększając czas oczekiwania (250 ms plus 100 ms na próbę), łącznie 3,5 sekundy w najgorszym przypadku. Mechanizm ten dotyczy tylko zapisów, nigdy odczytów.
Bramka nie wysyła żadnego sygnału przeładowania: tylko czeka. Jedynym prawdziwym wyzwalaczem jest pg_notify('pgrst', 'reload schema') wydany przez usługę bazy danych na ścieżce DDL. Jeśli ścieżka migracji zapomni wyemitować ten sygnał, 8 prób wyczerpie się w pamięci podręcznej, która nigdy się nie zmieni, ryzyko udokumentowane w komentarzu do kodu, a nie ukryte.
W przypadku samodzielnego wdrożenia PostgREST lekcja jest uogólniona. Każda ścieżka DDL w aplikacji powinna wywołać przeładowanie poprzez sygnał NOTIFY lub SIGUSR1 do procesu. W przeciwnym razie migracja powoduje gwałtowny wzrost opóźnienia p99 maskowany jako sporadyczne błędy zaraz po wdrożeniu.
To, co dzieli architektura, a nie surowa przepustowość
Cztery udokumentowane tutaj ograniczenia mają jedną wspólną cechę: żadne nie są widoczne w izolowanym teście przepustowości HTTP, ale wszystkie cztery określają, czy wdrożenie PostgREST skaluje się do środowiska produkcyjnego.
- Budżet połączenia: ogranicza liczbę dzierżawców aktywnych jednocześnie w klastrze współdzielonym, niezależnie od przepustowości na dzierżawcę.
- Koszt dokładnej LICZBY: rośnie wraz ze stołem, a nie obciążeniem; jest pomijany za pomocą
planned/estimated. - Ciche obcięcie: poprawnie skonfigurowany limit wiersza może nadal zakłócać słabo oprzyrządowane stronicowanie.
- Przeładowanie schematu: okno opóźnienia po każdej migracji, ograniczone, jeśli sygnał przeładowania jest dobrze podłączony, w przeciwnym razie nieograniczone.
Niezależnie od tego, czy wybierasz między hostowanym PostgREST, warstwą GraphQL w stylu Hasura, czy niestandardowym interfejsem API, te cztery osie są lepszym punktem porównania niż izolowana liczba wymagań/s. Zobacz nasze porównanie PostgREST vs Hasura vs niestandardowe API. Wybór Poolera, który stoi przed Twoją bazą danych, jest równie ważny: nasze porównanie PgBouncer vs Supavisor vs PgCat szczegółowo opisuje, dlaczego PostgREST nie może przejść przez Pooler w trybie transakcyjnym.
Często zadawane pytania
O czym pamiętać
PostgREST prawie nigdy nie psuje się pod samym obciążeniem HTTP: jego architektura jest na to zbyt prosta. To, co powoduje przerwy w produkcji, to to, co ją otacza: ile połączeń utrzymują otwarte jej repliki, ile dokładnie kosztuje ogółem. Obejmuje to również to, czy obcięcie pozostaje widoczne i jak długo trwa okno po migracji.
Te cztery ograniczenia nie są specyficzne dla Aurabase: mają zastosowanie do każdego wdrożenia PostgREST, zarówno hostowanego samodzielnie, jak i zarządzanego. Ten kod pokazuje, jak wdrożenie z wieloma dzierżawcami sprawia, że są one wyraźne, a nie zaskakujące w środowisku produkcyjnym.