Trakter & utløsere
En trakt er en graf. En kjøring er en rad. En venting er et tidspunkt.
Det er hele modellen, og alle de vanskelige egenskapene dette produktet har kommer ut av de tre setningene. Det er takket være dem at redigering av en live trakt er trygt. Et nytt forsøk kan ikke sende dobbelt på grunn av dem. En person som melder seg av tirsdagen hører ingenting torsdagen, på grunn av dem.
Hva som faktisk skjer når den kjører
Å publisere en trakt skriver en uforanderlig versjon. Noen kommer inn, og en rad dukker opp: én kjøring, for denne kontakten, festet til den versjonen, med gjeldende node, et oppvåkningstidspunkt og sin egen kontekst.
En worker leier kjøringene der oppvåkningstidspunktet har passert, og gjør krav på dem med SKIP LOCKED slik at to workere ikke kan ta samme. Den utfører noder til den når en venting eller en terminal, skriver så neste oppvåkningstidspunkt og slipper taket.
En node er en ren funksjon av kjøringen, kontakten og noden — og effektene committes i samme transaksjon som kjøringens eget fremskritt. Denne ene egenskapen er det som gjør et nytt forsøk trygt. En worker som drepes i halvsekundet mellom «send» og «avanser» etterlater enten begge eller ingen, aldri én.
Og fordi appen leverer tiden i stedet for databasen, er en rad som skrives nå forfallent nå. En planleggingskolonne som bruker databaseklokken som standard er ikke forfallent i øyeblikket den skrives, og går glipp av nettopp den tikken som skapte den — en feil som ser ut som ingenting før en melding er en time for sent.
- 1Leie
wake_at <= now(),FOR UPDATE SKIP LOCKED. En kø uten leieavtaler sender dobbelt som standard. - 2UtførNoder i rekkefølge til en venting eller en terminal. Hver skriver et steg: hva den bestemte, hva den sendte ut.
- 3ForpliktEffekter og fremskritt, én transaksjon. Det er hele trikset.
- 4SøvnNeste
wake_atskrevet. Ingen nedstrømsrader finnes ennå, og ingen trenger det.
Én kjøring per kontakt per versjon
Fjorten utløsere
Hva som kan starte en kjøring
Noen av disse er ting du gjør. De fleste er ting kunden din gjør, eller ikke gjør — og fraværet av en handling er like god en utløser som selve handlingen.
De ankommer
form_submit, list_join, contact_created, tag_added og segment_enter. Et segment er et levende predikat, så «blir med i segmentet» dekker alt du kan uttrykke med «har blitt» — tre ordrer, nitti dager stillhet, bruk over en terskel.
De gjør noe
page_visit fra nettstedskodet, message_opened, link_clicked, booking_made og payment_made. At en melding åpnes er i seg selv en hendelse på kontakten, så én trakt kan vente på en reaksjon på det siste den sendte.
Tid, eller du
date_field med et avslag — en bursdag, en fornyelse, «ett år siden», sju dager før eller tre dager etter. schedule for den gjentakende. manual for kunngjøringen du sender fordi du bestemte det.
Din egen applikasjon
api_event er en tilpasset hendelse du poster fra din egen kode med din egen token. Alt produktet ditt vet blir en utløser: aktivert, inviterte en kollega, nådde grensen, kansellerte, eksporterte. Dette er den som gjør et utsendelsesverktøy til en del av produktet ditt.
Gjeninngang, bestemt én gang
En trakt er enten gjeninngangsklar eller ikke, og databasen håndhever det: én kjøring per kontakt per trakt med mindre du har sagt noe annet. Samme utløser som fyrer to ganger i samme sekund kan ikke ved et uhell gi to kjøringer.
Inngangsvilkår
En utløser fyrer, og inngangsspesifikasjonen bestemmer om akkurat denne kontakten er kvalifisert — et segmentpredikat foran døren. Rimeligere enn en betingelsesnode på første steg, og det holder grafen om hva som skjer, i stedet for hvem som slipper inn.

