Foundations · Goals

Goals, users and numbers

Who uses omnium, what it must do, and the numbers every later part of the design is held to.

Design section
Section 1, agreed 3 Oct 2026
Main code
core/workflows, core/publishing/delivery, core/cache
Main tables
timeline_item, feed_definition, delivery_assignment, integration
Read time
about 15 minutes

In one minute

Omnium is a product, not a one-off fix. It replaces a legacy .NET scoring system, one sport at a time. Football and curling go first. Cricket, the biggest earner, comes later, but the base is designed for cricket from day 1.

Data comes in from many sources, often on the same match: scorers in the console, data-provider APIs, Scout jobs and hand edits. Every source takes the same road (commands), and a higher-priority source wins over a lower one. Data goes out to B2B clients (about 90 in 100 of our users) through files, APIs and push, and to fans through public APIs on client websites.

Section 1 turned "never lose data" and "no downtime" into eleven targets we can test. The four that shape most of the design: no action lost from the tap on, API and push updates reach the client within 2 s (95 in 100), FTP and SFTP files within 10 s, and 10 million public API requests a day, with peaks of 2,500 a second.

Most of these targets are agreed design. Today's code meets only parts of them; the last table on this page shows which.

0
actions lost from the tap on (target 5)
2 s
API and push, 95 in 100 updates (target 4)
10 s
FTP and SFTP files (target 4)
2,500/s
public API peak (target 10)

What this part does

Section 1 answers five questions before anything is designed. Every later section depends on these answers, so a guess here would be a guess everywhere.

#PartThe questionWhat it fixes later
1UsersWho puts data in, who checks it, who watches it, who takes it out?Roles and sign-in, the screens, the control screen
2Events and sportsWhich events come next? Which sports need event-by-event scoring, and which only results?How much live scoring we build, which sports first
3Size at peakHow many matches at once, actions per second, clients and fans?The database, live updates, cache or push
4TargetsHow correct, how fast, how often up, how fast back after a failure?Save and retry rules, backups, alerts
5First releaseFix what runs today first, or build live scoring first?The order of all the work

The answers came from the product owner in the design doc's question tables (1.1 to 5.2, F1 to F11, G1 to G6). They are written up below in teaching order, not in the order they were asked.

The users

A sketch titled Who uses omnium. On the left, under the heading data in, four boxes: Scorers (operations team), Data providers (their APIs), Scout jobs, and Console staff (fix data). Each has an arrow into a large yellow box in the middle: omnium, workflow platform. On the right, under the heading data out, an arrow labelled files goes to Media clients (NDTV, News18, DailyHunt), and an arrow labelled API goes to Client websites (our public API). Both of those have arrows down to Fans. Below omnium, a dashed arrow labelled alerts goes to Person on duty (one per live day).
Four kinds of source put data in. Clients take it out. Fans are reached mostly through clients.
UserWhat they doWhat they need from omnium
Scorers (our operations team)Score matches live in the console, from the office, a venue, or anywhereA screen that shows a tap at once and never loses it, even on a bad network
CommentatorsAdd editorial commentary, often on a second console for the same matchTheir own lane, so they never block the scorer (D9)
Console staffFix wrong data by hand, watch read-only screens and stats dashboardsFixes that stick (an import must not undo them) and bulk edits with a preview, without a developer
Data providersSend data through their APIs, pushed to us or pulled by usTo be registered once, by settings, and turned into the same commands as a scorer's
ScoutOur own scraper jobs that read official results sitesThe same road in as every other source
B2B clientsMedia, leagues and others: NDTV, News18, DailyHunt, Google and moreThe right score, fast, in the exact format their contract says
FansRead scores on client websites and apps, some through our public APIFast pages, at any crowd size
Person on dutyWatches the system on a live dayAn alert when something breaks, not a log to read (D8)

What we knew before Section 1, from the Asian Games (Aichi–Nagoya, 19 Sep to 4 Oct 2026), our only real load so far:

  • No live scorers. Every change came from Scout imports or hand edits in the console. The ball-by-ball scoring screens were built but never used on prod.
  • Three media clients. NDTV and News18 got 7 files each, by FTP, SFTP or S3. DailyHunt got the top-5 medal table only, from 30 Sep.
  • No targets were written down. No delay target, no uptime target, no "how much data may we lose" target. Real delay from the official site to a client file was minutes: Scout read the site every 1 to 10 minutes, then the send rounds followed.

How it works

