CodeWorlds
Powrót do kolekcji
Przewodnik17 min czytaniaZespół CodeWorlds

Inngest, zadania w tle sterowane zdarzeniami

Inngest uruchamia funkcje w reakcji na zdarzenia, z krokami, ponawianiem i sterowaniem przepływem. Wersja 4.18.1, licencja SSPL serwera i realny koszt.

Inngest, zadania w tle sterowane zdarzeniami

Inngest to platforma do uruchamiania zadań w tle, w której nie wywołujesz zadania bezpośrednio, tylko wysyłasz zdarzenie, a funkcje same deklarują, na które zdarzenia reagują. Pakiet inngest ma wersję 4.18.1, serwer w repozytorium inngest/inngest około 5,7 tysiąca gwiazdek, a jego licencja to SSPL z odroczonym przejściem na Apache 2.0 i to jest rzecz, którą trzeba sprawdzić przed wdrożeniem.

Co Inngest robi i czego nie robi

Różnica między Inngest a zwykłą kolejką zadań sprowadza się do kierunku zależności. W kolejce kod produkujący pracę musi znać nazwę zadania, które ma zostać wykonane: dodajesz do kolejki wpis send-welcome-email z identyfikatorem użytkownika. W Inngest kod produkujący pracę ogłasza fakt, który właśnie zaszedł, na przykład app/user.signup, i nie wie, ile funkcji na ten fakt zareaguje. Dopisanie drugiej reakcji, powiedzmy wpisu do systemu analitycznego, nie wymaga dotknięcia miejsca, w którym powstało zdarzenie.

To rozróżnienie jest praktyczne, a nie akademickie. Przy modelu zdarzeniowym łatwiej dołożyć funkcję i trudniej wyśledzić, co się w istocie uruchomi po pojedynczym zdarzeniu, bo lista odbiorców nie jest widoczna w miejscu wysyłki. Jeśli w projekcie masz pięć zadań i każde ma dokładnie jednego wywołującego, model zdarzeniowy dokłada warstwę pośrednią bez zysku. Jeśli masz jedno zdarzenie biznesowe i siedem rzeczy, które mają się po nim wydarzyć, ten model zaczyna się opłacać.

Sam serwer składa się z kilku wyodrębnionych usług i warto je znać, bo pojawiają się w logach oraz w konfiguracji hostowania własnego. Event API przyjmuje zdarzenia przez HTTP i uwierzytelnia je kluczem zdarzeń. Strumień zdarzeń buforuje je przed komponentem Runner, który tworzy nowe uruchomienia funkcji, wznawia funkcje wstrzymane przez waitForEvent i anuluje te pasujące do wyrażeń cancelOn. Kolejka jest wielodostępna i to w niej realizowane są mechanizmy sterowania przepływem. Executor wykonuje kroki i zapisuje stan pośredni do magazynu stanu.

Czego Inngest nie robi. Nie jest bazą danych, więc wyniki, które mają przetrwać dłużej niż historia uruchomień, musisz zapisać sam. Nie zastępuje monitorowania błędów aplikacyjnych, od czego jest Sentry. Nie wysyła wiadomości, więc do poczty transakcyjnej i tak potrzebujesz czegoś w rodzaju Resend. Nie hostuje Twojego kodu: funkcje żyją w Twojej aplikacji, a Inngest wywołuje je przez HTTP.

Licencja, trzy źródła i trzy różne odpowiedzi

To jest punkt, dla którego ten tekst powstał. Interfejs programistyczny GitHuba dla repozytorium inngest/inngest zwraca NOASSERTION, czyli wykrywacz nie rozpoznał standardowej licencji. Nie jest to usterka, tylko poprawna odpowiedź, bo plik licencyjny w tym repozytorium nie jest żadną z licencji z listy.

