{"record":{"id":"23cee2398edce545","repo":"SonarSource/sonarqube","slug":"the-maximum-number-of-concurrent-calls-for-this-we","errorCode":null,"errorMessage":"The maximum number of concurrent calls for this web service has been reached","messagePattern":"The maximum number of concurrent calls for this web service has been reached","errorType":"http","errorClass":"ServerException","httpStatus":503,"severity":"warning","filePath":"server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/ConcurrentCallsLimitInterceptor.java","lineNumber":50,"sourceCode":" * When the maximum number of concurrent calls is reached, returns HTTP 503.\n */\npublic class ConcurrentCallsLimitInterceptor implements ActionInterceptor {\n\n  private final ConcurrentHashMap<String, Semaphore> semaphores = new ConcurrentHashMap<>();\n  private final ThreadLocal<Semaphore> acquiredSemaphore = new ThreadLocal<>();\n\n  @Override\n  public void preAction(WebService.Action action, Request request) {\n    ConcurrentCallsLimit annotation = action.handler().getClass().getAnnotation(ConcurrentCallsLimit.class);\n    if (annotation == null) {\n      return;\n    }\n    String key = action.path() + \"/\" + action.key();\n    Semaphore semaphore = semaphores.computeIfAbsent(key, k -> new Semaphore(annotation.value()));\n    if (semaphore.tryAcquire()) {\n      acquiredSemaphore.set(semaphore);\n    } else {\n      throw new ServerException(503, \"The maximum number of concurrent calls for this web service has been reached\");\n    }\n  }\n\n  @Override\n  public void postAction(WebService.Action action, Request request) {\n    Semaphore semaphore = acquiredSemaphore.get();\n    if (semaphore != null) {\n      semaphore.release();\n      acquiredSemaphore.remove();\n    }\n  }\n}\n","sourceCodeStart":32,"sourceCodeEnd":63,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/ConcurrentCallsLimitInterceptor.java#L32-L63","documentation":"ConcurrentCallsLimitInterceptor.preAction enforces the @MaxConcurrentCalls-permit limit declared on a web service action. When all permits of the action's semaphore are held, it throws ServerException 503 telling the client the web service has reached its concurrency cap.","triggerScenarios":"Sending more simultaneous requests than the action's annotation.value() permits to an endpoint annotated with a concurrency limit (key = action.path() + '/' + action.key()) while existing calls are still in flight.","commonSituations":"Load tests or parallel CI scans hammering a rate-limited endpoint; a slow downstream dependency causing calls to pile up and exhaust the semaphore; burst retries after a timeout.","solutions":["Retry the request after a delay with backoff; 503 here is transient once other calls finish.","Reduce client-side concurrency (limit worker/parallelism count) below the server limit.","Investigate why in-flight calls are slow and holding permits (DB, ES, downstream latency).","If the limit is too low for legitimate traffic, raise the @MaxConcurrentCalls value on the server action."],"exampleFix":"// before\nfor (Project p : projects) executor.submit(() -> callSlowApi(p)); // 100 parallel\n// after\nExecutorService pool = Executors.newFixedThreadPool(5); // stay under the limit\nfor (Project p : projects) pool.submit(() -> retryOn503(() -> callSlowApi(p)));","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if (response.code() == 503 && response.message().contains(\"concurrent calls\")) { Thread.sleep(backoff); retry(); }","preventionTips":["Cap client parallelism below the server's declared limit","Use exponential backoff with jitter on 503","Monitor endpoint latency to spot permit exhaustion early"],"tags":["http-503","concurrency","rate-limiting","java"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}