The targets sit along the path one score takes, from the scorer's tap to a fan's screen. Each step has one number it must meet, and each number has a reason.

A sketch titled The targets along the path of a score. A row of five boxes joined by arrows: Scorer taps, then Kept on device until confirmed (with a green note: no data lost), then Saved on disk then confirmed (with a green note: failover 2 min max), then Sender, then Clients. Two arrows go from Sender to Clients: the upper one labelled API, push: 2 s, the lower one labelled FTP, SFTP: 10 s. From Saved on disk an arrow goes down to a blue box Cache, and from Cache an arrow goes right to Fans, with a green note: 10 million a day, peak 2,500 a second.
Each step of a score's path carries one agreed target. All of these are agreed design, not measured today.
  1. The scorer taps. The screen shows the action at once. The round trip from India to our servers in AWS US East is about 200 ms (from the design doc), so the screen must not wait for the server to show it.
  2. The device keeps it. The action stays on the scorer's device until the server confirms it. If the venue network drops, scoring goes on, and the actions sync when the network is back (targets 5 and 6).
  3. The server saves, then confirms. The server says "saved" only after the save is on disk. This is what makes "no data lost" true from the tap on (target 5). If the database fails over to its standby, clients see at most 2 minutes of extra delay, and scorers keep scoring on their devices (target 6).
  4. The sender tells the clients. For APIs and push, 95 in 100 updates reach the client within 2 s of being saved. For FTP and SFTP files, within 10 s. A client's contract may allow slower; nothing faster is promised (target 4).
  5. The cache answers fans. Public API reads go through a cache. The database sees a few reads per match per second, however many fans there are. The public API must be ready for 10 million requests a day and peaks of 2,500 a second (target 10).

Around the path there are four more rules. Every action records who did it and from which screen (target 8). Results go out at once by default, and a checker step can be switched on per sport or client later (target 7). A new client feed, provider or sport is added by settings, in 1, 3 and 5 days (target 11). And one person is on duty, with a minimum set of alerts, on every live day (D8).

A worked example

Example · A goal in the 63rd minute, on a busy day (agreed design)

This follows one football goal through the agreed targets. The times are the targets, not measurements. Nothing on this path is measured yet.

  1. 19:42:10.000. A scorer in the office in India taps "Goal, home, player 9". The console shows 1–0 at once and marks the action "unsent: 1".
  2. 19:42:10.000 to 19:42:10.200. The action travels to US East and back. That round trip is about 200 ms (design doc, point 7). The office wifi drops for 40 s just after the tap.
  3. During the 40 s. The device keeps the action. The scorer taps a yellow card too; it is kept in order behind the goal. Nothing is lost (target 5) and scoring did not stop (target 6).
  4. 19:42:50. The network is back. The device sends the goal, then the card, in order, each with the same id it had from the start. The server saves each one on disk, then confirms. "Unsent" goes to 0.
  5. Within 2 s of the save. A client on push or API, such as Google, has the goal in 95 updates out of 100 (target 4).
  6. Within 10 s of the save. The FTP file for a media client such as News18 holds the goal (target 4).
  7. The next minute. The goal brings 10 to 20 times the usual fan traffic to client websites that use our public API. That multiplier is an estimate from the design doc; we have no data yet. On a 10-million-request day, the average is about 116 a second, so 20 times is about 2,320 a second. The cache answers them. Postgres sees a few reads for this match each second (target 10).
  8. The log. The goal's row says who scored it (the scorer's account) and which screen it came from (target 8).

Example · A wrong value after an import (agreed design, part built today)

  1. A provider feed and Scout both cover a curling match. The provider has higher priority, so its score wins (D6).
  2. Scout's lower value is still saved, just not published. A difference between the two can raise an alert: a free second check.
  3. A console user spots a wrong end score and fixes it by hand. A hand edit sits above both feeds in priority, so the next import does not undo it.
  4. Today the "hand edit beats import" part is built, as a pin on the field (see Low-level design). Per-field priority between many sources, and keeping the lower value, are agreed in Section 2 and not built yet. See Truth and ownership.

Low-level design

The eleven targets

These are the targets agreed in Section 1. Every later section is checked against them. Each one says what it means and why it is set where it is.

