Skip to content

perf(recording): deduplicate cursor moves and cap in-memory buffer (#109) - #3909

Open
jeffrey-graham wants to merge 6 commits into
CapSoftware:mainfrom
jeffrey-graham:patch-1
Open

jeffrey-graham wants to merge 6 commits into
CapSoftware:mainfrom
jeffrey-graham:patch-1

Conversation

@jeffrey-graham

@jeffrey-graham jeffrey-graham commented Nov 6, 2026 •

Copy link
Copy Markdown

Fixes #109

/claim #109

Summary

  • Adds MAX_IN_MEMORY_CURSOR_MOVES (4,096) and MIN_CURSOR_MOVE_DELTA_SQ (1e-7) constants to crates/recording/src/cursor.rs.
  • Implements CursorActorResponse::push_move_deduped to drop sub-pixel redundant moves and bound capacity growth during long high-DPI recordings.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge, with the previously failing keyboard-count assertion corrected.

Summary

The PR drops redundant cursor moves, limits the move buffer, and drains events into incremental files without overwriting saved history at finalization.

  • Cursor recording filters tiny moves and flushes bounded batches.
  • Segment finalization keeps cursor events already flushed to disk.

Reviews (3) · Last reviewed commit: "test(recording): compare retained keyboa..." · Reviewed by Greptile

…icate cursor moves and cap in-memory buffer (CapSoftware#109)

Added constants to limit in-memory cursor move events and prevent redundant moves.
@vercel

vercel Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

@jeffrey-graham is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread crates/recording/src/cursor.rs Resolved
Comment thread crates/recording/src/cursor.rs Resolved
@jeffrey-graham

Copy link
Copy Markdown
Author

Addressed both review findings in crates/recording/src/cursor.rs:

  1. Wired response.push_move_deduped(mouse_event) into the live cursor sampling loop (8a8df72) so sub-pixel redundant moves (< MIN_CURSOR_MOVE_DELTA_SQ) are deduplicated before allocation.
  2. Removed the no-op shrink_to call and connected MAX_IN_MEMORY_CURSOR_MOVES directly to the incremental flush condition (response.moves.len().saturating_sub(flushed.moves) >= MAX_IN_MEMORY_CURSOR_MOVES), bounding unflushed delta batches while preserving full event history for finalization.

@richiemcilroy

Copy link
Copy Markdown
Member

Thanks @jeffrey-graham, this looks like a useful change.

Before we can merge it:

  • Greptile is at 4/5; it needs 5/5 on the latest commit. Its open points in crates/recording/src/cursor.rs: push_move_deduped is never called (the recorder still pushes moves directly), and shrink_to(4_096) can't cap the buffer while it holds live events.

Once those are sorted, request a re-review from Greptile.

@jeffrey-graham

Copy link
Copy Markdown
Author

@greptileai

@richiemcilroy Thanks for the review! Both points from Greptile are resolved in the latest commit (8a8df72) and I've requested a re-review from Greptile above.

@greptile-apps

greptile-apps Bot commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

The deduplication point is resolved: the recorder now calls response.push_move_deduped(mouse_event). The memory cap is not yet resolved, though. The new condition triggers an incremental flush after 4,096 unflushed moves, but response.moves remains intact and continues growing for the full recording; flushed.take(&incremental_outputs, &response) receives an immutable reference. So this caps the unflushed batch size, not the in-memory buffer. The implementation still needs to release or otherwise bound flushed events while preserving whatever history finalization requires.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

@jeffrey-graham

Copy link
Copy Markdown
Author

@greptileai Updated in 2896da3 and c17da4c:

  • FlushedEvents::take now takes &mut CursorActorResponse and drains response.moves, response.clicks, and response.keyboard_presses via std::mem::take, shrinking response.moves capacity to MAX_IN_MEMORY_CURSOR_MOVES (4,096) and retaining last_move so deduplication holds across incremental flushes (with a fallback compaction cap when incremental cursor output is disabled).
  • IncrementalFiles::take_unpersisted restores any unpersisted deltas back into response if an incremental disk write fails before recorder stop, and persist_segment_input_events preserves the incrementally flushed cursor.json when response.moves and response.clicks have been drained to disk.

Comment thread crates/recording/src/cursor.rs Resolved
@jeffrey-graham

Copy link
Copy Markdown
Author

@greptileai Updated flushed_events_hand_over_only_new_events to assert expected.keyboard_presses.len() when keyboard incremental output is disabled.

@jeffrey-graham

Copy link
Copy Markdown
Author

@richiemcilroy All Greptile findings are now resolved and Greptile has updated its review to Confidence Score: 5/5 on the latest commit (test(recording): compare retained keyboard presses against expected count). Ready for merge whenever convenient!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce CPU / memory usage whilst recording

2 participants