Tony GorbProduct Designer & Builder

The download took 5 seconds. The first picture took 11

Scenri's first run fetched its 154 MB library as one archive. The network was not the slow part. The shape of the install was.

In this story5 sections
  1. The wait was not the network
  2. Same archive, read by range
  3. Each file stands alone
  4. Before and after
  5. What I took from it

On a fresh install, Scenri's Home showed its first downloaded picture after 11.1 to 11.7 seconds. On a 20 Mbps line, after 70.9 seconds. I assumed the archive was simply big. It is 154.3 MB, so that sounded reasonable.

Then I downloaded the same archive from the same release host, twice, straight to nowhere. It took 5.33 and 5.49 seconds.

The wait was not the network

The first run worked like this. Three seconds after the server started, it made one request for the library archive. All 154 MB were held in memory, hashed, unpacked and written to a staging folder, which was then swapped into place in one move. Only then could the studio see any of it.

The QA report from 27 September splits the 11 seconds: about 3 seconds of scheduled wait, about 6 of download, about 2 of unpacking on the server's main thread. Unpacking bought almost nothing, since compression saved 0.5% of the size. And nothing was visible until all of it had finished.

Worse, the order was wrong in a way no one would choose. Home's own pictures sat 65 to 90% of the way into the archive. The picture the user was waiting for arrived with the last one.

Two failures came with that shape. A stop at 50% restarted from byte zero: the next launch fetched the full 154.3 MB again. And one bad file failed the whole library. Nothing was installed, 95 missing-picture marks showed on Home, and nothing was logged.

Same archive, read by range

The fix did not need a new host or a new format. The release host answers byte ranges, so Scenri now reads the published archive a file at a time.

A pin file, committed beside the code, lists every file in the archive: where its bytes start, how long they are, how they are compressed and the SHA-256 they must hash to. The host refuses suffix ranges, so the archive's own directory at its tail cannot be read remotely. The offsets have to ship with the app.

The order comes from what Home shows, read from the same records Home is built from, never a list of file names: each wall tile in its curated order, then the product and person chips on it, then the portraits and covers the library pages open on, then everything else in archive order. Pictures the app package already carries byte for byte, 262 files and 5.8 MB, are copied rather than fetched.

Reads run eight at a time, a number measured rather than picked, with neighbouring files merged into one read of up to 4 MiB. Each response's byte range is checked before its body is used.

Each file stands alone

Every file is kept the moment its bytes are in, and only once its own hash matches the pin. It is written aside and then renamed, so a stop never leaves half a file under its real name. A stop keeps every file that fully arrived, and the next start checks them and fetches only the rest.

A file that fails is retried on its own, five times with backoff, and then fails alone. Generation still reads only a whole library: the marker that says the library is installed is written last, when every file is there. The display routes show pictures as they land; the engine never sees a half library.

Before and after

First downloaded picture, fast line
1.6 sfrom 11.1 s
First downloaded picture at 20 Mbps
3.2 sfrom 70.9 s
Fetched twice after a stop
2.3 MBfrom 154.3 MB
Server peak memory
390 MBfrom 649 MB

The whole library now completes in 6.2 to 7.2 seconds on the fast line, against about 5.3 for the raw download. The gap is the price of reading Home's scattered files first, which is the point. At 20 Mbps it completes in 60.2 seconds, where the line itself needs 59.4 seconds for the bytes fetched. That is as fast as the connection allows. A 20 second network cut in the middle now finishes at 47.7 seconds instead of waiting for the next launch.

The same harness ran three first runs on a real Windows machine, including a stop and resume, with the library whole on NTFS.

What I took from it

The number that mattered was not the download time. It was the time until the first thing the user looks at. An archive optimises for the person publishing it: one file, one hash, one request. A first run should optimise for the person waiting.

  1. Time the raw transfer on its own before blaming the network. If it is much faster than your install, the wait is in your code.
  2. List what the first screen shows, and fetch those bytes first.
  3. Verify each file on its own, so one bad file fails alone.
  4. Keep every file that fully arrived. A restart should cost what was missing, not everything.
  5. Let the display read partial data, and keep anything that needs completeness behind a marker written last.

Tony Gorb, Product Designer & Builder

Questions about this story, or a design role to talk about? Write to bytonygorb@gmail.com or find me on LinkedIn.

More stories