---
layout: 'page'
uri: '/framework/fire-and-forget'
position: 20
slug: 'framework-fire-and-forget'
parent: 'framework-background'
navTitle: 'Fire-and-forget'
title: 'Fire-and-forget'
description: 'Fire-and-forget tvar durable enginu — krátká background práce, která se zařadí do fronty v transakci commandu (outbox), běží MIMO transakci a doručuje se at-least-once (handler musí být idempotentní). Fire-and-forget run je run bez checkpointu.'
---

# Fire-and-forget

**Fire-and-forget run** je krátký, „udělej a zapomeň" tvar [durable enginu](/framework/durable-run): práce,
která musí proběhnout i po pádu procesu, ale **nepotřebuje si pamatovat postup** (žádný
checkpoint). Hodí se na **volání ven** (e-mail/SMTP, webhook, jedno volání cizího API) i na
rychlý přepočet nad vlastní DB. Fire-and-forget run se zapíše do tabulky `runs` **uvnitř transakce commandu**
(outbox), worker si ho atomicky převezme a spustí **mimo transakci**. Selhání se přeplánuje
s backoffem, po vyčerpání pokusů run skončí terminálně.

> ℹ️ Fire-and-forget run a [durable run](/framework/durable-run) jsou **jeden engine** (tabulka `runs`, jeden worker,
> stejné lease/heartbeat). Liší se jen registrací: fire-and-forget run = `FireAndForget` (bez checkpointu),
> durable run = `Durable` (checkpoint + resume). Fire-and-forget run je run bez deníku. Kdy co → [Background work](/framework/overview).

> ⚠️ **Pozor:** handler běží **MIMO transakci** (jako každý run) — i krátké volání ven v
> transakci by drželo SQLite write-lock po dobu volání (SMTP visí, API i minuty) → zamrzne
> celá DB. Atomicitu „práce + hotovo" tu nahrazuje **idempotence**: dokončení se zapisuje
> **samostatně až po návratu handleru**, takže crash mezi tím handler na reclaimu zopakuje.

> Přehled toku. Návod (run kind, handler, retry policy) → `/gk-runs`; periodické úlohy → `/gk-scheduler`.


## K čemu to je

Když práce **musí přežít restart i pád procesu**, nesmí blokovat request, ale **nepotřebuje
resumovat** dosavadní postup (na to je [durable run](/framework/durable-run)). Zápis do fronty uvnitř
transakce commandu (outbox pattern) dá záruku: run se neztratí (commit ho zviditelní) ani
neosiří (rollback ho vrátí zpět). Cenou je doručení **at-least-once** — handler musí být
idempotentní, a to **včetně svých DB zápisů** (mimo transakci už je žádný in-tx rollback
neochrání).


## Jak to teče

1. **Enqueue** — handler volá `RunDispatcher.Enqueue(kind, maxRetries, payload)`; INSERT
   proběhne přes `r.Conn(ctx)`, takže se připojí ke **stejné transakci** jako business zápis.
2. **Commit** → run je viditelný pro worker. **Rollback** → žádný osiřelý run.
3. **Claim** — worker se každou chvíli ptá fronty a atomicky si převezme (claim) run, který
   je na řadě, jediným `UPDATE … RETURNING` (nastaví lease; `reclaims++` jen při převzetí
   propadlé lease — `attempts` claim nikdy nezvedá, ty rostou jen při `Reschedule`). Dva
   workery nedostanou stejný řádek.
4. **Execution** — handler dostane payload a běží **MIMO transakci** (worker mu ctx označí
   `ContextForbidTx`, takže `BeginTx` fail-closed selže). Při úspěchu worker zapíše
   `MarkComplete` **samostatným zápisem** po návratu handleru.
5. **Retry** — chyba nebo panika → `attempts < maxRetries` → `Reschedule` s exponenciálním
   backoffem; jinak `MarkFailed` + **report do Sentry**.

Stav se odvozuje ze sloupců (`completed_at` / `failed_at` / `cancelled_at` / `locked_until`), žádný `status`
enum tu není.


## Příklad

```go
// registrace (provideRunHandlerRegistry, DI) — fire-and-forget run = FireAndForget, bez checkpointu:
"welcome:send": runapp.FireAndForget(h.SendWelcome, 30*time.Second),

// z command (nebo event) handleru:
shared.RunDispatcherFromContext(ctx).Enqueue(ctx, "welcome:send", maxRetries, payload)
```

Neznámý `kind` (bez registrovaného handleru) i `maxRetries < 0` selžou už při zařazení do
fronty. Mimo bus (testy volající handler napřímo) je dispatcher no-op, takže volání je vždy bezpečné; CLI příkazy jdou přes SystemCommandBus, jehož chain dispatcher nese, takže i odtud se run durable zařadí.


## Související

- [Durable run](/framework/durable-run) — durable tvar téhož enginu (checkpoint + resume po pádu).
- [Command](/framework/command) — transakce, ke které se zařazení do fronty připojí.
- [Events](/framework/events) — odkud se často zařazuje navazující práce.
- Skilly: `/gk-runs`, `/gk-scheduler`, `/gk-di`.

---

[← Overview](/framework/overview.md) | [Durable run →](/framework/durable-run.md)