#TargetValue (agreed)Why this number
1Sources of dataScorers in the console, data-provider APIs and Scout, often mixed on one match. More sources will come.The Asian Games mixed Scout and console on the same units; every source must meet in one place
2SportsAny sport. Top 5: cricket, football, kabaddi, hockey, F1. 15 to 20 scored live. First on the new system: football and curling. Cricket later.Football and curling are slow sports, so they prove the base without cricket's pace or risk
3Size10 matches a day now, ready for 50. Built for 10 times that (500 a day, 100 at once) at little extra cost.50 matches a day is a few actions a second at most (design doc estimate); Postgres handles that easily
4Delay to clientsAPIs and push: 95 in 100 updates within 2 s of being saved. FTP and SFTP files: within 10 s. Slower only where a client's contract says so.Ahead of TV (about 19 s) and of most media data products (4 to 20 s)
5Data lossNone, from the tap on. The device keeps every action until the server confirms it, and the server confirms only after the save is on disk."Never lose data" made testable
6DowntimeScoring never stops: scorers keep working through an outage, and actions sync after. Clients see at most 2 minutes of extra delay while the database fails over. Releases cause no downtime."No downtime" made testable; no system can promise nothing ever fails
7CheckingPublish at once by default. A checker step can be switched on per sport or client later, without a redesign.Most entries go straight to clients today; a checker must be possible later
8Who did whatEvery action records the person and the screen it came from.Needed for priority, audit and fixing mistakes
9First releaseThe base first (scoring worker, workflows, console), ready in one month and proven with football and curling. Live scoring is the end goal."Focus on foundation" (answer 5.1)
10Public API loadReady for 10 million requests a day (about 116 a second on average) and peaks of 2,500 a second. A cache answers fans, so their number does not change the load on the database."Millions of requests a day" (answer F8), with room for match spikes
11Adding thingsA new client feed in 1 day, a new provider in 3 days, a new sport in 5 days, without changing the core. Operations fix data in the console without a developer."Build it as a product": new things come by settings, not code

What targets 5 and 6 cost

Decision D11 says "never lose data" and "no downtime" mean exactly targets 5 and 6, and we test them. The design doc names the price plainly:

  • a queue on the scorer's device, so actions survive a network drop;
  • a standby database in a second zone, which is where the 2-minute failover comes from;
  • two copies of every service, and releases that swap one copy at a time.

The device queue also explains decision D10. "Any device" includes Safari on iPhone and iPad, and Safari can delete data a site keeps on the device after 7 days with no visit, and when a private window closes. So the console works in any modern browser, but three setups are tested and certified for live scoring: Chrome on a laptop, Safari on an iPad, and Chrome on an Android phone. The console warns when it cannot keep data on the device.

The load, worked out

The arithmetic behind targets 3 and 10:

seconds in a day                     86,400
1 million requests a day   -> about     12 a second   (1,000,000 / 86,400)
10 million requests a day  -> about    116 a second   (10,000,000 / 86,400)
a goal spike, 10 to 20 times average  (estimate, design doc; no data yet)
20 x 116                   -> about  2,320 a second   -> target peak 2,500 a second

The point of the design doc's view 8: the load is small; correctness and delivery are the hard parts. The writes are a few actions a second. The real load is on the way out: many clients, many formats, and public APIs on client websites. Every public read needs a cache in front of it.

Who did what: the log today

Every change in omnium today is a workflow command, and every command lands as one row in timeline_item before any writer runs. The row carries who sent it. This is the base target 8 builds on.

# packages/core/src/omnium_core/workflows/records.py:45-62
PERSON = "person"
INTEGRATION = "integration"


@dataclass(frozen=True, slots=True)
class Actor:
    """Who sent a command."""

    #: ``person`` or ``integration``.
    kind: str
    #: What the log shows: an account's name, or ``integration:<code>``.
    name: str
    #: ``timeline_item.source_code``: the account id, or the integration code.
    source_code: str | None = None
    ...

Actor is the sender of one command. kind says whether a person or an integration (a Scout import) sent it. name is what the log shows. source_code is the id that makes a retry safe: two senders can each use the same idempotency key without clashing.

# packages/core/src/omnium_core/workflows/engine.py:141-154
item = TimelineItem(
    stream_id=stream_id,
    fixture_id=fixture.id if fixture is not None else None,
    source_code=actor.source_code,
    event_type=code,
    unit=UNIT,
    seq=await streams.next_seq(session, kind, subject_id),
    status=TimelineItemStatus.CONFIRMED,
    occurred_at=datetime.now(UTC),
    payload=_logged(node.impl, payload),
    idempotency_key=idempotency_key,
    actor=actor.name,
)

This is where the log row is made. actor and source_code record who sent it. seq is the next number in this match's (or event's) log. idempotency_key is the client's own id for the action, so a re-send is spotted and not applied twice. For how commands, validations and actions are written, see Writing a workflow in code.