Plik LICENSE.md zaczyna się od dwóch nagłówków: Server Side Public License w wersji 1.0 z 16 października 2018 roku oraz Apache 2.0 Future License. Zasadnicza treść to pełny tekst SSPL, licencji stworzonej przez MongoDB i nieprzyjętej przez Open Source Initiative. Jej sekcja trzynasta, zatytułowana Offering the Program as a Service, mówi wprost: jeśli udostępniasz funkcjonalność programu osobom trzecim jako usługę, musisz udostępnić kod źródłowy całej tej usługi każdemu, bezpłatnie, do pobrania przez sieć i na warunkach tej samej licencji. Definicja udostępniania jako usługi jest w tekście szeroka i obejmuje umożliwienie osobom trzecim zdalnego korzystania z funkcjonalności programu przez sieć komputerową.

Na końcu pliku znajduje się sekcja Grant of Future License. Inngest udziela w niej nieodwołalnie dodatkowej licencji Apache 2.0, skutecznej w trzecią rocznicę dnia, w którym dane wydanie oprogramowania zostało udostępnione. Po tej dacie ta sama wersja kodu jest zwykłym oprogramowaniem na licencji permisywnej. Konstrukcja przypomina licencję Business Source License, choć tekst bazowy jest inny.

Rozbieżność między źródłami jest tu widoczna gołym okiem. Rejestr npm dla paczki inngest-cli w wersji 1.43.0 podaje w polu license wartość SEE LICENSE IN LICENSE.md, czyli deklaruje licencję niestandardową i odsyła do pliku. GitHub mówi NOASSERTION. Plik mówi SSPL z odroczonym Apache 2.0. Wszystkie trzy odpowiedzi są zgodne co do faktu, że nie masz do czynienia ze standardową licencją otwartą, ale żadne narzędzie automatyczne nie powie Ci, co dokładnie z tego wynika.

Osobno wygląda sprawa pakietu klienckiego, i tu rozjazd jest już realny. Paczka inngest w wersji 4.18.1 ma w rejestrze npm pole license ustawione na Apache-2.0. Rozpakowanie opublikowanego archiwum potwierdza to samo: w katalogu głównym paczki leży LICENSE.md z pełnym tekstem licencji Apache w wersji 2.0. Plik packages/inngest/LICENSE.md w repozytorium inngest/inngest-js zawiera dokładnie ten sam tekst. Natomiast interfejs GitHuba dla tego repozytorium raportuje GPL-3.0, mimo że w katalogu głównym repozytorium nie ma żadnego pliku licencyjnego. Jeśli Twoje narzędzie do audytu zależności czyta metadane z GitHuba zamiast z paczek, wpisze Ci do zestawienia licencję copyleft dla biblioteki, która w rzeczywistości jest na Apache 2.0.

Praktyczny wniosek jest prosty do zapamiętania. Biblioteka, którą wciągasz do swojego kodu, jest permisywna i nie stwarza problemu. Serwer, który ewentualnie postawisz u siebie, jest na SSPL i przy odsprzedaży go dalej jako usługi klauzula z sekcji trzynastej zaczyna działać. Do użytku wewnętrznego w firmie SSPL nie stwarza kłopotu, bo nie oferujesz programu osobom trzecim. Do zbudowania na nim własnego produktu typu platforma dla klientów zewnętrznych stwarza kłopot poważny.

Klient, zdarzenia i pierwsza funkcja

Instalacja to jeden pakiet. Wymagany jest Node w wersji co najmniej 20 oraz TypeScript co najmniej 5.8, jeśli korzystasz z typów. Pakiet zależy między innymi od zod w wersji 3.25, a jako zależność równorzędną akceptuje zod w wersji 3.25 albo 4.

TSsrc/inngest/client.ts
TypeScript
// src/inngest/client.ts
import { Inngest, eventType } from 'inngest'

export const userSignup = eventType('app/user.signup').data<{
  userId: string
  email: string
  accountId: string
}>()

export const inngest = new Inngest({
  id: 'sklep-api',
  eventKey: process.env.INNGEST_EVENT_KEY
})

Funkcję tworzy się metodą createFunction, która przyjmuje obiekt konfiguracji i procedurę obsługi. W wersji czwartej pakietu wyzwalacze podaje się w polu triggers, jako tablicę, i to jest zmiana względem wersji trzeciej, gdzie drugim argumentem był pojedynczy obiekt wyzwalacza.

TSsrc/inngest/functions/onboarding.ts
TypeScript
// src/inngest/functions/onboarding.ts
import { NonRetriableError } from 'inngest'
import { inngest, userSignup } from '../client'

export const onboarding = inngest.createFunction(
  {
    id: 'user-onboarding',
    name: 'Powitanie nowego użytkownika',
    triggers: [{ event: 'app/user.signup' }],
    retries: 6
  },
  async ({ event, step }) => {
    const user = await step.run('pobierz-uzytkownika', async () => {
      const record = await db.users.findById(event.data.userId)
      if (!record) throw new NonRetriableError('Brak użytkownika w bazie')
      return record
    })

    await step.run('wyslij-powitanie', () =>
      mailer.send({ to: user.email, template: 'welcome' })
    )

    await step.sleep('odczekaj-trzy-dni', '3d')

    const activated = await step.waitForEvent('czekaj-na-aktywacje', {
      event: 'app/project.created',
      match: 'data.userId',
      timeout: '7d'
    })

    if (!activated) {
      await step.run('wyslij-przypomnienie', () =>
        mailer.send({ to: user.email, template: 'nudge' })
      )
    }

    return { userId: user.id, activated: Boolean(activated) }
  }
)

Funkcje trzeba wystawić pod adresem HTTP, pod który Inngest się dobija. Adaptery są osobnymi punktami wejścia pakietu i jest ich kilkanaście, między innymi inngest/next, inngest/express, inngest/fastify, inngest/hono, inngest/lambda, inngest/cloudflare, inngest/sveltekit i inngest/bun. W projekcie na Next.js wygląda to tak.

TSapp/api/inngest/route.ts
TypeScript
// app/api/inngest/route.ts
import { serve } from 'inngest/next'
import { inngest } from '@/src/inngest/client'
import { onboarding } from '@/src/inngest/functions/onboarding'

export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [onboarding],
  signingKey: process.env.INNGEST_SIGNING_KEY
})

Wysłanie zdarzenia z kodu aplikacji to wywołanie inngest.send. Metoda przyjmuje pojedynczy obiekt albo tablicę, a zwraca identyfikatory przyjętych zdarzeń. Zdarzenie ma nazwę i pole data, opcjonalnie także id służące do odsiewania duplikatów oraz ts z własnym znacznikiem czasu.

Kroki, ponawianie i obsługa błędów

Krok jest jednostką trwałości. Wynik każdego step.run zostaje zapisany po stronie serwera, a przy ponowieniu funkcji kroki już wykonane nie uruchamiają się drugi raz, tylko oddają zapisany wynik. To dlatego procedura obsługi jest wywoływana wielokrotnie w trakcie jednego uruchomienia, a nie raz od początku do końca. Kod pomiędzy krokami wykonuje się przy każdym takim wywołaniu, więc wszystko, co ma efekt uboczny, musi siedzieć wewnątrz kroku.

Dostępne narzędzia kroków to step.run do wykonania kodu, step.sleep i step.sleepUntil do czekania, step.waitForEvent do wstrzymania biegu do czasu nadejścia pasującego zdarzenia, step.invoke do wywołania innej funkcji i odebrania jej wyniku, step.sendEvent do wysłania zdarzenia w sposób trwały oraz step.fetch do zapytań sieciowych. Jest też step.ai.infer, przenoszący wywołanie modelu językowego na stronę serwera Inngest.

Ponawianie jest domyślnie włączone. Pole retries przyjmuje wartość od zera do dwudziestu, a domyślnie wynosi cztery. Dwa typy wyjątków zmieniają zachowanie ponawiania i oba są eksportowane z pakietu głównego.

Code
TypeScript
import { NonRetriableError, RetryAfterError } from 'inngest'

