Canlı TV operatörlerine bir planlama aracında ne görmek istediklerini sorduğunuzda, neredeyse istisnasız olarak şöyle derler: "Bir kanalı, dosyaları bir klasörde düzenlediğim gibi oluşturmama izin verin. Bir programı sürükleyin, istediğim yere bırakın, bitti."
Sorun şu ki, bir kanal programı bir klasör değildir. Bu, katı kısıtlamaları olan yasal bir belgedir. İki program aynı anda yayınlanamaz. Encoder, girişleri beş saniyeden daha hızlı değiştiremez. Halihazırda başlamış bir program düzenlenemez. Ve gece yarısını geçen bir film, gün sınırında bölünmüş iki program olarak değil, tek bir program olarak ele alınmalıdır.
Her drag-and-drop eylemi, yaşanmayı bekleyen potansiyel bir kısıtlama ihlalidir. Bu, mStudio'nun zamanlayıcısının operatör dostu drag-and-drop özelliğini AWS MediaLive üzerinde yasal olarak garantili bir kanal zaman çizelgesine nasıl dönüştürdüğünün — bir operatörün bir programı doğrudan diğerinin üzerine bıraktığı an da dahil olmak üzere — ve sistemin operatöre bir hata ve çözülmesi gereken bir bilmece vermek yerine bu çakışmaları neden otomatik olarak çözdüğünün mühendislik hikayesidir.
Hızlı Genel Bakış
| Yön | Detay |
|---|---|
| Alan | AWS MediaLive üzerinde canlı FAST kanalları için drag-and-drop planlama |
| Çakışma çözümü | 4 kuralı olan bir snap algoritması aracılığıyla otomatik kaydırma |
| Minimum komşu boşluğu | 6 saniye (MediaLive'ın 5s minimumu + 1s clock-drift güvenliği) |
| Basamaklı işlem | İlk dokunuşta orijinal zamanlar belleğe alınır, böylece zincirleme kaydırmalar program başına bir temizleme çağrısı üretir |
| Geçmiş tarih güvenliği | İki aşamalı koruma — girdi reddi, artı sessiz post-snap geri alma |
| Sıfırlama günü güvenliği | 2 dakikalık canlı oynatma arabelleği, artı devir koruması |
| Durum | Üretimde |
İş Problemi: Takvim Olmayan Bir Takvim
Kanal planlaması takvim yazılımına benzer. Operatörler de öyle davranmasını beklerler — bir filmi akşam 9 yuvasına sürüklemek, programları zaman çizelgesinde yukarı kaydırmak, bir diziyi toplu eklemek ve bölümlerin arka arkaya dizilmesini izlemek. Ancak canlı bir kanal programı, bir takvimde bulunmayan kısıtlamalar içerir:
- Programların çakışmasına izin verilmez. Canlı TV aynı anda yalnızca tek bir şey yayınlar.
- Encoder'ın minimum eylem aralığı vardır. AWS MediaLive, girişleri her 5 saniyeden daha hızlı değiştiremez. İki programı 4 saniye arayla planlarsanız, deploy reddedilir.
- Geçmiş programlar düzenlenemez. Yayın süresi zaten geçti; bitler zaten izleyicilerin ekranlarında.
- Gece yarısını aşan programlar tek bir birimdir. 23:30'dan 01:15'e kadar yayınlanan bir film, gün sınırında bölünmüş iki yarım program olarak değil, tek bir program olarak ele alınmalıdır.
İki saniyelik bir "neredeyse çakışma" operatör hatası değildir — bu, birbirine neredeyse uyan ama tam olarak uymayan iki programı sürüklemenin doğal sonucudur. Gerçek mühendislik zorluğu, "bir filmi akşam 9'a sürüklemeyi" "yasal bir kanal programına" çevirmektir. Yanlış yapıldığında, operatör ya her bırakışta bir hata duvarına çarpar ya da — daha kötüsü — yayın zamanında encoder'ın programın bir kısmını sessizce reddettiğini keşfeder.
AWS MediaLive'da "Yasal" Olmak Ne Demektir
Bir programın MediaLive'da yasal olması için, bitişik programlar ya şunlardan biri olmalıdır:
- Arka arkaya — aralarında sıfır boşluk, ya da
- En az 5 saniye ile ayrılmış — encoder'ın minimum eylem aralığı.
Tuzak, aradaki her şeydir. 1 saniyelik bir boşluk, 3 saniyelik bir boşluk, 4.9 saniyelik bir boşluk — hepsi bir UI'da gayet iyi görünür, ancak hepsi deploy zamanında reddedilir. Daha da kötüsü, ret temiz, atomik bir başarısızlık değildir; bazı programlama eylemlerinin MediaLive'a ulaştığı, diğerlerinin ise ulaşmadığı kısmen-deployed bir kanalla sonuçlanabilir.
Uygulama sunucuları ile AWS arasındaki clock drift için 1 saniyelik bir güvenlik marjı ekleyin; pratik taban 5 değil, 6 saniye olur. Bu tek sayı — MIN_NEIGHBOUR_GAP_MS = 6000 — tüm conflict-resolution sisteminin etrafında inşa edildiği tek sabittir.
Bariz Yaklaşımlar Neden Başarısız Oluyor
Otomatik çözüme karar vermeden önce, daha bariz birkaç strateji düşünülmüş ve reddedilmiştir:
"Herhangi bir uyuşmazlığı reddet ve operatörün çözmesini iste." Bu nedenle, operatörler her bırakma işleminde snap matematiğini manuel olarak yapmak zorundadır. Beş saniyelik bir sürükleme, beş dakikalık bir bulmacaya dönüşür ve playlist büyüdükçe bulmaca zorlaşır. Pratikte, operatörler drag-and-drop'ı tamamen bırakır ve spreadsheets'e geri dönerler.
"Her şeyi 5 dakikalık sınırlara sabitleyin ki hiçbir şey çakışmasın." Bu, operatörün amacını yok ederek teknik sorunu çözer. 21:03:15'te başlaması gereken bir program sessizce 21:05:00'e atlamamalıdır. Program, yuvarlama işlevine değil, operatöre aittir.
"Çakışmaları deploy zamanı yerine drop zamanında tespit et." Bu, UI'da daha hızlı hissettirir, ancak hatayı mümkün olan en kötü ana taşır. Operatör Deploy'a tıkladığında ve "program 47'de schedule reddedildi" mesajını gördüğünde, buna neden olan edit'ten zihinsel olarak uzaklaşmış olur.
"UI'da mikro boşluklara izin ver ve MediaLive'ın bunları reddetmesine izin ver." Bu, opak encoder hatalarını doğrudan operatöre geri iter ve kanalı, kurtarılması gerçekten zor olan yarı-deployed bir durumda bırakabilir.
Gerçekten işe yarayan kaldıraç, çakışmaları otomatik olarak, drop zamanında, deterministik kurallar kullanarak çözmek — ve düzeltilmiş timeline'ı operatöre anında yansıtmaktı.
Çözüm: Dört Kuralı Olan Bir Snap Algoritması
Her bırakmada, bir snap algoritması etkilenen kanaldaki bitişik program çiftlerinin her birini — yeni-yeniye, yeni-mevcuda veya bırakma nedeniyle boşluğu değişen mevcut-mevcuda çiftlerini — inceler ve aralarındaki boşluğa göre dört kuraldan tam olarak birini uygular:
- Boşluk = 0 → eylem yok. Arka arkaya yasal olup, operatörün neredeyse kesinlikle istediği şeydir.
- Boşluk ≥ 6 saniye → eylem yok. Operatör bilinçli olarak yer bırakmıştır, muhtemelen bir slate veya bir ad pod için.
- 0 < boşluk < 6 saniye → ikinci programı geriye doğru kaydırarak boşluğu sıfıra indirin.
- Negatif boşluk (çakışma) → ikinci programı çakışma miktarı kadar ileri kaydırın.
En önemlisi, algoritma sıralanmış timeline üzerinde bitişik çiftleri tek bir ileri pass'te işler. Her shifted program bir sonraki karşılaştırma için hemen "previous" item haline gelir — bu nedenle, bir shift zincirleme reaksiyonunu tetikleyen bir drop, recursion gerektirmeden tek bir linear walk'ta çözülür.
Şema 1 · Snap Karar Ağacı

Sistem Mimarisi
Planlayıcı, küçük, odaklanmış bir bileşen kümesi etrafında inşa edilmiştir:
- NestJS backend (
schedule.service.ts), snap walk'ı, cascade memoization'ı ve MediaLive ile write contract'ını yönetir. - Time-range overlap query. Bir drop geldiğinde, backend, yeni batch'in range'ine dokunan her mevcut programı ve her iki tarafta 6 saniyelik bir buffer'ı çeker. Bu query calendar dates'ten ziyade time ranges üzerinde çalıştığı için, cross-midnight programlar diğer programlarla aynı şekilde ele alınır — sistemde özel bir date-boundary logic mevcut değildir.
- Merged timeline. Yeni program DTO'ları ve sorgulanan mevcut programlar tek bir sıralı listeye birleştirilir. Snap walk, bu combined timeline'a göre çalışır.
shiftedExistingsmap'i. Bir shift'ten etkilenen herhangi bir mevcut program için, bu map ilk dokunulduğunda orijinal start ve end times'larını yakalar — ve sonraki dokunuşlarda asla üzerine yazmaz. Bu tek data structure, multi-step cascade'leri safe hale getirir.- Lambda
DELETE_PROGRAM. MediaLive'a zaten deployed olan herhangi bir shifted program için, orijinal times'ları, MongoDB güncellenmeden önce cleanup için bir Lambda function'a gönderilir. MIN_NEIGHBOUR_GAP_MS = 6000— her rule'un, her overlap query'nin ve her safety buffer'ın referans aldığı tek sabittir.
Temel Mühendislik Kararları
1. Dört kural, tek geçiş, özel durum yok. Aynı dört kural, operatörün oluşturabileceği her senaryoyu kapsar: iki mevcut program arasına bırakılan yeni bir program, birbiriyle çakışan iki yeni program veya aynı bırakmada daha önceki bir shift ile overlap'e itilen mevcut bir program. Bunların hiçbiri için ayrı bir code path yoktur — her case, "bitişik programlar arasındaki gap'i incele ve rule'u uygula"ya indirgenir.
2. Altı saniye, beş değil. MediaLive, schedule action'ları arasında minimum 5 saniyelik bir spacing uygular; iki input switch'i 4.9 saniye arayla scheduling etmek, bir deploy rejection'ına neden olur. Sistem 6 saniyeyi uygular — backend'in clock'u ile AWS'in clock'u arasındaki clock drift için bir saniyelik bir safety margin. Tam olarak 5.000 saniyede bir action submit edildiğinde, encoder'ın clock'u bunu 4.997 saniye olarak okuduğunda, network failure'lara benzeyen ve unreproducible bug'lar gibi hissettiren intermittent rejections'lar üretir. Ekstra saniye, bir intermittent failure mode'unu, sadece asla ateşlenmeyen birine dönüştürür.
Bu bilinçli bir tradeoff ile gelir: 6 saniyelik bir floor, 3 veya 4 saniyelik küçük back-to-back gap'lerin korunmak yerine sıfıra snap edilerek kapanacağı anlamına gelir. Bu tradeoff kasıtlı olarak kabul edildi — back-to-back transition'lar MediaLive'da temizdir ve görülebilir 3 saniyelik bir gap, viewers'a her halükarda bir glitch gibi görünme eğilimindedir.
3. Orijinal-zaman memoization'ı cascade'leri güvenli hale getirir. Tek bir drop, bir shift zincirini tetikleyebilir — program A, B'yi kaydırır, B, C'yi kaydırır, C, D'yi kaydırır. MediaLive'a yapılan cleanup call'ı, her programın cascade-shifted zamanını değil, orijinal deployed zamanını hedeflemelidir; yanlış zaman kullanmak, MediaLive'ın "no action found" yanıtını vermesine, cleanup'ın sessizce başarısız olmasına neden olur. Walk, her programa ilk kez dokunulduğunda yakalanan programId → {oldStartTime, oldEndTime} haritasını tutar. Daha sonraki cascade shift'leri yalnızca in-memory timeline'ı günceller; memoized orijinaller dokunulmadan kalır ve cleanup her zaman MediaLive'ın kaydında tam olarak ne varsa onu kullanır.
Şema 2 · Bir Basamak Örneği

4. Past-date guard'ları, iki ayrı phase'te uygulanır. Past-dated edit'ler, kasıtlı olarak iki kez block edilir:
- Phase 0, snap walk çalışmadan önce: "now"dan daha erken bir start time'a sahip herhangi bir new program, tüm batch'i clear bir error ile outright reject eder. Snap walk, impossible bir input'a karşı asla çalışmaz bile.
- Phase 4, snap walk'tan sonra: iki distinct sub-case farklı şekilde handle edilir. Snap'ın accidentally past'a çektiği new bir program (rare, but possible at request-time clock boundary'lerinde), silently original, pre-snap time'ına revert edilir — operatörün intent'i preserve edilir ve snap simply uygulanmaz. Bir shift'in past'a iteceği existing bir program bunun yerine entire batch'i reject eder — already started airing olan bir programa dokunmak, sistemin never silently absorb edeceği bir şey değildir.
5. Lambda öncelikli, database ikinci bir write contract'ı. Bir snap, MediaLive'a zaten deployed olan bir programı shifted ettiğinde, MongoDB ve MediaLive briefly out of sync olur ve reconciliation order'ı önemlidir. Contract: Lambda birinci, MongoDB ikinci. Backend, programın original times'larını kullanarak Lambda üzerinde DELETE_PROGRAM çağırır; eğer herhangi bir çağrı fails ederse, backend herhangi bir database write gerçekleşmeden önce throws eder. Her delete call succeeds olduktan sonra, tek bir bulkWrite, MongoDB'yi new times'larla update eder ve isDeployed: false reset'ler.
Bu, temiz bir invariant üretir: eğer MongoDB bir programı new bir time'da gösteriyorsa, MediaLive already that move'u accepted etmiştir. Eğer operatör bunun yerine bir error görüyorsa, neither system touched edilmemiştir. MongoDB ve MediaLive'ın bir programın time'ı hakkında silently disagree ettiği possible bir state yoktur.
6. Reset day'in kendi dedicated safety net'i vardır. "Reset day" belirli bir calendar day için bir channel'daki every programı delete eder — sistemdeki single most destructive operation'dır — bu nedenle iki specific protection taşır.
- Bir 2-minute buffer (
SAFETY_BUFFER_MS = 120000), next two minutes içinde starting olan any programı exempt eder, live playback'e bir grace window verir, böylece bir reset about to go to air olan bir programla never race edemez. - Carry-over preservation, previous day started olan but spill into today olan programları exclude eder — those yesterday's schedule'a aittir, not today's.
Gracious bir fallback de available'dır: eğer bir channel MediaLive'a at all deployed edilmemişse, Lambda, backend'in understands ettiği, no_infrastructure olarak logs ettiği ve then MongoDB-only soft deletion performs ettiği certain bir error string döndürür. Reset still succeeds; AWS step simply bir no-op olur.
Bu Tasarım Seçimlerinin Birleşimi Neden
| Karar | Neden yapıldı | Değerlendirilen alternatif | Kabul edilen değiş tokuş |
|---|---|---|---|
| Bırakma anında otomatik çözme vs. reddet ve sor | Drag-and-drop'ı ölçekte kullanılabilir tutar; manuel snap matematiği büyüyen bir playlist'te ayakta kalamaz | Çakışmada reddet, operatörden düzeltmesini iste | Doğruluğu operatörün değil, sistemin garanti etmesini gerektirir |
| 6 saniye taban vs. MediaLive'ın belirttiği 5 saniye minimum | Backend ve AWS arasındaki clock drift'i absorbe eder, intermittent deploy failure'larını önler | Tam olarak 5 saniye uygula | Küçük (3–4s) kasıtlı gap'ler korunmak yerine sıfıra snap edilir |
| Tek ileri-pass walk vs. recursive conflict resolution | Cascade'ler, recursion depth endişeleri olmadan deterministik olarak çözülür | Recursive shift-and-recheck | Sıralı timeline'ın önceden careful ordering'ini gerektirir |
| Lambda öncelikli / VT ikinci vs. VT öncelikli / Lambda ikinci | MongoDB ve MediaLive'ın asla silently disagree etmemesini garanti eder | MongoDB'yi optimistically update et, MediaLive'ı sonra sync et | Shifted-and-deployed program başına slightly higher latency, zero drift risk karşılığında |
Hala Gözlenenler
Dürüst mühendislik, sadece çözülenleri değil, açık kalan gap'leri de isimlendirmek anlamına gelir.
- Aynı channel'da concurrent edit'ler. Eğer iki operatör aynı channel üzerinde birkaç yüz milisaniye içinde Deploy'a tıklarsa, both aynı snapshot'ı load edecek, both snap walk'u independently run edecek ve both MongoDB'ye write edecektir. Bugün per-channel lock veya optimistic version check yoktur. Current mitigation operationaldır — one operator owns one channel at a time — while the technical fix, write time'da checked edilen channel document üzerindeki bir version field, roadmap'tedir.
- UI'da neyin snapped olduğuna dair feedback yok. Walk, bir programı three seconds kaydırdığında, operatörün view'ı corrected state'e refresh olur, but doesn't yet surface what moved and why. Data already response payload'da exists; bir toast, sidebar veya diff view scheduler UI'sının next iteration'ı için planned'dır.
Sonuçlar
- Operatörler bir programı timeline'da anywhere'a drop edebilir ve sistem resulting schedule'ı one deterministic pass'te legal hale getirir — no conflict modals, no error walls, no manual snap math.
- Tek bir four-rule algorithm every case'i kapsar — new-vs-new, new-vs-existing ve day boundaries across cascading shift'ler — without special-casing any of them.
- Cross-midnight programlar ve DST transition'ları, other any case'le aynı time-range overlap query aracılığıyla flow eder; maintain edilecek ayrı bir "midnight code path" yoktur.
- Reset day accidentally bir live programı off air'den alamaz — 2-minute buffer ve carry-over preservation every channel'a, every time uygulanır.
- Lambda öncelikli / database ikinci contract, silent schedule drift'i impossible hale getirir: MongoDB ve MediaLive'ın agree etmesi guaranteed'dır, veya operatör explicit bir error görür.
MIN_NEIGHBOUR_GAP_MSsingle tunable knob'dur. Every safety margin, every snap rule ve every overlap window it'i referans alır, so platform'un "legal" tanımını adjusting etmek one-line bir change'dir.
Son Düşünceler
Bu sistem hakkında en interesting şey, any single rule değil — it's how few rules were needed. Bir gap value üzerindeki four conditions, one forward pass'te applied edilerek, multi-step cascade'ler across midnight boundaries dahil, an operatörün create edebileceği every conflict'i cover eder. That's a deliberate design outcome: complexity, ever-growing special case'ler listesini handling etmek rather'dan, rules'ı one kez right getting'e pushed edildi.
The broader lesson, past scheduling software'ı generalizes eder: bir system'in hard external constraints'leri olduğunda — an encoder'ın minimum spacing'i, bir database'in consistency guarantees'leri, un-aired edilemeyen bir live broadcast — those constraints'leri enforce etmenin safest place'i, codebase boyunca scattered ad hoc handling'de değil, consistently applied edilen small number of deterministic rules'da'dır. Ve two systems of record (here, MongoDB ve MediaLive) sync'te kalmak must olduğunda, writes'ları failure always them'i known, agreeing state'te leaves edecek şekilde ordering etmek, it costs ettiği extra latency'ye worth'tür.
MicrocosmWorks Hakkında
MicrocosmWorks'te, complex engineering problem'lerini solving eden organization'lar için production-grade software build ediyoruz.
Expertise'ımız AI applications, SaaS platforms, enterprise software, cloud-native systems, media technology ve custom backend architecture'ı içerir.
Engineering blog'umuz aracılığıyla, real-world production systems'lerini designing ve operating'den learned practical lessons'ları share ediyoruz.
Okumaya Devam Edin
Bu article'ı enjoyed ettiyseniz, şu topics'leri de useful bulabilirsiniz:

