Skip to content

perf(watcher): cut redundant work from each session ingest run - #348

Open
0xbrayo wants to merge 1 commit into
ActivityWatch:masterfrom
0xbrayo:perf/session-ingest-single-pass
Open

0xbrayo wants to merge 1 commit into
ActivityWatch:masterfrom
0xbrayo:perf/session-ingest-single-pass

Conversation

@0xbrayo

@0xbrayo 0xbrayo commented Oct 8, 2026

Copy link
Copy Markdown
Member

Part of #333.

Session ingest runs hourly, on every app open and on widget refresh, and the first run after install covers all of the usage history Android retains. Each run did avoidable work:

  • App labels: SessionUtils.getAppName (a PackageManager binder call) ran once per session, even though most sessions belong to a handful of apps.
  • Event passes: queryEvents was called and fully iterated twice, once for sessions and once for unlocks, allocating a new UsageEvents.Event per entry.
  • Bucket listing: createBucketHelper serializes every bucket via getBuckets() to check whether one exists, and ran twice per run.

Change

  • parseEventsIntoSessions caches app labels for the duration of the run.
  • New SessionParser.parseSessionsAndUnlocksSince(sessionsSince, unlocksSince) reads sessions and unlocks in one pass, reusing a single Event. It takes separate lower bounds so it composes with fix(watcher): resume unlock ingest from the unlock bucket's last event #335, which gives unlocks their own cursor. The now-unused parseUsageEventsSince and parseUnlockEventsSince are removed.
  • RustInterface.createBucketsHelper(map) ensures several buckets with one listing. createBucketHelper delegates to it.

Notes

Testing

  • ./gradlew :mobile:testStandardDebugUnitTest passes.
  • The changed paths need UsageStatsManager and the native library, so this has no new unit test.

@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no blocking issue was found.

Summary

This PR cuts repeated work when saving app sessions and screen unlocks.

  • Session ingest gathers app sessions and unlocks in one event pass.
  • Sessions from the same app reuse one app-name lookup.
  • Session ingest checks both required buckets with one listing.

No actionable new issues were found. Tests were not rerun.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Start ingest run] --> B[List buckets once]
    B --> C[Create missing session and unlock buckets]
    C --> D[Read last stored session]
    D --> E[Query usage events once]
    E --> F[Collect activity events and unlock timestamps]
    F --> G[Parse sessions and cache app labels for this call]
    G --> H[Save sessions and unlocks]
Loading

Reviews (2) · Last reviewed commit: "perf(watcher): cut redundant work from e..." · Reviewed by Greptile

@0xbrayo
0xbrayo force-pushed the perf/session-ingest-single-pass branch from 4733dab to fcfc7a4 Compare October 8, 2026 19:37
Each run made a PackageManager binder call per session for the app
label, iterated queryEvents twice (once for sessions, once for unlocks)
allocating an Event per entry, and listed every bucket twice to ensure
two buckets exist. Cache labels per run, read sessions and unlocks in
one pass reusing a single Event, and ensure both buckets with one
listing.
@0xbrayo
0xbrayo force-pushed the perf/session-ingest-single-pass branch from fcfc7a4 to 662fe5d Compare October 11, 2026 13:13
@0xbrayo

0xbrayo commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

🤖 Claude, on behalf of @0xbrayo

@greptile review

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