Twoje zapytania SQL są szybkie, ale API jest wolne

Twoje zapytania do bazy danych są szybkie. Caching działa. Zadania w tle (background jobs) działają poprawnie.

Mimo to Twoje API wciąż działa wolno. Zużycie procesora (CPU) jest wysokie.

Problem nie leży w bazie danych. Problem tkwi w warstwie Ruby.

Wąskie gardło często pojawia się po tym, jak dane opuszczą bazę danych i trafią do Twojej aplikacji. Dzieje się to z trzech głównych powodów:

  • Przeładowana serializacja
  • Nadmierna alokacja obiektów
  • Powtarzalne obliczenia

Oto jak je naprawić.

  1. Unikaj przeładowanej serializacji

Wielu programistów zamienia całe modele na JSON.

render json: @shipments

Jeśli przesyłka ma 40 kolumn, a Twój frontend potrzebuje tylko 5, marnujesz cykle procesora. Ryzykujesz również wyciek prywatnych danych, takich jak klucze API czy koszty.

Rozwiązanie: zwracaj tylko te pola, których potrzebujesz.

render json: @shipments.as_json(only: [:id, :tracking_no, :status])

Dla jeszcze lepszej wydajności użyj pluck, aby pobierać dane jako tablice. Pozwala to całkowicie uniknąć tworzenia ciężkich obiektów ActiveRecord.

  1. Zredukuj alokację obiektów

Każdy obiekt tworzony przez Ruby kosztuje pamięć. Tworzenie tysięcy obiektów podczas pojedynczego żądania zmusza Garbage Collector (GC) do cięższej pracy. To spowalnia cały system.

Unikaj tworzenia nowych hashów lub ciągów znaków (strings) wewnątrz pętli.

Źle:

@shipments.map do |s|
  { label: "#{s.tracking_no} - #{s.status.upcase}" }
end

Dobrze: Przenieś dane statyczne poza pętlę. Wykonuj więcej pracy w bazie danych zamiast w Ruby.

  1. Unikaj powtarzalnych obliczeń

Jeśli wywołujesz tę samą metodę wielokrotnie w ramach jednego żądania, marnujesz czas.

Przykład:

def total_weight
  shipments.sum(&:weight)
end

Jeśli Twój widok (view), helper i serializer wszystkie to wywołują, obliczasz sumę trzy razy.

Rozwiązanie: użyj memoizacji.

def total_weight
  @total_weight ||= shipments.sum(&:weight)
end

Dzięki temu obliczenia zostaną wykonane tylko raz na żądanie.

Tabela podsumowująca:

  • Przeładowana serializacja: Zwracaj tylko potrzebne pola lub użyj pluck.
  • Wysoka alokacja: Twórz mniej obiektów w pętlach.
  • Powtarzalne obliczenia: Użyj memoizacji, aby ponownie wykorzystać wyniki.

Optymalizacja bazy danych polega na proszeniu o mniej danych. Optymalizacja aplikacji polega na wykonywaniu mniejszej ilości zbędnej pracy, gdy masz już dane.

Źródło: https://dev.to/danewu/your-sql-is-fast-but-the-api-is-slow-its-the-ruby-layer-2fno