export const syncInvoice = inngest.createFunction(
  {
    id: 'sync-invoice',
    triggers: [{ event: 'billing/invoice.created' }],
    retries: 8,
    onFailure: async ({ error, event }) => {
      await alerts.page('sync-invoice padło do końca', {
        message: error.message,
        eventId: event.data.event.id
      })
    }
  },
  async ({ event, step }) => {
    await step.run('wyslij-do-ksiegowosci', async () => {
      const res = await fetch('https://api.ksiegowosc.example/invoices', {
        method: 'POST',
        body: JSON.stringify(event.data)
      })

      if (res.status === 422) {
        // dane są trwale błędne, ponowienie niczego nie zmieni
        throw new NonRetriableError(`Odrzucone: ${await res.text()}`)
      }

      if (res.status === 429) {
        const after = res.headers.get('retry-after') ?? '60'
        throw new RetryAfterError('Limit dostawcy', `${after}s`)
      }

      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return res.json()
    })
  }
)

NonRetriableError kończy uruchomienie natychmiast, bez zużywania pozostałych prób. RetryAfterError przesuwa kolejną próbę o podany czas, co jest właściwą reakcją na odpowiedź 429 z nagłówkiem Retry-After. Procedura onFailure odpala się dopiero po wyczerpaniu wszystkich prób i to w niej należy umieścić zgłoszenie do człowieka.

Anulowanie działa przez cancelOn. Podajesz nazwę zdarzenia anulującego oraz pole match z notacją kropkową, po którym oba zdarzenia mają się zgadzać, albo pełne wyrażenie w polu if. Osobno działa pole timeouts z podpolami start i finish: pierwsze ogranicza czas oczekiwania w kolejce przed startem, drugie łączny czas trwania uruchomienia.

Sterowanie przepływem, czyli to, po co się tu przychodzi

Sterowanie przepływem jest tym, co odróżnia Inngest od prostszych narzędzi w tej kategorii. Wszystkie poniższe opcje są polami obiektu konfiguracji funkcji i wszystkie przyjmują opcjonalny klucz, będący wyrażeniem w języku CEL obliczanym dla każdego zdarzenia wyzwalającego. Klucz jest tu najważniejszy, bo zamienia jeden globalny limit w osobny limit dla każdej wartości klucza.

Code
TypeScript
export const generateReport = inngest.createFunction(
  {
    id: 'generate-report',
    triggers: [{ event: 'reports/requested' }],

    // najwyżej 5 równoległych uruchomień na każdego klienta
    concurrency: [
      { limit: 5, key: 'event.data.customerId', scope: 'fn' },
      { limit: 50, scope: 'env' }
    ],

    // 100 nowych uruchomień na minutę, z zapasem 20 na skok ruchu
    throttle: { limit: 100, period: '1m', burst: 20 },

    // scal serię szybkich zmian tego samego raportu w jedno uruchomienie
    debounce: { period: '30s', key: 'event.data.reportId', timeout: '10m' },

    // klienci premium przed pozostałymi
    priority: { run: 'event.data.plan == "pro" ? 300 : 0' },

    // to samo zdarzenie w ciągu 24 godzin uruchomi funkcję raz
    idempotency: 'event.data.reportId'
  },
  async ({ event, step }) => {
    /* ... */
  }
)

Trzy mechanizmy bywają mylone, więc rozdzielmy je wprost. concurrency ogranicza liczbę uruchomień działających jednocześnie i przyjmuje pole scope o wartościach fn, env albo account, przy czym dwie ostatnie wymagają podania klucza. throttle ogranicza liczbę uruchomień rozpoczętych w danym okresie, nadmiar czeka w kolejce, a pole burst dodaje zapas ponad limit na wypadek nierównego ruchu. rateLimit wygląda podobnie, ale zachowuje się inaczej: nadmiarowe zdarzenia są odrzucane, a nie kolejkowane, więc używa się go do wygaszania szumu, nie do wygładzania obciążenia.

Do tego dochodzą dwa mechanizmy scalające. debounce odkłada start o zadany okres i przy każdym kolejnym pasującym zdarzeniu odlicza od nowa, uruchamiając funkcję z ostatnim zdarzeniem. Okres mieści się w przedziale od jednej sekundy do siedmiu dni, a pole timeout wyznacza twardą granicę odkładania. batchEvents zbiera zdarzenia w paczkę i podaje je funkcji jako tablicę events, z polem maxSize ograniczonym do stu i polem timeout w zakresie od jednej do sześćdziesięciu sekund.

