Tempo

Click anywhere to begin

Vultr

The compute layer.

Tiger supplies the data forks; Vultr supplies the machines that replay against them. Five pre-warmed workers turn one click into five parallel timelines, a London box keeps Tempo running live, and a GPU was rented by the minute to test whether training needed one. It didn't.

Cloud compute

vc2 instances: 1 vCPU workers in New Jersey, a 2 GB backend in London, and an NVIDIA A16 rented by the minute.

Snapshots

One bootstrapped image, cloned into every worker.

Instance tags

tempo-worker marks the pool; anything untagged is never dispatched to.

v2 API

Create, list, lease and tear down the fleet from Python.

In Tempo

How Tempo uses Vultr

01

Five workers replay in parallel

  • Five vc2-1c-1gb instances in ewr, beside Tiger's us-east-1
  • Pre-warmed all day: a cold boot takes ~40 s to active and ~80 s to SSH, too slow for a click
  • One click takes one Tiger zero-copy fork and sends five shifted timelines to five workers at once
  • Each worker is a stateless FastAPI app under systemd: POST /replay returns the P&L, GET /health reports liveness
  • A shared bearer token guards /replay, so no one can aim a public worker at their own database
Orchestratorone clicktempo-worker-1−2tempo-worker-2−1tempo-worker-30tempo-worker-4+1tempo-worker-5+2TigerforkPOST /replay {database_url, decision_id, shift} · Bearer token

02

Lease-based scheduling

  • VultrClient lists instances tagged tempo-worker over the v2 API, following cursor pagination
  • Idle means Vultr reports active and no job holds a lease
  • lease() → mark_busy() → mark_idle(): one job per worker, always released
  • When every worker is busy, jobs queue, so five timelines still finish on three workers
  • Without Vultr credentials it falls back to the same worker app in-process, labelled local-1..n
GET /v2/instances?tag=tempo-workerINSTANCESTATUSLEASEtempo-worker-1activeshift −2tempo-worker-2activeshift −1tempo-worker-3activeshift 0tempo-worker-4activeshift +1tempo-worker-5pendingskippedshift +2 · queuedruns on the first worker releasedidle = Vultr says active AND not leased in memory

03

Snapshot to fleet in one command

  • build_worker_image.py: rsync the repo, bootstrap, verify /health and the token, snapshot, delete the build box
  • create_workers.py: clone five workers from the snapshot; idempotent, with --count, --status and --delete
  • create_backend.py: a separate lhr box running the dashboard and the poller, never tagged as a worker
  • time_vultr.py benchmarks create → active → SSH-ready
  • Snapshot ID, SSH key and worker token are written straight into .env
build_worker_image.pyBuild boxrsync · bootstrap/health ✓Snapshotid → .envbuild boxdeletedcreate_workers.py · ewrworker-1worker-2worker-3worker-4worker-5create_backend.py · lhrtempo-backenddashboard :8501 · pollertagged tempo-backend,never dispatched toidempotent: re-running keeps existing labels

04

A GPU by the minute, to answer one question

  • gpu_box.py: one NVIDIA A16 (vcg-a16-6c-60g-16vram, 6 vCPU, 60 GB, 16 GB VRAM) in lhr: create, check nvidia-smi and torch.cuda, report uptime and cost, delete
  • The plan is easy to miss: GET /v2/plans?type=vcg lists only L40S boxes with no locations; the A16 only appears under type=all
  • Why a GPU at all: the walk-forward backtest retrains a model for every one of 100 weeks, and a parameter sweep multiplies that. Would GPU training make it faster, and would it forecast better?
  • The answer, measured on the same box: 500 models in 17.4 s on the A16 against 28.0 s on its CPU, 1.6× faster, and the forecasts came out the same. Gradient-boosted trees on tabular features don't need one
  • So the live system stays CPU-only (90 s to train on a laptop), and the GPU is created when there's a question to answer and deleted when idle: three boxes, about 29 box-minutes, $1.44, each billed a one-hour minimum
gpu_box.py · one NVIDIA A16, by the minutecreateA16 · lhrnvidia-smi ✓driver, CUDAtorch.cuda ✓device founddelete$0.48 min. hourtrain 500 models · same box, same dataCPU6 vCPU28.0 sGPUA16 · 16 GB17.4 s1.6× faster, same forecasts.trees on tabular data don't need a GPU → the live system stays CPU-onlythree A16 boxes · about 29 box-minutes · $1.44 · deleted when idle