{"record":{"id":"f0a3809796faf40e","repo":"apache/druid","slug":"could-not-create-role-s-due-to-concurrent-updat","errorCode":null,"errorMessage":"Could not create role [%s] due to concurrent update contention.","messagePattern":"Could not create role \\[(.+?)\\] due to concurrent update contention\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":500,"severity":"error","filePath":"extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java","lineNumber":745,"sourceCode":"  }\n\n  private void createRoleInternal(String prefix, String roleName)\n  {\n    int attempts = 0;\n    while (attempts < numRetries) {\n      if (createRoleOnce(prefix, roleName)) {\n        return;\n      } else {\n        attempts++;\n      }\n      try {\n        Thread.sleep(ThreadLocalRandom.current().nextLong(UPDATE_RETRY_DELAY));\n      }\n      catch (InterruptedException ie) {\n        throw new RuntimeException(ie);\n      }\n    }\n    throw new ISE(\"Could not create role [%s] due to concurrent update contention.\", roleName);\n  }\n\n  private void deleteRoleInternal(String prefix, String roleName)\n  {\n    int attempts = 0;\n    while (attempts < numRetries) {\n      if (deleteRoleOnce(prefix, roleName)) {\n        return;\n      } else {\n        attempts++;\n      }\n      try {\n        Thread.sleep(ThreadLocalRandom.current().nextLong(UPDATE_RETRY_DELAY));\n      }\n      catch (InterruptedException ie) {\n        throw new RuntimeException(ie);\n      }\n    }","sourceCodeStart":727,"sourceCodeEnd":763,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java#L727-L763","documentation":"Creation of a basic authorizer role failed after numRetries compare-and-swap attempts because a concurrent writer kept updating the role/user metadata maps first. The updater retries the whole createRoleInternal operation with randomized delays, then throws this ISE naming the contested role. It indicates contention on metadata storage, not an invalid role name.","triggerScenarios":"Calling createRole while another client repeatedly mutates authorization metadata (users, roles, group mappings) for the same authorizer prefix, so every CAS in tryUpdateGroupMappingMap/userMap fails through all retries.","commonSituations":"Provisioning scripts creating many roles in parallel from several machines; concurrent role creation and user-role assignment from different admin tools; leader election races in multi-coordinator deployments.","solutions":["Retry createRole after a delay; contention is usually transient.","Confirm only one coordinator leader writes basic-security metadata.","Serialize role provisioning operations across scripts.","Increase numRetries to tolerate bursts of concurrent updates.","Investigate metadata storage performance/latency."],"exampleFix":"// before\nCompletableFuture.allOf(roles.map(r -> supplyAsync(() -> client.createRole(prefix, r))))\n                .join();\n// after: sequential creation with retry\nroles.forEach(r -> RetryUtils.retry(() -> client.createRole(prefix, r),\n                                    e -> e instanceof IllegalStateException,\n                                    MAX_ATTEMPTS));","handlingStrategy":"retry","validationCode":"// Skip creation if the role already exists\nMap<String, BasicAuthorizerRole> roles =\n    BasicAuthUtils.deserializeAuthorizerRoleMap(mapper, getCurrentRoleMapBytes(prefix));\nif (roles.containsKey(roleName)) return;","typeGuard":null,"tryCatchPattern":"try {\n  updater.createRole(prefix, roleName);\n} catch (IJSE e) {\n  RetryUtils.retry(() -> updater.createRole(prefix, roleName),\n                   ex -> ex instanceof IllegalStateException, MAX_ATTEMPTS);\n}","preventionTips":["Create roles sequentially in provisioning scripts","Verify leadership before writes","Use retry-with-backoff wrappers around role mutations","Tune numRetries upward for concurrent environments","Keep metadata store latency low"],"tags":["druid","basic-security","zookeeper","concurrency"],"backgroundTag":"concurrent-update-contention","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}