Når du spørger enhver live-TV-operatør, hvad de gerne vil se i et planlægningsværktøj, siger de næsten uden undtagelse: "Lad mig bygge en kanal, som jeg organiserer filer i en mappe. Træk et program ind, slip det, hvor jeg vil have det, færdigt.
Problemet er, at en kanalplan ikke er en mappe. Det er et juridisk dokument med hårde begrænsninger. To programmer kan ikke afspilles samtidigt. Encoderen kan ikke skifte inputs hurtigere end hvert femte sekund. Et program, der allerede er startet, kan ikke redigeres. Og en film, der løber efter midnat, skal behandles som ét program, ikke opdelt ved dagsgrænsen.
Hver træk-og-slip-handling er en potentiel overtrædelse af begrænsninger, der venter på at ske. Dette er ingeniørhistorien om, hvordan mStudios scheduler omdanner operatørvenlig træk-og-slip til en garanteret lovlig kanal-tidslinje på AWS MediaLive – inklusive det øjeblik en operatør slipper et program direkte oven på et andet – og hvorfor systemet automatisk løser disse konflikter i stedet for at give operatøren en fejl og et puslespil at løse.
Hurtigt Overblik
| Aspekt | Detalje |
|---|---|
| Domæne | Træk-og-slip-planlægning for live FAST-kanaler på AWS MediaLive |
| Konfliktløsning | Auto-forskydning via en 4-regels snap-algoritme |
| Minimumsnaboafstand | 6 sekunder (MediaLives 5s minimum + 1s sikkerhed for clock-drift) |
| Kaskadehåndtering | Originale tider memoiseres ved første berøring, så kædede forskydninger producerer ét rent oprydningskald pr. program |
| Sikkerhed mod fortidige datoer | To-faset sikring – inputafvisning plus lydløs post-snap tilbageførsel |
| Sikkerhed ved nulstillingsdag | 2-minutters live-afspilningsbuffer plus bevarelse af overførte programmer |
| Status | I produktion |
Forretningsproblemet: En kalender, der ikke er en kalender
Kanalplanlægning ligner kalendersoftware. Operatører forventer, at det opfører sig som sådan – træk en film ind i et 21:00-slot, skub programmer op ad tidslinjen, tilføj en serie i batch og se episoderne lægge sig ud efter hinanden. Men en live-kanalplan indeholder begrænsninger, som en kalender simpelthen ikke har:
- Programmer må ikke overlappe. Live-TV afspiller præcis én ting ad gangen.
- Encoderen har en minimumsaktionafstand. AWS MediaLive skifter ikke inputs hurtigere end hvert 5. sekund. Planlæg to programmer med 4 sekunders mellemrum, og deployment afvises.
- Tidligere programmer kan ikke redigeres. Sendetiden er allerede passeret; bits er allerede på seernes skærme.
- Programmer, der krydser midnat, er én enhed. En film, der kører fra 23:30 til 01:15, skal håndteres som et enkelt program, ikke to halve programmer opdelt ved dagsgrænsen.
Et "næsten overlap" på to sekunder er ikke en operatørfejl – det er det naturlige resultat af at trække to programmer, der næsten, men ikke helt, passer sammen. Den virkelige ingeniørudfordring er at oversætte "træk en film til 21:00" til "en lovlig kanalplan". Gør man det forkert, rammer operatøren enten en fejlbarriere ved hvert drop, eller – værre – opdager ved sendetidspunktet, at encoderen lydløst har afvist en del af planen.
Hvad "Lovlig" betyder på AWS MediaLive
For at en plan skal være lovlig på MediaLive skal tilstødende programmer enten være:
- Uafbrudt — nul mellemrum mellem dem, eller
- Adskilt af mindst 5 sekunder — encoderens minimumsaktionafstand.
Fælden er alt derimellem. Et 1-sekunds mellemrum, et 3-sekunders mellemrum, et 4,9-sekunders mellemrum – alle ser de helt fine ud i en UI, og alle afvises de ved deployment. Værre endnu, afvisningen er ikke en ren, atomisk fejl; den kan resultere i en delvist deployeret kanal, hvor nogle planlagte handlinger landede på MediaLive, og andre ikke gjorde.
Tilføj en 1-sekunds sikkerhedsmargin for clock drift mellem applikationsservere og AWS, og det praktiske minimum bliver 6 sekunder, ikke 5. Dette ene tal — MIN_NEIGHBOUR_GAP_MS = 6000 — er den ene konstant, hele konfliktløsningssystemet er bygget omkring.
Hvorfor de oplagte tilgange fejler
Før man besluttede sig for automatisk løsning, blev flere mere oplagte strategier overvejet og forkastet:
"Afvis enhver uoverensstemmelse og bed operatøren om at løse den." På grund af dette skal operatører manuelt udføre snap-matematik ved hvert drop. Et træk på fem sekunder bliver til et fem-minutters puslespil, og puslespillet bliver sværere, efterhånden som afspilningslisten vokser. I praksis opgiver operatører helt træk-og-slip og falder tilbage til regneark.
"Snap alt til 5-minutters grænser, så intet nogensinde overlapper." Dette løser det tekniske problem ved at ødelægge operatørens hensigt. Et program, der skulle starte kl. 21:03:15, bør ikke lydløst springe til 21:05:00. Planen tilhører operatøren, ikke en afrundingsfunktion.
"Registrer konflikter ved deployment-tidspunktet i stedet for drop-tidspunktet." Dette føles hurtigere i UI'en, men det flytter fejlen til det værst tænkelige tidspunkt. På det tidspunkt, hvor operatøren klikker på Deploy og ser "plan afvist ved program 47", har de mentalt bevæget sig videre fra den redigering, der forårsagede det.
"Tillad mikro-mellemrum i UI'en og lad MediaLive afvise dem." Dette skubber uigennemsigtige encoder-fejl direkte tilbage til operatøren og kan efterlade kanalen i en halv-deployeret tilstand, der er virkelig svær at genoprette fra.
Det, der faktisk virkede, var at løse konflikter automatisk, ved drop-tidspunktet, ved hjælp af deterministiske regler – og straks at reflektere den korrigerede tidslinje tilbage til operatøren.
Løsningen: En snap-algoritme med fire regler
Ved hvert drop undersøger en snap-algoritme hvert par af tilstødende programmer på den berørte kanal – nye-til-nye, nye-til-eksisterende eller eksisterende-til-eksisterende par, hvis mellemrum ændrede sig på grund af droppet – og anvender præcis én af fire regler baseret på mellemrummet mellem dem:
- Mellemrum = 0 → ingen handling. Uafbrudt er lovligt, og er næsten helt sikkert, hvad operatøren havde til hensigt.
- Mellemrum ≥ 6 sekunder → ingen handling. Operatøren har bevidst efterladt plads, sandsynligvis til en slate eller en ad pod.
- 0 < mellemrum < 6 sekunder → forskyd det andet program bagud for at lukke mellemrummet til nul.
- Negativt mellemrum (overlap) → forskyd det andet program fremad med overlapningsmængden.
Afgørende er, at algoritmen behandler tilstødende par i et enkelt fremadgående pass over den sorterede tidslinje. Hvert forskudt program bliver øjeblikkeligt det "forrige" element til den næste sammenligning – så et drop, der udløser en kædereaktion af forskydninger, løses i én lineær gennemgang, uden behov for rekursion.
Diagram 1 · Snap-beslutningstræet