Jest jeszcze singleton z polem mode przyjmującym skip albo cancel. Pierwszy tryb pomija nowe uruchomienie, jeśli dla tego samego klucza jedno już trwa. Drugi anuluje trwające i uruchamia nowe. To najprostszy sposób na zablokowanie sytuacji, w której dwa zadania synchronizacji tego samego zasobu deptają sobie po palcach.

Samodzielny hosting i cennik chmury

Hostowanie własne jest wspierane od wydania 1.0 serwera, a bieżące wydanie to 1.43.0 z 20 sierpnia 2026 roku. Dokumentacja mówi wprost, że zespół wsparcia Inngest nie gwarantuje bezpośredniej pomocy dla instancji hostowanych samodzielnie i odsyła do zgłoszeń na GitHubie albo do rozmowy o wariancie korporacyjnym. To informacja, którą trzeba przyjąć na wejściu, a nie odkryć w czasie awarii.

Serwer startuje jednym poleceniem i domyślnie nie ma zależności zewnętrznych. Trzyma stan w SQLite w pliku ./.inngest/main.db i uruchamia wbudowany serwer Redis w tym samym procesie, na potrzeby kolejki i stanu uruchomień, z okresowymi zrzutami do SQLite. Taki układ nie skaluje się poza jeden węzeł, więc do produkcji podłącza się usługi zewnętrzne.

docker-compose.yml
YAML
# docker-compose.yml
services:
  inngest:
    image: inngest/inngest
    command: inngest start
    ports:
      - '8288:8288'
      - '8289:8289'
    environment:
      # oba klucze muszą być ciągami szesnastkowymi o parzystej długości
      - INNGEST_EVENT_KEY=abcd1234
      - INNGEST_SIGNING_KEY=5678ef90
      - INNGEST_POSTGRES_URI=postgres://inngest:haslo@postgres:5432/inngest
      - INNGEST_REDIS_URI=redis://redis:6379
      - INNGEST_EXPERIMENTAL_PROM_METRICS=1
    depends_on:
      - postgres
      - redis

Port 8288 obsługuje serwer i jego interfejs, port 8289 to brama Connect dla stale podłączonych procesów roboczych. Każdą opcję wiersza poleceń da się podać zmienną środowiskową: zamieniasz myślniki na podkreślenia, zapisujesz wielkimi literami i dodajesz przedrostek INNGEST_, więc --postgres-uri staje się INNGEST_POSTGRES_URI. Serwer wystawia też punkt GET /metrics w formacie Prometheusa, domyślnie z jednym wskaźnikiem inngest_queue_depth, a liczniki cyklu życia funkcji włącza wspomniana flaga eksperymentalna. Uwierzytelnienie tego punktu nie jest domyślnie skonfigurowane, więc nie wystawiaj go publicznie.

Aplikacja łączy się z własnym serwerem przez zmienne INNGEST_DEV=0 oraz INNGEST_BASE_URL wskazujący adres instancji, obok kluczy zdarzeń i podpisu. Lokalnie do pracy służy npx inngest-cli dev, który podnosi serwer deweloperski z interfejsem i śladami wykonania.

W wariancie hostowanym rozliczaną jednostką jest execution, definiowana jako pojedyncze uruchomienie funkcji plus każdy krok w nim zawarty. Funkcja z pięcioma wywołaniami step.run zużywa sześć jednostek na jedno uruchomienie i ten mnożnik jest tu najważniejszą liczbą do policzenia we własnym projekcie. Plan Hobby kosztuje zero i obejmuje 50 tysięcy jednostek miesięcznie, 5 równoległych kroków, 3 użytkowników, 500 tysięcy przyjętych zdarzeń, zdarzenie do 256 KiB, paczkę do 5 zdarzeń oraz 24 godziny historii śladów. Po wyczerpaniu darmowego limitu wykonanie zostaje wstrzymane, a nie przełączone na rozliczanie miarowe. Plan Pro startuje od 99 dolarów miesięcznie z milionem jednostek w cenie, rozliczaniem miarowym do dwudziestu milionów, stoma równoległymi krokami i dopłatą 25 dolarów za każde kolejne 25, piętnastoma użytkownikami i dopłatą 10 dolarów za użytkownika oraz siedmioma dniami historii śladów. Integracja z zewnętrznymi systemami obserwowalności jest w cenniku pozycją za 300 dolarów, a zgodność z HIPAA dodatkiem do uzgodnienia. Wariant Enterprise nie ma podanej ceny.

