APPENG-5698: add per-user concurrent request limiting to analysis queue - #286
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
0e1346a to
c503fdf
Compare
…is queue A single authenticated user could submit enough large SBOM analysis requests to fill every slot in the global active-request queue, starving all other users for up to the queue timeout (T-011). RequestQueueService now tracks active requests per user identity in addition to the existing global count, and rejects a user's new submission with a UserQueueExceededException (mapped to HTTP 429) once they hit a configurable per-user limit (exploit-iq.queue.max-active-per-user, default 5). Pending requests retain their owning user so the limit is still enforced when they are promoted from the pending queue to active. - RequestQueueService: add max-active-per-user config, per-user active tracking (activeByUser/activeUserById), and PendingRequest record to carry user identity through the pending queue - ReportService: pass resolved user identity into queue() for both submit() and retry(), and surface UserQueueExceededException as a distinct report error state - ReportEndpoint/ProductEndpoint: map UserQueueExceededException to HTTP 429 with a dedicated message - ComponentProcessingService: distinguish per-user limit errors in component submission failure messages - AppConfig/application.properties/docs: document the new max-active-per-user setting reduce quarkus.http.limits.max-body-size from 300M to 90M. Maximum SPDX SBOM product in redhat is 87MB… ( openshift version 4.15 ). Add unit-tests Fix review note 2 - sync concerns Code review fix: Update active, activeUserById, activeByUser ConcurrentHashMap during application startup On applicatino crash/stopped - init the active, activeUserById, activeByUser ConcurrentHashMap with the current scan on the system. Code review fix: Use @ServerExceptionMapper for queue exceptions in ReportEndpoint Replace duplicated try/catch blocks for RequestQueueExceededException and UserQueueExceededException in process(), newRpmReport(), and submit() with class-level @ServerExceptionMapper methods, matching the pattern already used in ProductEndpoint. Ensures all three endpoints return the same 429 status and error message instead of process()/newRpmReport() using a hardcoded message while submit() returned e.getMessage(). Fix review note bug fix: Also calculate pending user request when checking user limits Review fix: enhance syncrnoizaton using user logk and not generic logs Fix code review: Remove reflection from unit-tests rebase fixes Code review fixes fix unit-tests Add debug logs
c503fdf to
bb8256f
Compare
zvigrinberg
left a comment
There was a problem hiding this comment.
@gnetanel Very good job...
Only 2 minor issues left..
Please take a look..
Thanks.
| public void deleted(String id) { | ||
| LOGGER.debugf("Removed deleted report %s. Removing from active queue.", id); | ||
| active.remove(id); | ||
| removeActive(id); |
There was a problem hiding this comment.
@gnetanel You're assuming that only active reports can be deleted, like it was before.
Let's keep it that way, but add documentation so it will be known that we're not supporting pending reports removal.
| } catch (Exception e) { | ||
| // Release the slot reserved above so a failing onAdmitted callback (e.g. a failed DB | ||
| // write) doesn't permanently consume queue capacity. | ||
| if (sendNow) { |
There was a problem hiding this comment.
@gnetanel I'm missing here a warning/error log message that explains what went wrong on the onAdmiited.run() callback invocation.. ( before it's removing the slots from the queues).
zvigrinberg
left a comment
There was a problem hiding this comment.
@gnetanel LGTM Approved.
Thanks for the great work!.
A single authenticated user could submit enough large SBOM analysis
requests to fill every slot in the global active-request queue,
starving all other users for up to the queue timeout (T-011).
RequestQueueService now tracks active requests per user identity in
addition to the existing global count, and rejects a user's new
submission with a UserQueueExceededException (mapped to HTTP 429)
once they hit a configurable per-user limit
(exploit-iq.queue.max-active-per-user, default 5). Pending requests
retain their owning user so the limit is still enforced when they are
promoted from the pending queue to active.
RequestQueueService: add max-active-per-user config, per-user active
tracking (activeByUser/activeUserById), and PendingRequest record to
carry user identity through the pending queue
ReportService: pass resolved user identity into queue() for both
submit() and retry(), and surface UserQueueExceededException as a
distinct report error state
ReportEndpoint/ProductEndpoint: map UserQueueExceededException to
HTTP 429 with a dedicated message
ComponentProcessingService: distinguish per-user limit errors in
component submission failure messages
AppConfig/application.properties/docs: document the new
max-active-per-user setting
In addition - max-body-size has been reduced from 300M to 90M