Skip to content
Protocolzone Protocolzone

Platform to Platform · Sports & Gaming

When a broadcast channel shows a result for a race that has not run

Our in-platform racing broadcast shows result graphics that lag, repeat or attach to the wrong race, and the only fix the ops team has is restarting the whole channel.

This is a sports & gaming problem we approach through our Platform to Platform service line.

The problem

Your platform carries live racing vision, and around the vision it renders race state: the field and prices as the race approaches the jump, then the result once it is known. Punters plan their next bet off that screen. Your trading and operations desks glance at it all day as a sanity check.

Then a channel shows the previous race’s dividends against a race that has not been run, and the display persists until someone notices and intervenes. Punters screenshot it. The complaint that reaches your operations desk says your platform paid a race that never happened.

We are describing a specific defect because we shipped it. A channel in the in-platform racing and sports broadcast module of a platform we built got stuck displaying the prior race’s result flow against the next race, permanently, until manual intervention. The rest of this page is what that defect taught us about how racing broadcast has to be built.

Why it’s hard

Broadcast is a state machine fed by racing data, and it looks like a video problem. The vision itself is the easy half: it plays, or it does not, and either way you know immediately. The hard half is the machine around it. A channel advances through a trigger flow as the race builds to the jump, then a result flow once the race settles. The result flow holds on screen for a fixed dwell before the cycle advances, and the advance to the next race is tied to the race actually jumping plus a delay while settlement-grade data arrives. Every one of those transitions is driven by racing data that arrives late, out of order, or amended.

The natural key is the wrong key. The channel is the thing on screen, so the obvious design attaches broadcast state to the channel. That is exactly the design that produced our stuck display. When result state belongs to the channel, a channel moving to the next race carries whatever state it was holding, and a result flow that was mid-dwell at the moment of the change gets dragged forward and displayed against a race it has nothing to do with. The state was real and the display logic was correct; the state was simply attached to the wrong entity.

Wall-clock assumptions fail on race day. A schedule gives a jump time and says results follow within minutes. Race day says otherwise: delayed jumps, photo finishes, protests, amended results. Any result display driven by “it should be done by now” will eventually show a result that is not final, or hold a screen empty when the result has been declared. The only safe trigger for a result display is a result event: results declared, then settlement-grade data arrived. Those are facts about the race; the clock only guesses.

Failures with no attribution cannot be repaired surgically. When a display is wrong and nothing records which race produced the state on screen, the only intervention available is the bluntest one: restart the channel. That takes down every race the channel covers to fix one, and it erases the state you needed to diagnose the fault.

How we approach it

Key broadcast state by the race. This was the redesign decision, stated in the design discussion almost exactly as it appears here: every entry is keyed by the race that produced it. Race-scoped state means a channel change cannot orphan a result display, because the channel is a view onto the current race’s state and holds nothing of its own between races. The defect class that produced our stuck display becomes unrepresentable rather than merely fixed.

Drive the result flow from result events. The result flow begins only when result events arrive. The dwell that holds a result on screen is an explicit state with an explicit exit condition; nothing sleeps in a loop waiting it out. The advance to the next race waits on the race jumping and on the settlement-data delay, both observed from the racing feed rather than assumed from the schedule.

Make every on-screen state attributable. Everything the channel renders is traceable to the race that produced it, in the state store and in the logs. When a display is questioned, by an operator or by a punter with a screenshot, the answer is a lookup rather than an investigation.

Build recovery per race. Because state is race-scoped and attributable, an operator can correct one race’s broadcast state while the channel keeps running. Channel restart stops being the recovery tool and goes back to being what it should be: an infrastructure operation, performed rarely, for infrastructure reasons.

What we would not do. We would not treat this as a video-pipeline project. The rendering and streaming layers are well-trodden; the failures that reach customers come from the state machine and its keys. We would also not put per-provider logic in the broadcast layer. Racing content is normalised upstream, once, and broadcast consumes the platform’s own race state.

The lesson generalises beyond broadcast. We have found the same defect class elsewhere in the same platform — behaviour driven by timers and assumptions where it should be driven by events keyed to the entity that owns them — and the fix has the same shape every time. It is now a design test we apply to any component that displays or acts on racing state: what is this state keyed to, and what event moves it?

What it takes

A racing feed that carries amendments as well as results. Protests, corrected results and late scratchings are the normal case on race day. A broadcast layer that only consumes clean declarations will display the first version of the truth and never the final one.

An agreed definition of settlement-grade. Someone on the operator side has to decide what quality of result data is fit to put on a screen that punters act on, and when interim results are shown versus held. That is a product and trading decision, and it has to be made before the state machine is built, because the states encode it.

Operational tooling from day one. Per-race state inspection and per-race correction ship as part of the module. If the recovery path is “restart it”, the design is not finished.

Testing against disorder. The test suite that matters replays race days where events arrive late, out of order, duplicated and amended. A broadcast state machine that has only been tested against a tidy schedule has not been tested.

Where this has been done

This is a shipped reference. The in-platform racing and sports broadcast module runs in production as part of a multi-tenant tote and fixed-odds betting platform we built and continue to operate. The stuck-display defect described above was found in that module in production, and the race-keyed, event-driven design described here is the redesign that runs today.

Position on this page

Evidence

Delivered work

Industry

Sports & Gaming

Written for

Head of Product, Operations, CTO

Outcome

Result displays keyed to the race that produced them and advanced by result events, so a wrong or stuck display is corrected per race without taking the channel down.

Questions we get asked

Straight answers.

What actually advances a broadcast channel from one race to the next?
Events, not the clock. The channel moves through a trigger flow as the race approaches the jump, then into a result flow once result events arrive: results declared, then settlement-grade data confirmed. The result flow holds for a configured dwell and the cycle advances only after the race has jumped and the settlement data has landed. Nothing in the cycle assumes a race will run on time, because races do not.
What happens if the channel switches to the next race while a result is still on screen?
Nothing is orphaned, because the result state does not belong to the channel. Every result flow entry is keyed by the race that produced it, so a channel moving on cannot strand a display with no owner. The channel reads the current race's state; it never carries state of its own between races.
How do you recover a channel that is showing the wrong thing?
Race by race. Because every on-screen state is attributable to a specific race, an operator can inspect and correct that race's broadcast state directly. Restarting the channel is never the recovery mechanism. That punishes every race on the channel for one race's bad state, and it destroys the evidence you need to work out what went wrong.
Does this depend on a particular racing vision or data provider?
No. The module consumes the platform's own normalised racing content, aggregated across providers upstream: fields, jump times, results, amendments. The broadcast layer reacts to that state and holds no provider-specific logic, which is what keeps a provider change from becoming a broadcast rewrite.

Read next

Related work.

Case studies

Other use cases

Platforms involved

Is this your problem?

Bring the constraint that makes your version harder than this one. That is the part worth an hour.