Trakter & utløysarar
Ein trakt er ein graf. Eit løp er ei rad. Ein venting er eit tidspunkt.
Det er heile modellen, og alle dei vanskelege eigenskapane dette produktet har kjem ut av desse tre setningane. Redigering av ein live trakt er trygg på grunn av dei. Eit nytt forsøk kan ikkje senda dobbelt på grunn av dei. Ein person som melder seg av på tysdag høyrer ingenting på torsdag på grunn av dei.
Kva som faktisk skjer når det køyrer
Å publisere ein trakt skriv ei uforanderleg versjon. Nokon kjem inn, og ei rad dukkar opp: eitt løp, for denne kontakten, festa til den versjonen, med gjeldande node, ei vekketid og sin eigen kontekst.
Ein worker leasar løpa der vekketida har passert, og hevdar dei med SKIP LOCKED slik at to workere ikkje kan ta den same. Den køyrer nodar til den når ein venting eller ein terminal, skriv så neste vekketid og slepp taket.
Ein node er ein rein funksjon av løpet, kontakten og noden — og effektane blir commita i same transaksjonen som løpet si eiga framrykking. Denne eine eigenskapen er det som gjer eit nytt forsøk trygt. Ein worker som blir drepen i halvsekundet mellom «send» og «framrykk» etterlet anten begge eller ingen, aldri berre éin.
Og fordi appen leverer tida i staden for databasen, er ei rad som blir skriven no, forfall no. Ein planleggingskolonne som brukar databaseklokka som standard er ikkje forfallen i augneblikket den blir skriven, og glipper den aller første tikken som skapte den — ein feil som ser ut som ingenting før ei melding er ein time for seint.
- 1Lease
wake_at <= now(),FOR UPDATE SKIP LOCKED. Ei kø utan lease sender dobbelt som standard. - 2KøyrNodar i rekkjefølgje til ei venting eller ein terminal. Kvar skriv eitt steg: kva den bestemte, kva den sende ut.
- 3BekreftEffektar og framrykking, éin transaksjon. Dette er heile trikset.
- 4KvileNeste
wake_atskriven. Ingen nedstraumsrader finst enno, og ingen treng å gjera det.
Éit løp per kontakt per versjon
Fjorten utløysarar
Kva som kan starta eit løp
Nokre av desse er ting du gjer. Dei fleste er ting kunden din gjer, eller ikkje gjer — og fråværet av ei handling er like god ein utløysar som handlinga sjølv.
Dei kjem inn
form_submit, list_join, contact_created, tag_added og segment_enter. Eit segment er eit levande predikat, så «kjem inn i segmentet» dekkjer kvar «har blitt» du kan uttrykkja — tre ordre, nitti dagar i stillheit, brukar over ei grense.
Dei gjer noko
page_visit frå nettstadsnutet, message_opened, link_clicked, booking_made og payment_made. At ei melding blir opna er i seg sjølv ein hending på kontakten, så éin trakt kan venta på ei reaksjon på det siste den sende.
Tid, eller du
date_field med ein forskyving — ein fødselsdag, ei fornying, «eit år sidan», sju dagar før eller tre dagar etter. schedule for den gjentakande. manual for kunngjeringa du sender fordi du bestemte det.
Eiga applikasjonen din
api_event er ei eigendefinert hending du postar frå eigen kode med eigen token. Kva som helst produktet ditt veit, blir ein utløysar: aktivert, inviterte ein kollega, nådde grensa, kansellerte, eksporterte. Dette er den som gjer eit utsendingsverktøy til ein del av produktet ditt.
Gjeninngang, avgjort éin gong
Ein trakt er anten gjeninngangsevne eller ikkje, og databasen handhever det: éit løp per kontakt per trakt med mindre du sa noko anna. Den same utløysaren som fyr to gongar i same sekund kan ikkje tilfeldigvis skapa to løp.
Inngangsvilkår
Ein utløysar fyr, og inngangsspesifikasjonen avgjer om akkurat denne kontakten er kvalifisert — eit segmentpredikat framfor døra. Rimlegare enn ein vilkjursnode på det første steget, og det held grafen fokusert på kva som skjer, ikkje på kven som slepp inn.

Fjorten ting kan starta det. Seksten ting kan skje etterpå.
Seksten handlingar og sju flytnodar
Kva ein trakt kan gjera, og korleis den bestemmer
Send noko
send_email, send_sms, send_push, send_letter — fire kanalar, éin graf — pluss notify_team når personen som må varslast jobbar for deg.
Endra kontakten
add_tag, remove_tag, add_to_list, remove_from_list, set_field. Å merka nokon undervegs er det som gjer segmentet i den neste trakta enkelt.
Nå ut over
http_request til kva du vil, create_payment mot di eiga tilkopla konto, og book_slot mot ein bookingressurs. Betalaren held fram med å vera di; ingen blir vår kunde.
Flytt mellom trakter
enter_funnel og exit_funnel. Bygg éi velkomstsekvens og kall den frå fire stader, i staden for å vedlikehalda fire kopiar som glir frå kvarandre innan mars.
Generer ved sendingstid
ai_generate fyller flettevariablar per kontakt, målt mot kvoten din og bufra etter hash av kreativt og kontaktattributt — elles ville du betalt per mottakar ved kvar einskild sending.
Vent, forgrein, del, samle
wait tek imot ei varigheit, eit datoklokkeslett, ei vekedag og tid, eller eit datofelt med forskyving. condition er eit segmentpredikat med eit ja og eit nei. split er A/B/n med vekter. join samler forgreiningane att.
goal — den som tel
Eit mål avsluttar køyret for alle som når det. Dei booka, dei kjøpte, dei svarte: resten av sekvensen blir avbroten for dei spesifikt. Ingen får «vi saknar deg» dagen etter at dei kom tilbake, og dette er noden som hindrar det.
throttle og quiet_hours
Throttle dempar eit utbrot slik at ei postkasse med låg rategrense ikkje blir hamra ned i suspensjon. Stilletider held ei melding til eit forsvarleg klokkeslett — brukt på meldingsnivå, ikkje kopiert inn i kvar forgreining i grafen.
Kontrollert før den kan køyre
Validatoren nektar det same for deg og for KI-en
Ein graf blir kontrollert før han kan publiserast, og kontrollen er ikkje berre råd. Det er same kodevegen om ein person teikna trakta eller ein modell genererte ho, og det er den einaste grunnen til at genererte trakter i det heile kan stolast på.
Nøyaktig éi inngang
To inngangsnodar er ei tvitydigheit ingen legg merke til før feil halvdel av ei liste får feil sekvens.
Kvar node er nåbar
Ein foreldlaus forgreining er anten ein feil eller ei melding du trur går ut, men som ikkje gjer det. Begge delar er det verdt å bli fortalt om.
Ein sykel må innehalde ein vent
Ei løkke utan vent er ei uendeleg løkke med ei sending i midten. Denne regelen er skilnaden mellom ein trakt og ein hending.
Kvar sending har sitt kreative
Ein send_email-node utan kreativt og emne kan ikkje publiserast. Meldinga med rett emne og tom kropp er ein reell feilmodus; ho blir avvist her.
Kvar variabel er kjend
Flettevariablar blir validerte ved publisering. {{frist_name}} er ein skrivefeil du ser medan du redigerer, ikkje «Hei ,» som kjem i førti tusen innboksar.
Simuler den så
Køyr ein ekte kontakt tørt gjennom heile grafen og les att vegen som blei teken og kvar melding som ville blitt sendt, med dagen sin. Ingenting forlet huset.
Dei vanskelege tilfella
Sju ting som bryt ein naiv motor
Kvar einskild av desse er ein test med ei deterministisk klokke bak, fordi kvar og ein bryt ei implementering som ser fullstendig korrekt ut til ho møter røynda.
- 1Ein trakt som looper inn i seg sjølvLegitimt, vanleg og heilt greitt — så lenge sykelen inneheld ein vent, noko validatoren insisterer på.
- 2Nokon meldar seg av undervegsUndertrykking blir kontrollert ved sending, ikkje ved kølegging. Å kontrollere ved kølegging postar til folk som forlot under ventetida.
- 3Trakta blir redigert med ti tusen folk inniDei held fram på versjonen dei kom inn på, heilt til enden. Nye deltakarar går på den nye.
- 4Same utløysaren fyr to gonger i løpet av eitt sekundEitt løp, fordi unikheit er ein indeks og ikkje ein kontroll etterfulgt av ein innsetjing.
- 5Ein kontakt blir sletta midt i eit løpLøpet blir avslutta. Ei sletting som etterlet ei sekvens som framleis er adressert til nokon, er ikkje ei sletting.
- 6Ein vent spenner over eit sommartidskifte«Tre døger klokka ni om morgonen» betyr ni om morgonen på begge sider av klokkeskiftet.
- 7Ein worker blir drepen mellom send og advanceEffektar og advance er éi transaksjon. Anten skjedde begge deler, eller ingen av dei.
Deterministisk klokkeKvar test bryt ei plausibel implementering
Kampanjar er den andre halvdelen
Ein trakt reagerer. Ein kampanje er ei einskyting: eit publikum, ein timeplan, ein spjerring og ein utsending. Det er noko anna i form, og det blir med vilje haldne atskilt i staden for å late som det er ein trakt med éin node.
To reglar gjer det trygt. Eit publikum blir materialisert éin gong og nektar å køyre to gonger — å løysa det på nytt ville stille leggja til alle som kom til etter at utsendinga starta, og dedupliseringsindeksen ville skjula det ved å hindra duplikata han sjølv skapte. Og tellarane blir rekna ut på nytt frå leveringshendingar, aldri auka på staden, så ein webhook som prøver om att kan ikkje blåsa opp ein rapport.
Kva du skal bruka: skjer det på grunn av noko personen gjorde, er det ein trakt. Skjer det på grunn av noko du bestemde deg for ein tysdag, er det ein kampanje. Forvillande mykje marknadsautomatisering går gale fordi ein tvingar det siste inn i forma til det første.