{"record":{"id":"e22cc2d1fa14be49","repo":"apache/druid","slug":"could-not-delete-role-s-due-to-concurrent-updat","errorCode":null,"errorMessage":"Could not delete role [%s] due to concurrent update contention.","messagePattern":"Could not delete 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":764,"sourceCode":"  }\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    }\n    throw new ISE(\"Could not delete role [%s] due to concurrent update contention.\", roleName);\n  }\n\n  private void assignUserRoleInternal(String prefix, String userName, String roleName)\n  {\n    int attempts = 0;\n    while (attempts < numRetries) {\n      if (assignUserRoleOnce(prefix, userName, 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":746,"sourceCodeEnd":782,"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#L746-L782","documentation":"Deletion of a basic authorizer role failed because all numRetries compare-and-swap attempts on the authorization metadata maps were beaten by concurrent writers. The updater re-reads and re-applies the role removal on each retry; persistent failure raises this ISE with the role name. It reflects update contention in metadata storage rather than a missing role.","triggerScenarios":"Calling deleteRole while other clients continuously mutate the same authorizer prefix's metadata, so tryUpdateGroupMappingMap CAS never succeeds within the retry budget.","commonSituations":"Parallel role cleanup by multiple automation jobs; role deletion racing with permission updates or user-role unassignments; multi-writer misconfiguration after coordinator failover.","solutions":["Retry deleteRole after a short backoff.","Ensure single-writer semantics: only the elected coordinator should mutate basic-security metadata.","Serialize role cleanup across tooling and environments.","Increase numRetries if concurrent admin operations are expected.","Check metadata storage health and latency."],"exampleFix":"// before\nclient.deleteRole(prefix, roleName);\n// after\nfor (int i = 0; i < 5; i++) {\n  try { client.deleteRole(prefix, roleName); return; }\n  catch (IJSE e) { Thread.sleep(1000); }\n}\nthrow new IllegalStateException(\"deleteRole still contended: \" + roleName);","handlingStrategy":"retry","validationCode":"// Confirm role exists under this prefix before deleting\nMap<String, BasicAuthorizerRole> roles =\n    BasicAuthUtils.deserializeAuthorizerRoleMap(mapper, getCurrentRoleMapBytes(prefix));\nif (!roles.containsKey(roleName)) return;","typeGuard":null,"tryCatchPattern":"try {\n  updater.deleteRole(prefix, roleName);\n} catch (IJSE e) {\n  await.atMost(Duration.ofSeconds(10)).untilAsserted(\n      () -> updater.deleteRole(prefix, roleName));\n}","preventionTips":["Avoid parallel role cleanup jobs","Single coordinator writer only","Add backoff between delete attempts","Increase numRetries if needed","Monitor CAS failure rates via logs"],"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"}