Skip to content

fix(export): send HTTP headers before serializing large exports - #721

Merged
ErikBjare merged 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/export-stream-headers
Sep 23, 2026
Merged

ErikBjare merged 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/export-stream-headers

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Why

HTTP JSON exports still wait until serialization finishes before sending headers (#677 writes a tempfile first). A ~500k-event export then hits the web UI's 30s axios timeout and looks like a no-op. Same on desktop.

ActivityWatch/aw-android#228

Companion PRs:

Change

  • 404 missing buckets and pick the download filename up front
  • Return 200 + Content-Disposition immediately
  • Serialize into an anonymous pipe while the client reads the body

Mid-stream failures truncate the download instead of hanging the connection. Event buffering stays bounded (same serializer as #677).

Tests

cargo test -p aw-server --test api export — existing round-trip/header tests plus empty all-buckets filename.

ActivityWatch#677 still wrote the full JSON tempfile before responding, so a ~500k-event
export could sit silent until the 30s web UI timeout. Open a pipe, return
200 + Content-Disposition immediately, and serialize into the body.

Missing buckets still 404 before headers. Mid-stream failures truncate the
download instead of hanging the connection.

ActivityWatch/aw-android#228

Git-Session-Id: a180614b-5a5a-5d29-84d8-e1c4e076b990
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Review guide (auto-posted)

Structured warm-up for reviewers — what changed, what to run, where to look.

Key files

  • aw-server/src/endpoints/util.rs (+75/-21, Δ96)
  • aw-server/tests/api.rs (+18/-0, Δ18)
  • Cargo.lock (+0/-1, Δ1)
  • aw-server/Cargo.toml (+0/-1, Δ1)

Suggested verification

  • Run the repo's usual CI-equivalent checks locally

Known risks / watch points

  • No automatic high-risk tags; use file list + diff for judgment.

Suggested review focus

  • Confirm behavior matches the PR description acceptance criteria.
  • Skim the largest diffs first (listed above).
  • If CI is green, spot-check the highest-risk paths called out here.

Generated by scripts/github/pr-warmup-review-guide.py for #721.

@greptile-apps

greptile-apps Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the prior datastore-worker blocking defect is fixed and no new actionable failure remains.

Findings

  1. P1 Slow clients block datastore ▶
  2. P2 Performance change lacks measurement ▶

Summary

This PR changes JSON exports so HTTP success headers and the download filename are determined before serialization begins.

  • Validates requested buckets before constructing the response, preserving a 404 for missing buckets.
  • Serializes exports into a private tempfile on the datastore worker, then copies the staged file to the response pipe from a separate thread.
  • Adds coverage for empty all-bucket exports and verifies that an unread response body no longer blocks datastore operations.
  • The earlier datastore-blocking finding is fully addressed: client-paced writes no longer occur on the shared datastore worker.
  • TimeToBuildBob declined the performance-measurement finding because this addresses a reported timeout and incorrect response timing rather than a speculative optimization; they accepted that concern with this rationale.

Diagram

sequenceDiagram
    participant Client
    participant Endpoint
    participant ExportThread
    participant DatastoreWorker
    participant Tempfile

    Client->>Endpoint: GET export
    Endpoint->>Endpoint: Validate bucket and choose filename
    Endpoint-->>Client: 200 + Content-Disposition headers
    Endpoint->>ExportThread: Spawn export stream
    ExportThread->>Tempfile: Create private staging file
    ExportThread->>DatastoreWorker: Export JSON into staging file
    DatastoreWorker->>Tempfile: Serialize export
    DatastoreWorker-->>ExportThread: Completed file
    ExportThread->>Tempfile: Rewind
    ExportThread-->>Client: Copy staged JSON through pipe
Loading

Reviews (2) · Last reviewed commit: "fix(export): don't let a slow client sta..."

Comment thread aw-server/src/endpoints/util.rs Outdated
Comment thread aw-server/src/endpoints/util.rs
@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.22222% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.85%. Comparing base (656f3c9) to head (e6e245f).
⚠️ Report is 134 commits behind head on master.

Files with missing lines Patch % Lines
aw-server/src/endpoints/util.rs 82.50% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #721      +/-   ##
==========================================
+ Coverage   70.81%   79.85%   +9.03%     
==========================================
  Files          51       75      +24     
  Lines        2916     8404    +5488     
==========================================
+ Hits         2065     6711    +4646     
- Misses        851     1693     +842     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Serialize to a tempfile on the worker (disk-paced, same as ActivityWatch#677), then
copy to the response pipe from the export thread. Headers still go out
before the body; unread or slow downloads no longer block heartbeats.

ActivityWatch/aw-android#228

Git-Session-Id: 3f22e322-f840-50e9-aa06-6db541c7f001
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click.

This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted.

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.

2 participants