Two gaps against target 8, both checked in the code:

# packages/admin/src/omnium_admin/auth.py:31, 52-56
NO_LOGIN = "admin"
...
    @property
    def actor(self) -> Actor:
        """A person, for the workflow engine. The account id is what makes a retry safe."""
        source = f"account:{self.signed_in.account_id}" if self.signed_in is not None else None
        return Actor(kind=PERSON, name=self.name, source_code=source)
  • The person can be "admin". When nobody is signed in, the log says admin and source_code is empty. Sign-in is off by default: admin_auth_required: bool = False (packages/core/src/omnium_core/settings.py:56).
  • The screen is not recorded. timeline_item has actor and source_code (packages/contract/src/omnium_contract/models/timeline.py:76-78) but no column for the app or screen the command came from.

Sources and priority: the pin today

Decision D6 (a higher source beats a lower one) is settled in Section 2. Today there is one built level of priority: a person beats an import. A field a person set in the console is "pinned", and imports leave it alone.

# packages/core/src/omnium_core/ingest/fixtures.py:1141-1150
pinned = set(row.pinned or [])
changed = False

def allowed(name: str, same: bool) -> bool:
    if same:
        return False
    if name in pinned:
        report.kept_by_hand += 1
        return False
    return True

For each field an import wants to change, allowed asks two things. Is the value the same? Then skip it. Did a person pin this field? Then keep the person's value and count it in kept_by_hand. The pin lists live on fixture.pinned and fixture_competitor.pinned (see the module note at packages/core/src/omnium_core/ingest/fixtures.py:33-38).

Provider feeds: not built today

Decision D5 says provider data takes the same road as scorer data: no side road, raw data kept. Today, the only no-code source is a Scout job. An integration row connects one Scout job to one workflow's import command (packages/contract/src/omnium_contract/models/integrations.py:27-43, with scout_job_id at line 43). The seam meant for live provider feeds does nothing yet:

# packages/worker/src/omnium_worker/ingest/runner.py:293-299
async def _fold_command_stub(fixture_id: uuid.UUID, record: RawRecord) -> None:
    """Tier-1 fold seam — no-op until the provider sample lands.

    The real mapper turns ``record.payload`` into a scoring command,
    ``ledger.append``s it and enqueues the scoring job (which folds).
    """
    return None

A provider message that reaches this seam is turned into nothing. Agreed design, not in the code yet: each provider is registered once by settings (address, login, push or pull, how often), its raw data is kept, and each message becomes the same commands a scorer sends.

Delay to clients: what the code does today

Target 4 is measured from the save. Today's path to a client file has three timers, all settings with these defaults:

# packages/core/src/omnium_core/settings.py:143, 154, 238
feed_debounce_seconds: float = 1.0     # a burst of writes -> one feed rebuild per window
feed_rebuild_seconds: float = 60.0     # sweep: is any event's data newer than its feeds?
delivery_poll_seconds: float = 5.0     # sender asks "who is owed a file?"
  • feed_debounce_seconds joins a burst of writes into one feed rebuild per second.
  • feed_rebuild_seconds is a safety sweep. Writes from another process (such as imports) may not reach the feed engine directly, so once a minute the delivery worker checks for feeds behind their data.
  • delivery_poll_seconds is how often the sender reads the database to find clients who are owed a newer file (packages/core/src/omnium_core/publishing/delivery/dispatcher.py:1-27).

Read from these settings alone (an estimate, not a measurement), a console edit can reach an FTP client in about 1 s plus up to 5 s plus the upload, which fits the 10 s target on paper. A change that waits for the 60 s sweep does not. Neither path is measured end to end today: not checked. The 2 s target for APIs and push needs a different path, designed in Stats, feeds and delivery.

Public reads: the cache today

Target 10 needs a cache in front of every public read. Today the public API has a Redis cache with time limits by how fast the data changes:

# packages/core/src/omnium_core/settings.py:77, 92-103
redis_url: str | None = None
...
cache_ttl_live: int = 3
cache_ttl_scheduled: int = 60
cache_ttl_final: int = 21_600  # 6h
cache_ttl_reference: int = 3_600  # 1h
cache_ttl_leaderboard: int = 60
cache_ttl_negative: int = 45
cache_stale_window: int = 30

Live data is kept 3 s, finished results 6 hours. cache_stale_window lets a key be served for 30 s more while it is refreshed in the background. redis_url is optional, so an environment with no Redis has no cache at all. The 10-million-a-day and 2,500-a-second figures have not been load-tested on this branch: not checked.

Adding things by settings (target 11)

Thing to addTargetToday
A client feed1 daySettings. A feed is a saved GraphQL query in feed_definition; ops change what it selects with no deploy (packages/contract/src/omnium_contract/models/feeds.py:1-10). Sending is a delivery_assignment row.
A provider3 daysSettings only for Scout jobs (integration). Any other provider needs code, and the seam for it is a stub.
A sport5 daysCode: a new package of six files in the flows plugin (see Writing a workflow in code). The core does not change.
A data fixno developerConsole commands for single fixes. A bulk edit with a preview does not exist: on 1 Oct a script was needed to fix 296 shooting values.

When things go wrong

What each failure must look like when the targets are met (agreed design):

What goes wrongWhat happens
Venue or office network drops mid-matchThe scorer keeps scoring. The device keeps every action and sends them in order when the network is back. Nothing is lost (targets 5, 6).
The server gets an action twice (a retry)The action carries the same id on every retry. The server spots it and does not apply it twice.
The server crashes just after a saveThe save is on disk before the "saved" reply, so nothing confirmed is lost. Today this is not true: see the next row.
Today: the process dies after replying, before the commitThe console edit is lost and nobody is told. The commit runs in get_write_session after yield (packages/admin/src/omnium_admin/deps.py:24-32); the research note of 30 Sep found FastAPI runs it after the reply is sent. This breaks target 5 and is fixed in the rebuild.
The database fails over to its standbyScorers keep working on their devices. Clients see at most 2 minutes of extra delay (target 6).
A release is deployed during a matchReleases swap one copy of a service at a time. No downtime (target 6).
The top-priority source goes quiet mid-matchAn operator takes over with one switch, or a lower source takes over after a set time. Settled in Section 2.
A goal brings a traffic spikeThe cache answers fans. The database sees a few reads per match per second (target 10).
Safari deletes data kept on the deviceUnsent actions are normally sent within seconds, so the risk is small but not zero. The console warns when it cannot keep data on the device (D10).
Something breaks and nobody noticesMonth 1 has a minimum alert set in one Slack channel: a live match with no update for too long (set per sport), a client file that fails to send, and a worker that is down. One named person is on duty per live day (D8). On 1 Oct the delivery worker failed every 5 to 7 minutes and was found by reading logs, not by an alert.

Decisions

All fifteen were agreed on 3 Oct 2026. D12 to D15 came from answers G1 to G4.

#DecisionIn plain words
D1The new system is a product. It replaces the legacy .NET system, sport by sport.This is a migration as well as a build.
D2Football and curling go first. Cricket comes later, on the same foundation.Slow sports prove the base; cricket is too important to risk first.
D3The foundation is designed for cricket from day 1. A recorded cricket match must run through it in a test before the foundation counts as done.If cricket does not fit, we find out in week 3, not month 4.
D4Month 1 ends with football and curling running in shadow next to the legacy system. Clients switch one by one after two clean weeks.Same real matches, both systems, outputs compared after every match.
D5Every source (scorer, provider, Scout, hand edit) goes through the same commands. Provider data gets no side road. Raw provider data is kept.One place where all values meet, so priority, "never lose data" and "who did what" can work.
D6Sources win by priority. A higher source overrides a lower one, never the other way round. The details are settled in Section 2.See Truth and ownership.
D7The whole thing is called the workflow platform. Scoring is one kind of workflow.Sources in, workflows in the middle, feeds out. Commentary, results and Games operations are workflows too.
D8Month 1 includes a minimum alert set and one named person on duty per live day. Full alerting comes later.Nobody can keep a promise they cannot see breaking.
D9Commentary is its own lane, linked to the match and, where it fits, to a ball or event. How clients get it is decided in Section 9.A commentator must never make the scorer's actions out of date.
D10The console works in any modern browser. Three setups are tested and certified for live scoring.Chrome on a laptop, Safari on an iPad, Chrome on an Android phone.
D11"Never lose data" and "no downtime" mean targets 5 and 6, and we test them.Goals become tests we can run.
D12Client feeds moved from the legacy system keep exactly the same responses: format, names and ids.A client must not notice the switch.
D13Nothing is designed for one sport. Every part works for any sport, at any depth of detail.Football goals-only or every event; curling per end or every stone.
D14A source can be anything: a scorer, a provider that pushes or is pulled, a file, Scout, or a hand edit.New source kinds are added, not designed around.
D15The shadow run starts only when the base is fully built and tested.No fixed start date; the base being ready is the gate.
A sketch titled From build to first client. Five boxes left to right joined by arrows: Build the base (workflows, worker, console), Football and curling (on the base), Cricket test (a recorded match) with a note underneath saying base counts as done, Shadow run (next to legacy .NET) with an arrow down labelled after every match to a box Compare outputs, and finally a green box Switch clients (one by one). The arrow from Shadow run to Switch clients is labelled two clean weeks.
The order D2, D3, D4 and D15 set. There is no fixed date: the base being ready is the gate.

Built today, or still to build

Checked against the si-build-omnium code on 7 Oct 2026 (branch feat/result-feeds, commit 76ef1e2).

PieceTodayAgreed designStatus
One road for every changeEvery write is a workflow command logged in timeline_item (packages/core/src/omnium_core/workflows/engine.py:141-156)Same, for every source kind (D5, D14)Partly built
Who did whatPerson name and account id, or integration:<code> (workflows/records.py:50-58). "admin" when nobody signs in (admin/auth.py:31). No screen recorded.Person and screen on every action (target 8)Partly built
No data lostServer can reply before it commits (admin/deps.py:24-32; research note 30 Sep). No device queue in the console (research note 02-console-and-bridge.md; not re-checked).Device keeps actions; server confirms after save (target 5)Agreed, to build
No downtimeNot checked in this repo (infrastructure).Standby database, two copies of every service, rolling releases (target 6)Agreed, to build
Delay to clientsDebounce 1 s, sender poll 5 s, sweep 60 s (core/settings.py:143, 154, 238). Not measured end to end.2 s for API and push, 10 s for files, 95 in 100 (target 4)Partly built
Public API cacheRedis cache, live data 3 s (core/settings.py:92); optional (redis_url, line 77). Not load-tested here.10 M a day, 2,500 a second peak, fans never reach the database (target 10)Partly built
Priority between sourcesPerson beats import, by pins (ingest/fixtures.py:1141-1150)Per-field priority across all sources, lower value kept (D6)Partly built
Provider feedsScout jobs only (integration.scout_job_id). Provider seam is a no-op (worker/ingest/runner.py:293-299).Any provider, registered by settings, raw data kept (D5)Agreed, to build
Checker stepNone in the workflow engine (searched for checker or approve: no match).Off by default, switchable per sport or client (target 7)Agreed, to build
Minimum alertsA Slack sender exists (core/notify/slack.py) but no service calls it.Three alerts in one channel, one person on duty (D8)Agreed, to build
FootballPlugin at packages/flows/src/omnium_flows/football/, no substitutions (no match for "substitut").Any depth of detail (D13)Partly built
CurlingNo plugin and no catalogue entry (no match for "curling" in packages/).First sport with football (D2)Agreed, to build
New client feed by settingsSaved GraphQL in feed_definition; delivery_assignment rows1 day (target 11)Built today
Bulk edit with previewNot built.Ops fix data without a developer (target 11)Agreed, to build
Certified devicesNot built.Three setups certified, warning when the device cannot keep data (D10)Agreed, to build
Shadow run against legacyNot started.After the base is built and tested (D4, D15)Agreed, to build

Numbers

NumberWhat it isSource
44 sports, 59 disciplines, 470 eventsAsian Games 2026 sizeDesign doc, "What we know today"
5,531 units, 806 medal units, over 16 daysAsian Games 2026 sizeDesign doc
45 countries, 10,825 peopleAsian Games 2026 sizeDesign doc
621 unitsBusiest day (2 Oct)Design doc
160 units across 13 venuesBusiest half hour (from 10:00, 24 Sep)Design doc
662 unitsIndia aloneDesign doc
3.4 MBThe calendar fileDesign doc
1 to 10 minutesHow often Scout read the official siteDesign doc
about 200 msNetwork round trip, India to AWS US EastDesign doc, point 7
10 to 20 timesFan traffic after a goal, against the averageEstimate, design doc; no data yet
116 a secondAverage of 10 million requests a dayArithmetic, 10,000,000 / 86,400
296 valuesShooting values fixed by script on 1 OctDesign doc
every 5 to 7 minutesDelivery worker failures on 1 OctDesign doc
0.35 s; about 4 s; about 19 sParis 2024 results to media; Wimbledon click to screens; broadcast TV behind the fieldResearch note 03-industry.md, published sources