-
Notifications
You must be signed in to change notification settings - Fork 4
AIML-365: Eliminate N+1 queries in get_route_coverage [STACKED] #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
POST /route/filter requires sessionID or metadata filter - cannot send empty body. Revert to GET/POST logic but add expand=observations to GET URL to include observations inline and eliminate N+1 queries.
Lightweight response wrapper for route coverage that holds: - API status (success, messages) - Aggregate statistics (totalRoutes, exercisedCount, discoveredCount, coveragePercent) - Vulnerability totals (totalVulnerabilities, totalCriticalVulnerabilities) - List of RouteLight objects Part of route coverage N+1 elimination effort. Task 2 of 5.
Implements mapper component that: - Transforms Route -> RouteLight (removes redundant app, servers, routeDetailsResponse, routeHashString fields) - Transforms RouteCoverageResponse -> RouteCoverageResponseLight (computes aggregate statistics: coverage percent, exercised/discovered counts, vulnerability totals) - Handles null safety for lists (returns empty list instead of null) Part of route coverage N+1 elimination effort.
…nses Update GetRouteCoverageTool to return RouteCoverageResponseLight instead of the full RouteCoverageResponse. This reduces payload size for AI agents by eliminating redundant fields (app, servers, routeHashString, routeDetailsResponse) and adds aggregate statistics (exercisedCount, discoveredCount, coveragePercent, totalVulnerabilities, totalCriticalVulnerabilities). - Add RouteMapper dependency via constructor injection - Change return type from RouteCoverageResponse to RouteCoverageResponseLight - Update unit tests to use record accessors instead of getters - Add test for aggregate statistics computation - Update integration tests to work with RouteLight records
- RouteLight: add compact constructor to ensure observations is never null - RouteMapper: use case-insensitive status comparison for robustness - RouteMapper: count DISCOVERED explicitly instead of subtracting from total - RouteMapper: round coveragePercent to 2 decimal places - MetadataJsonFilterSpec: add validation error for empty array values
…putation - Use single-pass loop in RouteMapper for aggregate statistics instead of 4 separate stream operations (minor performance improvement) - Remove unused routeDetailsResponse field from Route class - Remove getRouteDetails() method from SDKExtension (N+1 pattern eliminated) - Delete RouteDetailsResponse class (no longer needed) - Update tests to remove references to deleted method
6006a0f to
f838feb
Compare
- Delete PaginationHandler.java and PaginationHandlerTest.java (unused) - Add .tldr/ and .tldrignore to gitignore (TLDR tool artifacts) - Update CLAUDE.md with PR description guidance
JacobMagesHaskinsContrast
approved these changes
Jan 12, 2026
Alex-Contrast
approved these changes
Jan 12, 2026
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Why
The
get_route_coveragetool had severe performance issues caused by an N+1 query pattern:Root causes:
expand=observations, forcing the N+1 loopWhat Changed
1. API Call Optimization
2. Response Size Optimization (~60% reduction)
Routeobjects with redundantapp,servers,routeDetailsResponseper routeRouteLightrecords with only essential fields3. New Aggregate Statistics
Pre-computed aggregates eliminate client-side computation:
totalRoutes- Total route countexercisedCount/discoveredCount- Route status breakdowncoveragePercent- Percentage of exercised routes (0.0-100.0, rounded to 2 decimals)totalVulnerabilities/totalCriticalVulnerabilities- Summed across all routes4. Input Validation Improvement
sessionMetadataFilters{"branch":[]}now returns clear error instead of silent failure5. Other changes
PaginationHandler.javaand related test. (Should have been deleted in prior PRs, but was missed).How
The implementation follows a layered approach:
Layer 1: SDK - Always Include Observations Inline
File:
SDKExtension.java(+20/-52 lines)The key insight was that TeamServer supports
expand=observationsto return observations inline, but the SDK wasn't using it:Also removed the now-unused
getRouteDetails()method that powered the N+1 loop.Layer 2: Data - Add Observations Fields to Route Model
File:
Route.java(+6/-2 lines)Added fields to capture inline observations:
Removed the
routeDetailsResponsefield that held N+1 query results.Layer 3: Data - Create Lightweight Response Models
Files:
RouteLight.java(56 lines),RouteCoverageResponseLight.java(43 lines)New immutable Java records that strip redundant data:
Fields removed from response:
app- Application data (already known from request context)servers- Full server list (summary count retained asserversTotal)routeDetailsResponse- Legacy field, now obsoleterouteHashString- Duplicate ofrouteHashLayer 4: Mapper - Transform to Light Response
File:
RouteMapper.java(98 lines)New
@Componentmapper that:Route→RouteLight(strips redundant fields, null-safe)RouteCoverageResponse→RouteCoverageResponseLightLayer 5: Tool - Remove N+1 Loop
File:
GetRouteCoverageTool.java(+11/-14 lines)The N+1 loop was completely removed:
Layer 6: Validation - Improve Error Messages
File:
MetadataJsonFilterSpec.java(+6/-1 lines)Added explicit validation for empty array values:
Step-by-Step Walkthrough
Phase 1: Enable Inline Observations (Commits 1-5)
Commit 1 (
b385d5c): AddedobservationsandtotalObservationsfields toRoute.javaso Gson can deserialize inline observations.Commit 2 (
c42c7d6): First attempt - switched to always use POST endpoint withexpand=observations. This worked for filtered requests.Commit 3 (
8bd993d): Removed the N+1 loop fromGetRouteCoverageToolsince observations are now inline.Commit 4 (
7a536d5): Tried using empty request object{}for unfiltered requests, but POST requires actual filter criteria.Commit 5 (
dd6ab8f): Final solution - use GET withexpand=observationsfor unfiltered requests, POST for filtered. Both include observations inline.Phase 2: Lightweight Response Models (Commits 6-9)
Commit 6 (
6f3af05): AddedRouteLight.javarecord with essential fields only.Commit 7 (
80162c4): AddedRouteCoverageResponseLight.javarecord with aggregate statistics fields.Commit 8 (
19718d2): AddedRouteMapper.javato transform full responses to light versions with computed aggregates.Commit 9 (
e3514c8): IntegratedRouteMapperintoGetRouteCoverageTool, changed return type toRouteCoverageResponseLight.Phase 3: Code Review Refinements (Commits 10-11)
Commit 10 (
e442af9): Applied code review feedback:RouteLight: Added compact constructor ensuring observations never nullRouteMapper: Case-insensitive status comparison, explicit DISCOVERED count, coverage percent rounded to 2 decimalsMetadataJsonFilterSpec: Added validation error for empty array valuesCommit 11 (
f838feb): Final optimizations:routeDetailsResponsefield fromRoutegetRouteDetails()method fromSDKExtensionRouteDetailsResponse.javaclass entirelyTesting
New Test Files
RouteTest.javaSDKExtensionTest.javaRouteLightTest.javaRouteCoverageResponseLightTest.javaRouteMapperTest.javaMetadataJsonFilterSpecTest.javaUpdated Test Files
GetRouteCoverageToolTest.javaGetRouteCoverageToolIT.javaRouteLightrecordsResults
Files Changed Summary