Skip to content

fix: slim packet loss history payloads#222

Merged
s0up4200 merged 1 commit into
developfrom
fix/packetloss-history-cutover
Apr 11, 2026
Merged

fix: slim packet loss history payloads#222
s0up4200 merged 1 commit into
developfrom
fix/packetloss-history-cutover

Conversation

@s0up4200
Copy link
Copy Markdown
Collaborator

@s0up4200 s0up4200 commented Apr 10, 2026

Summary

  • split packet-loss history into paginated summary rows plus lazy per-result MTR detail
  • remove mtrData from the normal history payload and keep the existing expand UI via a detail fetch
  • add monitor history ordering/index changes and refresh the frontend cache without broad history refetches

Why

  • 1-minute packet-loss/MTR history was loading blob-heavy rows into the normal history path and growing memory pressure over time
  • this keeps the current UI behavior while hard-cutting the main history payload down to summary data

Testing

  • pnpm -C web lint
  • pnpm -C web build
  • Other: go test ./...

Screenshots (if UI)

  • N/A

Checklist

  • Diff is scoped to the issue (no unrelated changes)
  • No new lockfiles (pnpm only)
  • No generated/dist files committed
  • AI-assisted changes reviewed and edited by a human

Summary by CodeRabbit

Release Notes

  • New Features

    • Packet loss history now uses pagination with "Load More" functionality for faster initial page loads
    • MTR details load on-demand when expanding rows, improving performance and reducing data transfer
  • Performance Improvements

    • Optimized packet loss history queries with new database indexes for faster retrieval

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

This PR implements packet-loss history pagination by decoupling MTR data from summary responses. The backend introduces paginated summary endpoints (without MTR data) and on-demand detail endpoints (with full MTR data). Database indexes improve query performance. Frontend migrates to infinite scroll with lazy-loaded detail fetching.

Changes

Cohort / File(s) Summary
Planning & Documentation
docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md
New cutover plan document defining backend/frontend changes, migration strategy, task mapping, and verification checklist for decoupling packet-loss history from MTR data.
Database Migrations
internal/database/migrations/postgres/021_packetloss_history_summary_index_postgres.sql, internal/database/migrations/sqlite/021_packetloss_history_summary_index.sql
Added indexes on (monitor_id, created_at DESC, id DESC) for both PostgreSQL and SQLite to optimize paginated history queries.
Database Interface & Implementation
internal/database/database.go, internal/database/packetloss.go
Replaced GetPacketLossResults(monitorID, limit) with GetPacketLossResults(monitorID, page, limit) returning paginated wrapper. Added GetPacketLossResultDetail(monitorID, resultID) for on-demand MTR data retrieval. Implementation now counts total, normalizes pagination params, and returns summary-only by default.
Backend Handlers & Routing
internal/handlers/packetloss.go, internal/server/server.go
Updated GetMonitorHistory to support page query parameter with adjusted limits (default 25, cap 100). Added new GetMonitorHistoryDetail handler and registered route /packetloss/monitors/:id/history/:resultId.
Type Definitions
internal/types/types.go, web/src/types/types.ts
Added backend types PacketLossResultSummary and PaginatedPacketLossResults. Frontend separated PacketLossResult (summary, no MTR data) from new PacketLossResultDetail (includes MTR data).
Frontend API Layer
web/src/api/packetloss.ts
Updated getPacketLossHistory to paginated endpoint (page, limit params, returns PaginatedResponse). Added new getPacketLossHistoryDetail function for on-demand detail fetching.
Frontend Components
web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx, web/src/components/speedtest/packetloss/PacketLossMonitorDetails.tsx, web/src/components/speedtest/TracerouteTab.tsx
Migrated history to infinite scroll pagination via useInfiniteQuery. MonitorResultsTable now supports server-driven pagination, lazy-loads MTR details on row expansion with caching/error states, and renders "Load More" button. Parent component wires pagination state and detail fetching.
Frontend Utilities
web/src/components/speedtest/packetloss/constants/packetLossConstants.ts, web/src/components/speedtest/packetloss/hooks/usePacketLossMonitorStatus.ts
Added PACKET_LOSS_HISTORY_PAGE_SIZE constant (30 items). Enhanced usePacketLossMonitorStatus with mergeLatestHistoryPage helper to merge new history pages into infinite query cache, supporting conditional cache updates on test completion.
Tests
internal/database/cascade_delete_integration_test.go, internal/database/packetloss_integration_test.go
Updated existing tests to use new paginated signature. Added TestGetPacketLossResultsStablePagination (deterministic ordering with tied timestamps) and TestGetPacketLossResultDetail (detail endpoint with MTR data).

