Skip to content

Load storages in the storage Worker in the standalone app - #1323

Merged
c-bata merged 5 commits into
optuna:mainfrom
c-bata:standalone-storage-worker
Aug 14, 2026
Merged

Load storages in the storage Worker in the standalone app#1323
c-bata merged 5 commits into
optuna:mainfrom
c-bata:standalone-storage-worker

Conversation

@c-bata

@c-bata c-bata commented Aug 14, 2026

Copy link
Copy Markdown
Member

Contributor License Agreement

  • I agree this patch may be ported to Goptuna by other Goptuna contributors.

Reference Issues/PRs

Follow-up of #1322, which added the storage Worker and its client without any caller. This is the first caller.

What does this implement/fix? Explain your changes.

Selecting a storage file in the standalone app blocked the page. sqlite-wasm initialization, sqlite3_deserialize() and every query ran on the UI thread, and a Journal file was decoded, split, JSON-parsed and replayed there as well, so nothing responded until parsing finished.

The app now reads the file with File.arrayBuffer() and transfers the buffer to the storage Worker, which detects the format, opens the backend and answers queries. The UI thread only sends requests and renders the results.

Session handling

StorageProvider owns the session instead of holding whatever storage it was handed:

  • one storage at a time, and a second file is refused while one is open, rather than silently replacing it
  • the loader disappears once a storage is open, and the study list gains a button to close the current one, which is the supported way to open another file
  • the storage is closed on unmount, and a load whose session was replaced while it ran closes the storage it just opened rather than publishing it
  • loading and error state are tracked, so the UI can show both

Failures are visible

A rejected storage request used to leave the page as it was, since nothing caught it and nothing rendered it. The study list and the study page now survive one, ignore a response that arrives after they stopped caring about it, and report it to a snackbar. This is the first time any of these errors are visible.

Bundle

The UI bundle no longer contains the SQLite backend: it is only reachable from the Worker entry. vite build before and after:

before  index-*.js  5,538.73 kB │ gzip: 1,672.55 kB
after   index-*.js  5,308.73 kB │ gzip: 1,610.26 kB
        storage_worker-*.js       220.61 kB

The VS Code Webview is unchanged

Starting the Worker in a Webview needs assets that the Webview build does not emit yet, so vscode_entry.tsx keeps parsing on the UI thread and hands the result to the provider through setStorage().

The format detection moved from the provider into that entry point: left where it was, it would keep pulling the SQLite backend into the standalone bundle. Both go away in the follow-up that moves the Webview over.

c-bata added 5 commits August 14, 2026 14:23
A caller has to tell the storage Worker where sqlite3.wasm is, and knowing that
means resolving a file inside node_modules. Wrap it in a `?url` import the
bundler rewrites to whatever it emitted, so an app can hand over the URL without
reaching into the dependency layout itself.

The suffix is understood by Vite, hence the module declaration; the VS Code
build resolves the same asset its own way.
Selecting a storage file blocked the page: sqlite-wasm initialization, the
queries, and for a Journal file the whole decode, split, parse and replay all
ran on the caller's thread, which is the UI thread. The app stopped responding
until it finished. The file is now read with File.arrayBuffer() and its buffer
transferred to the storage Worker.

The provider owns the session rather than holding whatever storage it was handed:
it keeps at most one at a time, refuses a second file while one is open, closes
on unmount, and tracks loading and error state so the UI can show both. A load
whose session was replaced while it ran closes the storage it just opened rather
than publishing it. Which Worker to start is injected, because a Vite build and
a VS Code Webview start one differently.

The Webview still parses on the UI thread and hands the result to setStorage(),
which is why that entry point survives. Its format detection moved into
vscode_entry.tsx, where it belongs until that side is moved over as well: left
in the provider, it would keep pulling the SQLite backend into the standalone
bundle, which is exactly what this change gets rid of.
A storage request that failed used to leave the page as it was, since nothing
caught it and nothing rendered an error. The study list and the study page now
survive a rejected request, ignore a response that arrives after they stopped
caring about it, and report the failure to a snackbar, which is the first time
any of these errors are visible.

The loader is hidden once a storage is open, so a second file cannot quietly
replace the first, and the study list gains a button to close the current one.
Dropping an unrelated file opened it as a storage with nothing in it: the loader
disappeared, no study appeared, and nothing said why. Three files did that.

A JSON or text file is not SQLite, so it was read as a Journal, where a line that
cannot be read is collected as a warning rather than raised. That is what keeps a
partially written Journal usable, but it also makes a file of the wrong format
read as one with no records. Count the lines that were understood as records and
reject a file that yielded none. A Journal storage whose studies were all deleted
still has records, so it stays openable.

An empty file is neither format, and creating a storage does leave one behind, so
name that case instead of opening it.

A SQLite database that Optuna never wrote deserializes like any other and only
failed on the first query, from where the study list could not tell it apart from
a storage with no study. Check for the tables the queries need while opening.

All three checks are in the Worker, next to the format detection they belong to.
The button that closed the current storage was an unlabelled icon: it said
neither what was loaded nor what pressing it would do. Show the file name in the
app bar instead, as a chip whose delete icon closes it and brings the loader
back.

The name comes from the File the loader read, so it is only there for a storage
the standalone app opened itself.
@c-bata
c-bata merged commit 6807b64 into optuna:main Aug 14, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant