vike
Desktop appData Manager

Backfill from the GUI

What the Backfill button plans from a selection, the one resolved address that decides whether it runs in this process or on a data service, and what a venue backfill cannot repair.

Select rows in the Stored grid and the bulk bar offers Backfill and Update. Both take the same path: ToolView::stored_backfill's doc records that they alias for now, there being no separate "ignore gaps, always refetch to now" mode yet, and that unlike Delete the action needs no confirm — a network fetch plus an idempotent ingest is non-destructive. The selection drains into crates/vike-desktop/src/app_methods.rs's maybe_spawn_stored_backfill, which plans the jobs and then decides where they run; the authority for that decision is crates/vike-app-core/src/backfill_route.rs.

Planning

crates/vike-app-core/src/backfill_plan.rs maps the selection to jobs. Its header states the gate: only kind == "bar" series are backfillable, and on the local route only on a venue in SUPPORTED_BACKFILL_VENUES — binance, bybit, okx, the three the backfill crate has a keyless kline collector for, over public REST with no credentials. Every other selected series — a quote, trade or book series, or a venue with no collector yet — is counted as skipped, never silently dropped from the tally the status line reports.

Each job targets the series' known gap ranges when it has any, and otherwise a single window ending now; maybe_spawn_stored_backfill's doc fixes that default lookback at 30 days. A run is one at a time — a click while one is in flight, or with an empty selection, is a silent no-op — and completion re-triggers the Stored refresh so the coverage bars and gap cut-outs reflect what landed.

The route

backfill_route is a pure function of one input: the resolved data-service address. Some means the GUI sits on a remote store, so a backfill written anywhere else would be invisible to every reader, and the jobs go over the wire; None means the local store, exactly the path that existed before routing did. The same resolution decides which store the Studio opens and which the Stored grid reads, so the plane a backfill writes and the store the grid shows cannot disagree — crates/vike-app-core/src/stored_mode.rs's header records the incident that forced this: a wire backfill once landed its rows in the server's store while the grid kept reading the local one, and the freshly backfilled series never appeared.

The address is crates/vike-app-core/src/datahub_resolve.rs's resolve_datahub_addr, the one adoption rule: an explicit config.datahub_addr always wins; absent it, the active backend's Welcome advertisement fills in; absent both, there is no data service. Explicit-wins is deliberate — an address the operator wrote into their own config must never be silently replaced by whatever a daemon claims; an advertisement is a default, not an override. The advertisement arrives only at a backend handshake, and a link drop clears it. See config.toml for the key and Remote runs for the Studio's side of the same address.

Local

spawn_stored_backfill_local runs the backfill crate's kline collectors — backfill_binance_klines and its bybit and okx siblings — against a fresh local store handle on a worker thread. The status slot reads Backfilling N series (M skipped)…, then Backfill done: N backfilled, M failed, K skipped. This arm takes the WIRE route: the binary links no collector and no local store engine, so the work happens in the datahub and the status slot names the fix when no datahub answers — set config.datahub_addr, or connect a backend that advertises one. See Observe mode.

Over the wire

crates/vike-app-core/src/backfill_wire.rs's run_wire_backfill makes one backfill verb call per planned range against the resolved address — "History is fetched by the backend, once, into the store — clients request, never fetch." The rows are in the server's store before the BackfillDone frame is written, so nothing streams back, and completion re-reads the remote store as the local arm does. There is no client-side venue gate on this route: which venues have collectors is the server's roster, crates/vike-datahub/src/backfill.rs's BackfillTable, so an off-roster venue is planned, sent, and answered by the server's own refusal naming its supported set. The production table is the same three collectors, writing through the handle the server serves, so a backfilled range is visible to the next bars request on any connection. The kind == "bar" gate still applies — the verb is klines-only.

The capability check happens once, up front. The verb shipped without a protocol-version bump, so a server must advertise FEATURE_BACKFILL in its Welcome — only a backfill-serve build with collectors mounted does — and an unadvertised server is refused client-side with zero request frames sent. render_wire_backfill_status gives every outcome a sentence naming its fix:

  • unreachable — Backfill failed: datahub <addr> unreachable (…) — nothing was sent;
  • too old — the server "predates backfill-on-demand", with its advertised features listed and two remedies: upgrade it to a backfill-serve build, or unset config.datahub_addr to backfill the local store;
  • ran — Backfill done via datahub: N backfilled (R rows), F failed, S skipped, with the first error appended verbatim, so a server-side refusal reaches the operator instead of dissolving into a count.

A per-range failure marks its job failed and the run continues, so one bad venue never strands the rest of the selection. The same seam serves a chart's own gap requests — the header names the Data-Manager and chart-gap kline backfill together.

What a venue backfill cannot repair

crates/vike-backfill/src/caps.rs's header is the finding behind the kind == "bar" gate: no venue-direct backfill in this workspace serves trade or book at all. Every collector that talks to a venue writes bars, dukascopy resampling them from ticks it also writes as quotes; trades and L2 come only from archives and paid vendors. So a hole in a recorded tape generally cannot be filled from the venue — the recorder writes the kinds a venue backfill never does — and Polymarket is the sharpest case, with no price-history endpoint at all. That is why the grid counts those rows as skipped rather than offering a refill it could not perform, and why the Partial column on Coverage exists. Data providers covers the sources that do serve them.

DataSets and the universe

The Symbols tab defines a universe rather than fetching one. crates/vike-data/src/datasets.rs's header describes a DataSet as symbols plus a default provider, interval and benchmark, persisted to storage/datasets.json and seeded with Crypto Majors and FX Majors on first run, so the Data Manager can edit or test a whole set at once. Their names are what the Stored sidebar lists under Watchlists — a label only today, since the grid receives names and not membership. Backfill itself is driven from the grid's selection: "select all (filtered)" over a search is how a universe becomes a job list.

On this page