Fjorten ting kan starte den. Seksten ting kan skje videre.
Seksten handlinger og sju flytnoder
Hva en trakt kan gjøre, og hvordan den bestemmer
Send noe
send_email, send_sms, send_push, send_letter — fire kanaler, én graf — pluss notify_team når personen som må varsles jobber for deg.
Endre kontakten
add_tag, remove_tag, add_to_list, remove_from_list, set_field. Å merke noen underveis er det som gjør neste trakts segment enkelt.
Nå utenfor
http_request til hva du vil, create_payment mot din egen tilkoblede konto, og book_slot mot en bookingsressurs. Betaleren forblir din; ingen blir vår kunde.
Beveg deg mellom trakter
enter_funnel og exit_funnel. Bygg én velkomstsekvens og kall den fra fire steder, i stedet for å vedlikeholde fire kopier som glir fra hverandre innen mars.
Genereres ved utsending
ai_generate fyller merge-variabler per kontakt, målt mot kvoten din og bufret etter kreativ og kontaktattributt-hash — ellers ville du betalt per mottaker ved hver eneste utsending.
Vent, forgren, del, sammenføy
wait tar en varighet, et dato-klokkeslett, en ukedag med klokkeslett, eller et datofelt med forskyvning. condition er et segmentpredikat med et ja og et nei. split er A/B/n med vekter. join samler grenene igjen.
goal — den som teller
Et mål avslutter kjøringen for alle som når det. De booket, de kjøpte, de svarte: resten av sekvensen kanselleres for dem spesifikt. Ingen får «vi savner deg» dagen etter at de kom tilbake, og dette er noden som forhindrer det.
throttle og quiet_hours
Throttle demper en strøm slik at en postboks med lav hastighetsgrense ikke hamres inn i suspensjon. Stille timer holder en melding til et sivilisert tidspunkt — brukes på meldingsnivå, ikke kopiert inn i hver gren av grafen.
Kontrollert før den kan kjøre
Validatoren nekter de samme tingene for deg og for AI-en
En graf kontrolleres før den kan publiseres, og kontrollen er ikke veiledende. Det er samme kodebane enten en person tegnet trakteren eller en modul genererte den, og det er den eneste grunnen til at genererte trakter er til å stole på i det hele tatt.
Nøyaktig én inngang
To inngangsnoder er en tvetydighet ingen legger merke til før feil halvdel av en liste får feil sekvens.
Alle noder er nåbare
En foreldreløs gren er enten en feil eller en melding du tror går ut, men som ikke gjør det. Begge deler er verdt å få beskjed om.
En syklus må inneholde en wait
En løkke uten wait i seg er en uendelig løkke med en utsending i midten. Denne regelen er forskjellen mellom en trakt og en hendelse.
Hver utsending har sitt kreative
En send_email-node uten kreativ og emne kan ikke publiseres. Meldingen med riktig emne og tom kropp er en reell feilmodus; den nektes her.
Alle variabler er kjent
Merge-variabler valideres ved publisering. {{frist_name}} er en skrivefeil du ser mens du redigerer, ikke «Hei ,» som lander i førti tusen innbokser.
Simuler den deretter
Kjør en prøve med en ekte kontakt gjennom hele grafen og les tilbake banen som ble tatt og hver melding som ville blitt sendt, med sin dag. Ingenting forlater bygningen.
De vanskelige tilfellene
Sju ting som knekker en naiv motor
Hver av disse er en test med en deterministisk klokke bak seg, fordi hver av dem knekker en implementasjon som ser helt korrekt ut til den møter virkeligheten.
- 1En trakt som looper inn i seg selvLegitimt, vanlig og greit — forutsatt at syklusen inneholder en wait, noe validatoren insisterer på.
- 2Noen melder seg av midt i kjøringenUndertrykking kontrolleres ved utsending, ikke ved kølegging. Å kontrollere ved kølegging poster til folk som forlot under ventetiden.
- 3Trakteren redigeres med ti tusen personer inneDe fortsetter å gå versjonen de kom inn på, til slutten. Nye deltakere går den nye.
- 4Samme utløser fyres to ganger på ett sekundÉn kjøring, fordi unikhet er en indeks snarere enn en kontroll etterfulgt av en innsetting.
- 5En kontakt slettes midt i kjøringenKjøretiden tar slutt. En sletting som lar en sekvens fortsatt være adressert til noen, er ikke en sletting.
- 6En ventetid strekker seg over en endring av sommertid«Tre dager klokka ni om morgenen» betyr ni om morgenen på begge sider av klokkeskiftet.
- 7En worker blir drept mellom sending og avanseringEffekter og avansering er én transaksjon. Enten skjedde begge, eller ingen av dem.
Deterministisk klokkeHver test knekker en plausibel implementering
Kampanjer er den andre halvdelen
En trakt reagerer. En kampanje er utsendelsen som skjer én gang: et publikum, en tidsplan, en struping og en sending. Det er en annen type ting, og den holdes bevisst adskilt i stedet for å late som om den er en trakt med én node.
To regler gjør det trygt. Et publikum materialiseres én gang og nekter å kjøre to ganger — å løse det på nytt ville i stillhet lagt til alle som har blitt med siden utsendelsen startet, og dedupliseringsindeksen ville skjult det ved å hindre duplikatene den selv skapte. Og tellere beregnes på nytt fra leveringshendelser, aldri økes på stedet, så en webhook som prøvesendes på nytt kan ikke blåse opp en rapport.
Hva du skal bruke: skjer det på grunn av noe personen gjorde, er det en trakt. Skjer det på grunn av noe du bestemte deg for en tirsdag, er det en kampanje. Forbløffende mye markedsføringsautomatisering går galt ved å tvinge det siste inn i formen til det første.