Systemarkitektur
Scheduleren er bygget op omkring et lille, fokuseret sæt af komponenter:
- NestJS backend (
schedule.service.ts) ejer snap-gennemgangen, kaskade-memoiseringen og skrivekontrakten med MediaLive. - Tidsinterval-overlapningsforespørgsel. Når et drop ankommer, trækker backend'en hvert eksisterende program, hvis tidsvindue berører den nye batch's interval, plus en 6-sekunders buffer på hver side. Fordi denne forespørgsel opererer på tidsintervaller snarere end kalenderdatoer, håndteres programmer, der krydser midnat, identisk med ethvert andet program – der findes ingen speciel datogrænselogik noget sted i systemet.
- Flettet tidslinje. De nye program DTO'er og de forespurgte eksisterende programmer flettes til en enkelt sorteret liste. Snap-gennemgangen kører mod denne kombinerede tidslinje.
shiftedExistingsmap. For ethvert eksisterende program, der berøres af en forskydning, fanger dette map dets originale start- og sluttider første gang det berøres – og overskriver dem aldrig ved efterfølgende berøringer. Denne ene datastruktur er det, der gør flertrins-kaskader sikre.- Lambda
DELETE_PROGRAM. For ethvert forskudt program, der allerede var deployeret til MediaLive, sendes dets originale tider til en Lambda-funktion for oprydning, før MongoDB opdateres. MIN_NEIGHBOUR_GAP_MS = 6000— den eneste konstant, som hver regel, hver overlapningsforespørgsel og hver sikkerhedsbuffer refererer til.
Vigtige ingeniørbeslutninger
1. Fire regler, én gennemgang, ingen særskilte tilfælde. De samme fire regler dækker ethvert scenarie, en operatør kan skabe: et nyt program droppet mellem to eksisterende, to nye programmer, der kolliderer med hinanden, eller et eksisterende program, der skubbes ind i overlap af en tidligere forskydning i det samme drop. Der er ingen separat kodeløsning for nogen af disse – hvert tilfælde reduceres til "undersøg mellemrummet mellem tilstødende programmer og anvend reglen."
2. Seks sekunder, ikke fem. MediaLive håndhæver en minimumsafstand på 5 sekunder mellem planlagte handlinger; planlægning af to inputskift med 4,9 sekunders mellemrum forårsager en deploy-afvisning. Systemet håndhæver 6 sekunder – en et-sekunds sikkerhedsmargin for clock drift mellem backendens ur og AWS'. At indsende en handling på præcis 5.000 sekunder, når encoderens ur læser den som 4.997 sekunder, producerer intermitterende afvisninger, der ligner netværksfejl og føles som ugentagelige bugs. Det ekstra sekund omdanner en intermitterende fejlfunktion til en, der simpelthen aldrig udløses.
Dette kommer med et bevidst kompromis: et 6-sekunders minimum betyder, at små uafbrudte mellemrum på 3 eller 4 sekunder lukkes til nul i stedet for at bevares. Dette kompromis blev accepteret bevidst – uafbrudte overgange er rene på MediaLive, og et synligt 3-sekunders mellemrum har en tendens til at ligne en fejl for seerne uanset.
3. Original-tids memoisering gør kaskader sikre. Et enkelt drop kan udløse en kæde af forskydninger – program A forskubber B, B forskubber C, C forskubber D. Oprydningskaldet til MediaLive skal målrette hvert programs originale deployerede tid, ikke dets kaskade-forskudte tid; brug af forkert tid får MediaLive til at svare med "ingen handling fundet", hvilket lydløst får oprydningen til at fejle. Gennemgangen opretholder et map af programId → {oldStartTime, oldEndTime}, fanget første gang hvert program berøres. Senere kaskadeforskydninger opdaterer kun den i-hukommelsen værende tidslinje; de memoiserede originaler forbliver uberørte, og oprydningen bruger altid præcis det, MediaLive faktisk har registreret.
Diagram 2 · Et kaskadeeksempel

