DEV Community

chovy
chovy

Posted on Originally published at dev.profullstack.com

A spinner is not a status

Someone reported that DiskPush hung when they mirrored a folder from a server down to their laptop. The dialog opened, showed a spinner and the words "Scanning both sides", and stayed like that.

It was not one bug. It was three, and only the first one has anything to do with the spinner.

The scan reported nothing

A mirror preview is an rsync dry run. The code awaited it to completion and returned the result.

const plan = await buildPlan(request, { dryRun: true })
const result = await runToCompletion(plan)
Enter fullscreen mode Exit fullscreen mode

That is correct, and it is useless. A dry run walks both trees. From a laptop to a server over a WAN that is minutes, and for all of those minutes there is no count, no filename, no clock. A scan that is working and a scan that has wedged look identical.

rsync will happily tell you what it is doing. --info=progress2 prints to-chk=400/1000 as it goes, and the parser was already reading it. Nothing was passing it to the window.

The result was 71MB nobody read

Every itemized change was collected into an array and handed back to the renderer. I ran the real dry run against one ordinary source tree to see how big that gets:

332,303 changes, 71.1 MB of JSON
Enter fullscreen mode Exit fullscreen mode

That crosses the Electron IPC boundary by structured clone on every single preview. The renderer used summary, deletes, command. It never touched changes. Not once, anywhere.

The delete list had the same shape of problem in the DOM. One node per entry, no cap, so a first mirror into an empty destination asks Chromium for a few hundred thousand rows.

Cancel did not cancel

Cancel called setPreviewOpen(false). The dialog went away. The rsync process did not. It ran to the end and resolved into a window nobody was looking at, and there was no way to stop it, because nothing had kept a handle on it.

The two I could not read

Two more only turned up after building the renderer and driving it in headless Chromium with the app's real Content Security Policy.

The delete list did not clip. It had max-h-[210px] on the ScrollArea root, and the Base UI viewport inside it is size-full. With no definite height that grows to fit its content, so the rows printed straight over the disclosure below them. Every other ScrollArea in the app is min-h-0 flex-1 inside a flex column, which is where the height actually comes from.

The other one: the dialog footer rendered throughout the scan, so a full strength blue Mirror button sat under the spinner, offering to run a mirror whose delete list did not exist yet. It was disabled. It did not look disabled, and a control that ignores you is worse than one that is not there.

Neither is visible in the source. Both are obvious in a screenshot.

Now

Progress streams while the scan runs: how much has been compared, the file it is on, elapsed time. The result carries exact totals, with the enumerated deletes capped at 5,000 and a line saying how many more there are, so a truncated list never understates what a mirror is about to remove. Stop scanning kills the process.

One rule came out of this that I would keep. If a value crosses a process boundary and nobody on the other side reads it, that is not overhead. That is the bug, and it is sitting right next to the thing you are trying to fix.

DiskPush 0.2.16 is out: https://diskpush.com/download

This post was drafted with AI assistance.

Top comments (0)