{"record":{"id":"f6589410ea1d282c","repo":"risingwavelabs/risingwave","slug":"user-was-concurrently-dropped","errorCode":null,"errorMessage":"user {} was concurrently dropped","messagePattern":"user (.+?) was concurrently dropped","errorType":"exception","errorClass":"MetaError","httpStatus":null,"severity":"warning","filePath":"src/meta/src/controller/utils.rs","lineNumber":590,"sourceCode":"{\n    let count = Object::find_by_id(job_id).count(db).await?;\n    if count == 0 {\n        return Err(MetaError::cancelled(format!(\n            \"job {} might be cancelled manually or by recovery\",\n            job_id\n        )));\n    }\n    Ok(())\n}\n\n/// `ensure_user_id` ensures the existence of target user in the cluster.\npub async fn ensure_user_id<C>(user_id: UserId, db: &C) -> MetaResult<()>\nwhere\n    C: ConnectionTrait,\n{\n    let count = User::find_by_id(user_id).count(db).await?;\n    if count == 0 {\n        return Err(anyhow!(\"user {} was concurrently dropped\", user_id).into());\n    }\n    Ok(())\n}\n\n/// `check_database_name_duplicate` checks whether the database name is already used in the cluster.\npub async fn check_database_name_duplicate<C>(name: &str, db: &C) -> MetaResult<()>\nwhere\n    C: ConnectionTrait,\n{\n    let count = Database::find()\n        .filter(database::Column::Name.eq(name))\n        .count(db)\n        .await?;\n    if count > 0 {\n        assert_eq!(count, 1);\n        return Err(MetaError::catalog_duplicated(\"database\", name));\n    }\n    Ok(())","sourceCodeStart":572,"sourceCodeEnd":608,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/controller/utils.rs#L572-L608","documentation":"`ensure_user_id` verifies that a user referenced by an operation still exists by counting rows in the user table. If the count is zero the operation aborts with \"user {} was concurrently dropped\", because another transaction deleted the user between the caller's earlier lookup and this check. It is a deliberate race-detection guard, not a plain not-found error.","triggerScenarios":"Calling `ensure_user_id` (from alter_owner, alter_secret, create_database, create_schema, create_subscription_catalog, create_source) with a UserId whose row was deleted by a concurrent DROP USER while the first operation was in flight.","commonSituations":"Two sessions racing: one runs ALTER ... OWNER TO <user> while another drops that user; automation scripts issuing parallel DDL against the same user; retry logic replaying an operation after the user was removed.","solutions":["Re-run the operation; it will now fail fast with a normal 'user not found' error that can be handled cleanly.","Before executing ownership/creation DDL, verify the target user exists and avoid concurrently dropping it (serialize DDL on the same user in your tooling).","If this occurs in tests or CI, add synchronization between the DROP USER and the dependent DDL statements.","Use a single transaction that both checks and uses the user reference so the meta layer's locking prevents the race."],"exampleFix":"// before\nlet user = get_user_by_name(name).await?;\nspawn_drop_user(user.id); // races\ncreate_database(owner_id: user.id).await?;\n// after\nlet user = get_user_by_name(name).await?;\ncreate_database(owner_id: user.id).await?; // drop only after dependents are removed","handlingStrategy":"retry","validationCode":"// Pre-check user existence before dependent DDL\nif User::find_by_id(user_id).count(db).await? == 0 {\n    return Err(\"user no longer exists; abort DDL\");\n}","typeGuard":null,"tryCatchPattern":"match ensure-dependent-ddl().await {\n    Err(e) if e.to_string().contains(\"was concurrently dropped\") => retry_or_skip(),\n    other => other,\n}","preventionTips":["Serialize user DDL and dependent object DDL in your tooling","Avoid dropping users while ownership/creation operations are in flight","In CI, synchronize DROP USER with any dependent statements"],"tags":["meta","concurrency","user"],"backgroundTag":"entity-not-found","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}