Inngest a alternatywy

KryteriumInngesttrigger.devTemporal
Sposób wyzwalaniazdarzenie, wielu odbiorcówbezpośrednie wywołanie zadaniauruchomienie przepływu przez klienta
Licencja rdzeniaSSPL z odroczonym Apache 2.0rozjazd między repozytorium a paczkąMIT
Sterowanie przepływemthrottle, debounce, concurrency, singleton, prioritykolejki i limity współbieżnościpolityki po stronie kolejki zadań
Gdzie działa kodw Twojej aplikacji, wywołanie przez HTTPna infrastrukturze dostawcy albo własnejw Twoim procesie roboczym
Hosting własnywspierany, bez gwarancji wsparciawspieranywspierany, wymaga bazy danych
Plan darmowy50 tysięcy jednostek, potem wstrzymanielimit czasu wykonania i zadańtylko usługa Temporal Cloud jest płatna

Trigger.dev jest najbliższym odpowiednikiem i różnica sprowadza się do modelu wyzwalania oraz do tego, gdzie fizycznie wykonuje się kod. Tam zadanie wywołujesz po nazwie i uruchamia się ono na infrastrukturze dostawcy, tu wysyłasz zdarzenie, a Twój własny serwer dostaje żądanie HTTP. Jeśli zadanie ma trwać godzinę i zjadać pamięć, model Inngest jest gorszy, bo obciąża Twoją aplikację. Jeśli chcesz mieć kod zadań w tym samym repozytorium i tym samym procesie co reszta aplikacji, jest lepszy.

Temporal gra w innej lidze pod względem gwarancji i pod względem kosztu wdrożenia. Daje pełne trwałe wykonanie z historią zdarzeń i deterministycznym odtwarzaniem, ale wymaga uruchomienia procesów roboczych, utrzymania klastra i przyjęcia modelu programowania, w którym kod przepływu musi być deterministyczny. Do wysłania powitalnego maila po rejestracji to narzędzie za ciężkie.

Osobno warto rozważyć samą platformę wdrożeniową. Funkcje serwerowe na Vercel mają limity czasu wykonania, a Inngest omija je przez podział pracy na kroki: każdy krok jest osobnym żądaniem HTTP, więc dziesięciominutowy proces składa się z wielu krótkich wywołań. To sensowne obejście, ale oznacza też, że narzut sieciowy rośnie liniowo z liczbą kroków.

Sąsiednią, choć węższą kategorię obsługuje Hookdeck: nie uruchamia Twoich funkcji, tylko przyjmuje webhooki od cudzych systemów, kolejkuje je, ponawia i pozwala podejrzeć ładunek przy diagnozowaniu. Bywa sensownym uzupełnieniem, bo problem gubionych zdarzeń przychodzących jest inny niż problem trwałego wykonania. Dwie rzeczy trzeba wiedzieć przed wpięciem: biblioteka dla TypeScriptu stoi na wersji 0.4.0 z sierpnia 2024 roku i nie ma pola licencji w rejestrze, klienta dla Pythona nie ma wcale, a na stronie cennika ta sama pozycja rozliczeniowa występuje w trzech różnych stawkach.

Typowe błędy

Efekt uboczny poza krokiem. Kod pomiędzy wywołaniami step.run wykonuje się przy każdym żądaniu w ramach jednego uruchomienia. Zapis do bazy umieszczony tam wykona się tyle razy, ile kroków ma funkcja. Wszystko, co zmienia stan świata, należy do wnętrza kroku.

Zmiana identyfikatora kroku przy działających uruchomieniach. Identyfikator, czyli pierwszy argument step.run, jest kluczem, po którym odnajdywany jest zapisany wynik. Przemianowanie go w trakcie wdrożenia sprawia, że trwające uruchomienia nie znajdą swojego stanu i wykonają krok ponownie.