4. Sikring mod fortidige datoer, håndhævet i to separate faser. Fortidige redigeringer blokeres to gange, bevidst:
- Fase 0, før snap-gennemgangen kører: ethvert nyt program med en starttid tidligere end "nu" afviser hele batchen direkte med en klar fejl. Snap-gennemgangen kører aldrig engang mod et umuligt input.
- Fase 4, efter snap-gennemgangen: to forskellige undercases håndteres forskelligt. Et nyt program, som snap'en ved et uheld trak ind i fortiden (sjældent, men muligt ved anmodnings-tids-urgrænser), tilbageføres lydløst til sin originale, pre-snap-tid – operatørens hensigt bevares, og snap'en anvendes simpelthen ikke. Et eksisterende program, som en forskydning ville skubbe ind i fortiden, afviser i stedet hele batchen – at røre ved et program, der allerede er begyndt at sendes, er aldrig noget, systemet lydløst vil absorbere.
5. En Lambda-først, database-sekund skrivekontrakt. Når en snap forskubber et program, der allerede er deployeret til MediaLive, er MongoDB og MediaLive kortvarigt ude af synk, og rækkefølgen af afstemning betyder noget. Kontrakten: Lambda først, MongoDB sekund. Backend kalder DELETE_PROGRAM på Lambda ved hjælp af programmets originale tider; hvis et kald fejler, kaster backend'en en fejl, før nogen database-skrivning sker. Først når hvert delete-kald lykkes, opdaterer et enkelt bulkWrite MongoDB med de nye tider og nulstiller isDeployed: false.
Dette producerer en ren invariant: hvis MongoDB viser et program på et nyt tidspunkt, har MediaLive allerede accepteret denne flytning. Hvis operatøren i stedet ser en fejl, er intet system blevet berørt. Der er ingen mulig tilstand, hvor MongoDB og MediaLive lydløst er uenige om et programs tidspunkt.
6. Nulstillingsdagen har sit eget dedikerede sikkerhedsnet. "Nulstillingsdag" sletter hvert program på en kanal for en given kalenderdag – den enkelt mest destruktive operation i systemet – så den bærer to specifikke beskyttelser.
- En 2-minutters buffer (
SAFETY_BUFFER_MS = 120000) fritager ethvert program, der starter inden for de næste to minutter, hvilket giver live-afspilning et spillerum, så en nulstilling aldrig kan komme i konflikt med et program, der er ved at gå i luften. - Bevaring af overførte programmer udelukker programmer, der startede dagen før, men strækker sig ind i dag – disse tilhører gårsdagens plan, ikke dagens.
En venlig fallback er også tilgængelig: hvis en kanal slet ikke er deployeret til MediaLive, returnerer Lambda en bestemt fejlstreng, som backend'en forstår, logger som no_infrastructure, og udfører derefter en MongoDB-kun soft-sletning. Nulstillingen lykkes stadig; AWS-trinnet bliver simpelthen en no-op.
Hvorfor denne kombination af designvalg
| Beslutning | Hvorfor det blev truffet | Alternativ overvejet | Kompromis accepteret |
|---|---|---|---|
| Automatisk løsning ved drop-tidspunkt vs. afvis-og-spørg | Holder træk-og-slip brugbart i stor skala; manuel snap-matematik overlever ikke en voksende afspilningsliste | Afvis ved konflikt, bed operatør om at rette | Kræver, at systemet, ikke operatøren, garanterer korrekthed |
| 6-sekunders minimum vs. MediaLives angivne 5-sekunders minimum | Absorberer clock drift mellem backend og AWS, hvilket forhindrer intermitterende deploy-fejl | Håndhæv præcis 5 sekunder | Små (3–4s) bevidste mellemrum snappes til nul i stedet for at bevares |
| Enkelt fremadgående pass-gennemgang vs. rekursiv konfliktløsning | Kaskader løses deterministisk uden bekymringer om rekursionsdybde | Rekursiv forskydning-og-genkontrol | Kræver omhyggelig forudgående sortering af tidslinjen |
| Lambda-først / DB-sekund vs. DB-først / Lambda-sekund | Garanterer, at MongoDB og MediaLive aldrig lydløst kan være uenige | Opdater MongoDB optimistisk, synkroniser MediaLive bagefter | Lidt højere latenstid pr. forskudt og deployeret program, i bytte for nul drift-risiko |
Hvad der stadig overvåges
Ærlig ingeniørkunst betyder at nævne de huller, der stadig er åbne, ikke kun dem, der er løst.
- Samtidige redigeringer på samme kanal. Hvis to operatører klikker på Deploy på den samme kanal inden for få hundrede millisekunder af hinanden, vil begge indlæse det samme snapshot, begge vil køre snap-gennemgangen uafhængigt, og begge vil skrive til MongoDB. Der er ingen per-kanal lås eller optimistisk versionskontrol i dag. Den nuværende afbødning er operationel – én operatør ejer én kanal ad gangen – mens den tekniske løsning, et versionsfelt på kanal-dokumentet kontrolleret ved skrivetidspunktet, er på køreplanen.
- Ingen in-UI feedback for hvad der snappede. Når gennemgangen forskubber et program med tre sekunder, opdateres operatørens visning til den korrigerede tilstand, men viser endnu ikke hvad der flyttede sig og hvorfor. Dataene findes allerede i respons-payload'en; en toast, sidebar eller diff-visning er planlagt til næste iteration af scheduler UI'en.
Resultater
- Operatører kan droppe et program hvor som helst på tidslinjen, og systemet gør den resulterende plan lovlig i én deterministisk gennemgang – ingen konflikt-modaler, ingen fejlbarriere, ingen manuel snap-matematik.
- En enkelt fire-regels algoritme dækker ethvert tilfælde – nyt-mod-nyt, nyt-mod-eksisterende og kaskadeforskydninger over dagsgrænser – uden at specialbehandle nogen af dem.
- Programmer, der krydser midnat, og DST-overgange flyder gennem den samme tidsinterval-overlapningsforespørgsel som ethvert andet tilfælde; der er ingen separat "midnat-kodeløsning" at vedligeholde.
- Nulstillingsdagen kan ikke ved et uheld tage et live program af luften – 2-minutters buffer og bevaring af overførte programmer gælder for hver kanal, hver gang.
- Lambda-først / database-sekund kontrakten gør lydløs planforskydning umulig: MongoDB og MediaLive er garanteret at være enige, eller operatøren ser en eksplicit fejl.
MIN_NEIGHBOUR_GAP_MSer den eneste justerbare knap. Hver sikkerhedsmargin, hver snap-regel og hvert overlapningsvindue refererer til den, så justering af platformens definition af "lovlig" er en ændring på én linje.
Afsluttende tanker
Det mest interessante ved dette system er ikke nogen enkelt regel – det er, hvor få regler der var nødvendige. Fire betingelser for en mellemrumsværdi, anvendt i ét fremadgående pass, dækker enhver konflikt en operatør kan skabe, inklusive flertrins-kaskader over midnatsgrænser. Det er et bevidst designresultat: kompleksiteten blev skubbet ind i at få reglerne rigtige én gang, snarere end ind i at håndtere en stadigt voksende liste af specialtilfælde.
Den bredere lektion generaliserer ud over planlægningssoftware: når et system har hårde eksterne begrænsninger – en encoders minimumsafstand, en databases konsistensgarantier, en live-udsendelse, der ikke kan afsendes – er det sikreste sted at håndhæve disse begrænsninger i et lille antal deterministiske regler anvendt konsekvent, ikke i ad hoc-håndtering spredt ud over kodebasen. Og når to systemer af optegnelser (her, MongoDB og MediaLive) skal holdes synkroniseret, er det den ekstra latenstid værd at ordne skrivningerne, så fejl altid efterlader dem i en kendt, enig tilstand.
Om MicrocosmWorks
Hos MicrocosmWorks bygger vi produktionsklar software til organisationer, der løser komplekse ingeniørproblemer.
Vores ekspertise omfatter AI-applikationer, SaaS-platforme, enterprise-software, cloud-native systemer, mediateknologi og tilpasset backend-arkitektur.
Gennem vores ingeniørblog deler vi praktiske erfaringer fra design og drift af virkelige produktionssystemer.
Læs Videre
Hvis du nød denne artikel, kan du måske også finde disse emner nyttige:

