Skip to content

feat(ui): implement duplicate request mitigation and input debounce controls #266 - #458

Open
Shubham-Gotawade wants to merge 2 commits into
agnivo988:mainfrom
Shubham-Gotawade:feature-rate-limiting-266
Open

feat(ui): implement duplicate request mitigation and input debounce controls #266#458
Shubham-Gotawade wants to merge 2 commits into
agnivo988:mainfrom
Shubham-Gotawade:feature-rate-limiting-266

Conversation

@Shubham-Gotawade

@Shubham-Gotawade Shubham-Gotawade commented Jun 5, 2026

Copy link
Copy Markdown

Contributed as part of GSSoC '26.

Problem Description

The application lacked an ingress-throttling architecture for database and API analysis commands. If a user rapidly double-clicked execution buttons, spammed shortcuts, or re-submitted processing targets during active loading routines, multiple parallel background worker threads were dispatched for the exact same entity. This behavior triggered redundant HTTP loads, accelerated GitHub token rate-limit depletion, and introduced potential data race hazards across internal view states. Fixes #266.

Solution Implemented

  1. State Isolation Properties: Extended the primary MainModel struct inside internal/ui/app.go with an analysisInProgress logical gate and a lastSubmitTime tracking stamp.
  2. Comprehensive Ingress Guarding: Embedded a synchronized validation layer across all four interaction boundaries (stateInput, stateCompareInput, stateFavorites/History, and the Dashboard hotkey handler). Submissions are instantly dropped if an analysis is active or if time.Since(lastSubmitTime) falls below a strict 2-second debounce threshold.
  3. Deterministic Lifecycle Reset: Rewired execution handlers inside stateLoading and stateCompareLoading to clear the active execution token under all exit vectors: complete payload delivery, execution failures/exceptions, or manual user-driven aborts (ESC / BackToMenuMsg).
  4. Harness Verification Coverage: Authored a localized testing suite inside internal/ui/app_test.go (TestAnalysisDeduplicationAndDebounce and TestCompareDeduplication) to assert that rapid sequential events are dropped seamlessly.

Verification & Test Logs

Validated across localized system paths with passing results:

  • go test -v ./internal/ui/...: PASSED (Ingress guards and debounce clocks verified)
  • go build ./...: PASSED (Clean binary compilation)

Summary by CodeRabbit

  • Bug Fixes

    • Added request deduplication to prevent duplicate repository analysis submissions across all entry points. A 2-second debounce blocks rapid resubmissions, and analysis state is reliably cleared on completion, cancellation, ESC/menu navigation, or errors — including compare flows.
  • Tests

    • Added tests covering analysis and compare deduplication, debounce timing, and proper clearing of in-progress state.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds cross-screen duplicate-request guarding with a 2‑second debounce to analysis triggers, tracks analysis state in MainModel, clears the flag on completion or cancel, makes compare analysis cancellable via context propagation, and adds tests for deduplication and debounce.

Changes

Analysis Duplicate-Request Prevention with Debounce

Layer / File(s) Summary
Analysis state tracking fields
internal/ui/app.go
Added compareCtxCancel context.CancelFunc, analysisInProgress bool, and lastSubmitTime time.Time to MainModel.
Duplicate-request guard across analysis entry points
internal/ui/app.go
Added 2s debounce/duplicate-request guards at main input analyze (AnalyzeRepoMsg), compare input (CompareReposMsg), favorites (Enter), history (Enter), and dashboard (".") triggers; sets analysisInProgress and updates lastSubmitTime when allowed.
State cleanup on completion and navigation
internal/ui/app.go
Clears analysisInProgress on AnalysisResult, CachedAnalysisResult, and error; cancels compareCtxCancel on ESC and in BackToMenuMsg, and clears analysis state when returning to menu.
Compare command context and cancellation checks
internal/ui/app.go
Changed compareRepos to accept ctx context.Context, attached ctx to the GitHub client, and added ctx.Err() checks after each major fetch step for both repositories to propagate cancellation.
Test coverage for deduplication and debounce
internal/ui/app_test.go
Added TestAnalysisDeduplicationAndDebounce and TestCompareDeduplication covering initial submit behavior, ignoring immediate duplicates, clearing state on completion, and time-based debounce acceptance/rejection.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs:

Suggested labels: ECWoC26-ENDED

"🐇
A timid hop, a careful pace,
Two seconds hold each eager race,
No duplicate shall cross my lawn,
One analysis at dawn by dawn,
The rabbit nods — go run, but once!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main feature: implementing duplicate request mitigation and input debounce controls for the UI layer.
Linked Issues check ✅ Passed The PR implements all key requirements from #266: duplicate request prevention via analysisInProgress gate, debounce mechanism with 2-second threshold, and synchronized guards across input boundaries.
Out of Scope Changes check ✅ Passed All changes are scoped to duplicate request mitigation and debounce controls: MainModel fields, ingress guards, context cancellation for compare flow, and corresponding tests.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/ui/app_test.go (1)

32-37: ⚡ Quick win

Duplicate-request checks are not exercising the input-state guards directly

These duplicate assertions run after the model has already moved to loading states, so they can pass even if the stateInput/stateCompareInput guard logic regresses. Add explicit cases that keep state at the entry boundary (e.g., set m.state = stateInput with analysisInProgress = true, and similarly for stateCompareInput) and assert cmd == nil.

Also applies to: 84-87

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/ui/app_test.go` around lines 32 - 37, The test's duplicate-request
assertions check after the model already transitioned to loading, so add
explicit cases that exercise the input-state guards directly: set m.state =
stateInput and m.analysisInProgress = true, call m.Update(duplicateMsg) and
assert returned cmd is nil and model remains MainModel.analysisInProgress true;
repeat the same pattern for m.state = stateCompareInput (and the duplicate
compare message) to cover the other guard (also apply same fix for the similar
assertions around the block referenced at 84-87). Ensure you call m.Update(...)
on the same MainModel instance and assert cmd == nil and analysisInProgress
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/ui/app.go`:
- Around line 354-362: The compare starts work without a cancelable context
(m.analysisInProgress is cleared on ESC but the running compareRepos continues),
so make compareRepos cancellable and wire a cancel func into the model: add a
field like m.compareCancel (context.CancelFunc), create a ctx, cancel :=
context.WithCancel(context.Background()) before setting m.analysisInProgress and
m.lastSubmitTime and pass ctx into compareRepos (instead of starting it without
cancellation), store cancel in m.compareCancel, and on ESC (where
analysisInProgress is cleared) call m.compareCancel() and set it to nil; also
ensure compareRepos clears m.analysisInProgress and nils m.compareCancel on
normal completion or error so a new submission cannot start while the previous
compare is still running.

---

Nitpick comments:
In `@internal/ui/app_test.go`:
- Around line 32-37: The test's duplicate-request assertions check after the
model already transitioned to loading, so add explicit cases that exercise the
input-state guards directly: set m.state = stateInput and m.analysisInProgress =
true, call m.Update(duplicateMsg) and assert returned cmd is nil and model
remains MainModel.analysisInProgress true; repeat the same pattern for m.state =
stateCompareInput (and the duplicate compare message) to cover the other guard
(also apply same fix for the similar assertions around the block referenced at
84-87). Ensure you call m.Update(...) on the same MainModel instance and assert
cmd == nil and analysisInProgress unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8b7e4081-a597-4f98-a234-8652f5822c4e

📥 Commits

Reviewing files that changed from the base of the PR and between 6d6fa89 and e6c720b.

📒 Files selected for processing (2)
  • internal/ui/app.go
  • internal/ui/app_test.go

Comment thread internal/ui/app.go Outdated
@Shubham-Gotawade

Copy link
Copy Markdown
Author

Resolved in commit edc728b. Added a dedicated compareCtxCancel context.CancelFunc tracker to the primary MainModel. The background comparison task now accepts a cancelable context, and all major computational milestone boundaries explicitly evaluate ctx.Err(). Pressing ESC or invoking BackToMenuMsg now immediately triggers termination, preventing orphaned background worker loops.

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.

Enhancement: Add Rate Limiting and Duplicate Request Prevention for Repository Analysis Requests

1 participant