Sequence Diagram

sequenceDiagram
    participant User
    participant FrontendUI as Frontend<br/>(MonitorResultsTable)
    participant APILayer as API Layer<br/>(getPacketLossHistory)
    participant Backend as Backend<br/>(Handler)
    participant Database as Database

    User->>FrontendUI: Load monitor history
    FrontendUI->>APILayer: getPacketLossHistory(page=1)
    APILayer->>Backend: GET /packetloss/monitors/:id/history?page=1&limit=25
    Backend->>Database: GetPacketLossResults(monitorID, page=1, limit=25)
    Database-->>Backend: PaginatedPacketLossResults{data, total, page, limit}
    Backend-->>APILayer: 200 JSON (summary, no MTR data)
    APILayer-->>FrontendUI: PaginatedResponse with summary list
    FrontendUI->>FrontendUI: Render rows, show "Load More"

    User->>FrontendUI: Expand row (view MTR details)
    FrontendUI->>APILayer: getPacketLossHistoryDetail(resultId)
    APILayer->>Backend: GET /packetloss/monitors/:id/history/:resultId
    Backend->>Database: GetPacketLossResultDetail(monitorID, resultID)
    Database-->>Backend: PacketLossResult{data, MTRData}
    Backend-->>APILayer: 200 JSON (with MTR data)
    APILayer-->>FrontendUI: PacketLossResultDetail (cache)
    FrontendUI->>FrontendUI: Parse MTRData, render detail view

    User->>FrontendUI: Scroll & click "Load More"
    FrontendUI->>APILayer: getPacketLossHistory(page=2)
    APILayer->>Backend: GET /packetloss/monitors/:id/history?page=2&limit=25
    Backend->>Database: GetPacketLossResults(monitorID, page=2, limit=25)
    Database-->>Backend: Next page results
    Backend-->>APILayer: 200 JSON
    APILayer-->>FrontendUI: Append next page to list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Hop! Skip! Paginate with glee,
Split the history, let details be free,
Lazy load the MTR when you need to expand,
Lighter summaries load swift as planned!
No more bloat—just peek when you please,
Performance hops through the database breeze! 🌙✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: slim packet loss history payloads' is clear and specific, accurately summarizing the main change of reducing payload size in the packet loss history endpoint.
Description check ✅ Passed The description covers all required sections: Summary, Why, Testing (with checkboxes), and Checklist. It provides clear context and rationale for the changes, though one test checkbox (web lint) remains unchecked.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/packetloss-history-cutover

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.

@s0up4200 s0up4200 marked this pull request as ready for review April 10, 2026 15:00
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 (2)
docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md (1)

15-43: Replace absolute paths with repository-relative paths.

The file map contains absolute paths like /Users/soup/.codex/worktrees/4fd8/netronome/internal/types/types.go which are specific to a developer's local machine. These should be relative paths from the repository root for portability.

Example fix for one entry
-- Modify: `/Users/soup/.codex/worktrees/4fd8/netronome/internal/types/types.go`
+- Modify: `internal/types/types.go`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md` around lines
15 - 43, The plan file
docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md contains
absolute developer-local paths (e.g.
/Users/soup/.codex/worktrees/4fd8/netronome/internal/...) — replace all such
absolute paths with repository-relative paths from the repo root (e.g.
internal/types/types.go, internal/database/packetloss.go, web/src/components/...
etc.), ensuring both modified and added entries (like the new migrations and web
files) use the relative form; search for any lines beginning with “/Users/” in
that file and update them consistently so the plan is portable.
web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx (1)

280-293: Button shows hover styles while disabled.

The "Load More" button has disabled={isFetchingMore} but the className lacks disabled: variants, so hover effects remain active on disabled state. This can mislead users into thinking the button is interactive.

♻️ Suggested enhancement
              className="px-4 py-2 bg-gray-200/30 dark:bg-gray-800/30 border border-gray-300/50 dark:border-gray-900/50 text-gray-600/50 dark:text-gray-300/50 hover:text-gray-700 dark:hover:text-gray-300 rounded-lg hover:bg-gray-300/50 dark:hover:bg-gray-800/50 transition-colors"
+             className="px-4 py-2 bg-gray-200/30 dark:bg-gray-800/30 border border-gray-300/50 dark:border-gray-900/50 text-gray-600/50 dark:text-gray-300/50 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed hover:enabled:text-gray-700 dark:hover:enabled:text-gray-300 hover:enabled:bg-gray-300/50 dark:hover:enabled:bg-gray-800/50"

The same applies to the mobile "Load More" button at lines 419-432.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx`
around lines 280 - 293, The Load More button in MonitorResultsTable.tsx is still
showing hover styles when disabled via disabled={isFetchingMore}; update the
button rendering (both desktop and mobile instances around the onLoadMore
handlers) to either add Tailwind disabled: utility classes (e.g.,
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent
disabled:hover:text-current) to the className or conditionally remove hover
classes when isFetchingMore is true so hover styles and pointer interactions are
suppressed; ensure both the button using onLoadMore and its mobile counterpart
use the same disabled styling logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx`:
- Around line 252-265: The two expanded-state rows render with colSpan={8}
though the table only has 7 columns; update both occurrences in
MonitorResultsTable.tsx (the conditional blocks referencing result.usedMtr &&
isExpanded && isLoadingDetail and result.usedMtr && isExpanded &&
hasDetailError) to use colSpan={7} so the loading and error rows span the
correct number of columns.

---

Nitpick comments:
In `@docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md`:
- Around line 15-43: The plan file
docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md contains
absolute developer-local paths (e.g.
/Users/soup/.codex/worktrees/4fd8/netronome/internal/...) — replace all such
absolute paths with repository-relative paths from the repo root (e.g.
internal/types/types.go, internal/database/packetloss.go, web/src/components/...
etc.), ensuring both modified and added entries (like the new migrations and web
files) use the relative form; search for any lines beginning with “/Users/” in
that file and update them consistently so the plan is portable.

In `@web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx`:
- Around line 280-293: The Load More button in MonitorResultsTable.tsx is still
showing hover styles when disabled via disabled={isFetchingMore}; update the
button rendering (both desktop and mobile instances around the onLoadMore
handlers) to either add Tailwind disabled: utility classes (e.g.,
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent
disabled:hover:text-current) to the className or conditionally remove hover
classes when isFetchingMore is true so hover styles and pointer interactions are
suppressed; ensure both the button using onLoadMore and its mobile counterpart
use the same disabled styling logic.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ad4fe33-0f46-429e-8865-5a6d25634f80

📥 Commits

Reviewing files that changed from the base of the PR and between bf756a6 and 31f4969.

📒 Files selected for processing (17)
  • docs/superpowers/plans/2026-04-09-packetloss-history-cutover.md
  • internal/database/cascade_delete_integration_test.go
  • internal/database/database.go
  • internal/database/migrations/postgres/021_packetloss_history_summary_index_postgres.sql
  • internal/database/migrations/sqlite/021_packetloss_history_summary_index.sql
  • internal/database/packetloss.go
  • internal/database/packetloss_integration_test.go
  • internal/handlers/packetloss.go
  • internal/server/server.go
  • internal/types/types.go
  • web/src/api/packetloss.ts
  • web/src/components/speedtest/TracerouteTab.tsx
  • web/src/components/speedtest/packetloss/PacketLossMonitorDetails.tsx
  • web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx
  • web/src/components/speedtest/packetloss/constants/packetLossConstants.ts
  • web/src/components/speedtest/packetloss/hooks/usePacketLossMonitorStatus.ts
  • web/src/types/types.ts

Comment on lines +252 to +265
{result.usedMtr && isExpanded && isLoadingDetail && (
<tr className="bg-gray-100/50 dark:bg-gray-900/50">
<td colSpan={8} className="p-4 text-sm text-gray-600 dark:text-gray-400">
Loading MTR details...
</td>
</tr>
)}
{result.usedMtr && isExpanded && hasDetailError && (
<tr className="bg-gray-100/50 dark:bg-gray-900/50">
<td colSpan={8} className="p-4 text-sm text-red-600 dark:text-red-400">
Failed to load MTR details.
</td>
</tr>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect colSpan value — table has 7 columns, not 8.

The loading and error rows use colSpan={8}, but the table header defines only 7 columns (Time, Loss, Avg RTT, Min RTT, Max RTT, Sent/Recv, Mode). This doesn't break rendering but is semantically incorrect.

🔧 Proposed fix
                  {result.usedMtr && isExpanded && isLoadingDetail && (
                    <tr className="bg-gray-100/50 dark:bg-gray-900/50">
-                      <td colSpan={8} className="p-4 text-sm text-gray-600 dark:text-gray-400">
+                      <td colSpan={7} className="p-4 text-sm text-gray-600 dark:text-gray-400">
                        Loading MTR details...
                      </td>
                    </tr>
                  )}
                  {result.usedMtr && isExpanded && hasDetailError && (
                    <tr className="bg-gray-100/50 dark:bg-gray-900/50">
-                      <td colSpan={8} className="p-4 text-sm text-red-600 dark:text-red-400">
+                      <td colSpan={7} className="p-4 text-sm text-red-600 dark:text-red-400">
                        Failed to load MTR details.
                      </td>
                    </tr>
                  )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{result.usedMtr && isExpanded && isLoadingDetail && (
<tr className="bg-gray-100/50 dark:bg-gray-900/50">
<td colSpan={8} className="p-4 text-sm text-gray-600 dark:text-gray-400">
Loading MTR details...
</td>
</tr>
)}
{result.usedMtr && isExpanded && hasDetailError && (
<tr className="bg-gray-100/50 dark:bg-gray-900/50">
<td colSpan={8} className="p-4 text-sm text-red-600 dark:text-red-400">
Failed to load MTR details.
</td>
</tr>
)}
{result.usedMtr && isExpanded && isLoadingDetail && (
<tr className="bg-gray-100/50 dark:bg-gray-900/50">
<td colSpan={7} className="p-4 text-sm text-gray-600 dark:text-gray-400">
Loading MTR details...
</td>
</tr>
)}
{result.usedMtr && isExpanded && hasDetailError && (
<tr className="bg-gray-100/50 dark:bg-gray-900/50">
<td colSpan={7} className="p-4 text-sm text-red-600 dark:text-red-400">
Failed to load MTR details.
</td>
</tr>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/speedtest/packetloss/components/MonitorResultsTable.tsx`
around lines 252 - 265, The two expanded-state rows render with colSpan={8}
though the table only has 7 columns; update both occurrences in
MonitorResultsTable.tsx (the conditional blocks referencing result.usedMtr &&
isExpanded && isLoadingDetail and result.usedMtr && isExpanded &&
hasDetailError) to use colSpan={7} so the loading and error rows span the
correct number of columns.

@s0up4200
Copy link
Copy Markdown
Collaborator Author

@codex review

@s0up4200 s0up4200 merged commit d698b47 into develop Apr 11, 2026
13 checks passed
@s0up4200 s0up4200 deleted the fix/packetloss-history-cutover branch April 11, 2026 19:45
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