Mylenie throttle z rateLimit. Pierwszy kolejkuje nadmiar, drugi go wyrzuca. Ustawienie rateLimit tam, gdzie chodziło o wygładzenie ruchu do zewnętrznego API, kończy się cicho gubionymi zdarzeniami.

Limit współbieżności bez klucza. concurrency: { limit: 5 } to pięć uruchomień w ogóle, nie pięć na klienta. Bez pola key jeden duży klient zablokuje kolejkę wszystkim pozostałym.

Liczenie kosztu po uruchomieniach zamiast po jednostkach. Funkcja rozbita na dwanaście drobnych kroków kosztuje trzynaście jednostek za jedno uruchomienie. Nadmierne dzielenie pracy na kroki jest tu wprost płatne.

Założenie, że skoro projekt jest otwarty, to licencja jest permisywna. Serwer stoi na SSPL i przy budowaniu na nim własnej usługi dla klientów zewnętrznych trzeba przeczytać sekcję trzynastą, zanim zapadnie decyzja.

FAQ

Czy Inngest jest oprogramowaniem otwartym?

Kod jest publiczny, ale licencja serwera nie jest zatwierdzona przez Open Source Initiative. Plik LICENSE.md w repozytorium inngest/inngest to Server Side Public License 1.0 z dodatkową, nieodwołalną licencją Apache 2.0 wchodzącą w życie w trzecią rocznicę udostępnienia danego wydania. Biblioteka kliencka inngest z npm jest natomiast na Apache 2.0 i to potwierdzają zarówno metadane rejestru, jak i plik w opublikowanej paczce.

Czym różni się throttle od rateLimit i concurrency?

throttle ogranicza liczbę uruchomień startujących w danym okresie i kolejkuje nadmiar, opcjonalnie z zapasem w polu burst. rateLimit w tej samej sytuacji nadmiarowe zdarzenia odrzuca. concurrency w ogóle nie patrzy na czas, tylko na liczbę uruchomień działających jednocześnie, i przyjmuje zasięg fn, env albo account.

Czy da się uruchomić Inngest bez konta w chmurze?

Tak. Obraz inngest/inngest z poleceniem inngest start podnosi pełny serwer, domyślnie na SQLite z wbudowanym Redisem w tym samym procesie. Do produkcji podłącza się zewnętrzny Postgres flagą --postgres-uri i zewnętrzny Redis flagą --redis-uri. Dokumentacja zaznacza, że wsparcie techniczne dla takich instancji nie jest gwarantowane.

Jak liczony jest koszt w planie płatnym?

Jednostką rozliczeniową jest execution, czyli jedno uruchomienie funkcji plus każdy krok w nim wykonany. Funkcja z pięcioma krokami zużywa sześć jednostek. Plan Hobby daje 50 tysięcy jednostek miesięcznie i po ich wyczerpaniu wstrzymuje wykonanie, plan Pro startuje od 99 dolarów z milionem jednostek w cenie i rozliczaniem miarowym powyżej.

Co się stanie, gdy funkcja padnie w połowie?

Kroki już wykonane mają zapisane wyniki i przy ponowieniu nie uruchomią się drugi raz. Domyślnie funkcja ma cztery próby, a pole retries przyjmuje wartości od zera do dwudziestu. Rzucenie NonRetriableError kończy uruchomienie natychmiast, RetryAfterError przesuwa kolejną próbę o podany czas, a po wyczerpaniu wszystkich prób wywoływana jest procedura onFailure.

Czy mogę uruchomić funkcję bez wysyłania zdarzenia?

Tak, służy do tego step.invoke, które wywołuje inną funkcję i czeka na jej wynik, oraz odwołania tworzone przez referenceFunction, pozwalające wskazać funkcję żyjącą w innej aplikacji. Model zdarzeniowy pozostaje domyślny, ale bezpośrednie wywołanie jest dostępne tam, gdzie potrzebujesz wartości zwracanej.

Dokumentację znajdziesz na stronie Inngest, cennik na stronie z planami, a kod serwera w repozytorium na GitHubie.

Czytaj dalej

Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie