Merge pull request #691 from AppFlowy-IO/fix-user-creation-deadlock-2

Fix user creation deadlock 2
This commit is contained in:
Zack 2024-07-16 10:44:15 +08:00 committed by GitHub
commit 82bbee19a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 32 additions and 67 deletions

View File

@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT pg_advisory_xact_lock($1)",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "pg_advisory_xact_lock",
"type_info": "Void"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
null
]
},
"hash": "a06e1d9f6f95e4c4c2b98310ebddcc9d963cc033582bf2e945e8bf3a301b4247"
}

View File

@ -1,25 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n WITH ins_user AS (\n INSERT INTO af_user (uid, uuid, email, name)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT(email) DO NOTHING\n RETURNING uid\n ),\n owner_role AS (\n SELECT id FROM af_roles WHERE name = 'Owner'\n ),\n ins_workspace AS (\n INSERT INTO af_workspace (owner_uid)\n SELECT uid FROM ins_user\n RETURNING workspace_id, owner_uid\n ),\n ins_collab_member AS (\n INSERT INTO af_collab_member (uid, oid, permission_id)\n SELECT ins_workspace.owner_uid,\n ins_workspace.workspace_id::TEXT,\n (SELECT permission_id FROM af_role_permissions WHERE role_id = owner_role.id)\n FROM ins_workspace, owner_role\n )\n SELECT workspace_id FROM ins_workspace;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workspace_id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Int8",
"Uuid",
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "b4fa2e732c975fbb23c346981c4fb1e2e0aab6f3a1ac9c2fc99b4cdee9e58cbe"
}

View File

@ -0,0 +1,25 @@
{
"db_name": "PostgreSQL",
"query": "\n WITH ins_user AS (\n INSERT INTO af_user (uid, uuid, email, name)\n VALUES ($1, $2, $3, $4)\n RETURNING uid\n ),\n owner_role AS (\n SELECT id FROM af_roles WHERE name = 'Owner'\n ),\n ins_workspace AS (\n INSERT INTO af_workspace (owner_uid)\n SELECT uid FROM ins_user\n RETURNING workspace_id, owner_uid\n ),\n ins_collab_member AS (\n INSERT INTO af_collab_member (uid, oid, permission_id)\n SELECT ins_workspace.owner_uid,\n ins_workspace.workspace_id::TEXT,\n (SELECT permission_id FROM af_role_permissions WHERE role_id = owner_role.id)\n FROM ins_workspace, owner_role\n )\n SELECT workspace_id FROM ins_workspace;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workspace_id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Int8",
"Uuid",
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "ce3b2a3ddee0f420166719e39e0e4befa495bb6438ef88be860fa1e557bf9281"
}

View File

@ -12,6 +12,7 @@ lazy_static! {
password: std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap_or("password".to_string()),
}
};
static ref ADMIN_SIGN_IN_MUTEX: tokio::sync::Mutex<()> = tokio::sync::Mutex::new(());
}
#[derive(Clone, Debug)]
@ -32,7 +33,8 @@ pub async fn admin_user_client() -> Client {
web_sys::console::log_1(&msg.into());
}
admin_client
let _guard = ADMIN_SIGN_IN_MUTEX.lock().await;
let _is_new = admin_client
.sign_in_password(&ADMIN_USER.email, &ADMIN_USER.password)
.await
.unwrap();

View File

@ -1,5 +1,3 @@
use crate::pg_row::AFUserIdRow;
use app_error::AppError;
use futures_util::stream::BoxStream;
use sqlx::postgres::PgArguments;
use sqlx::types::JsonValue;
@ -7,6 +5,10 @@ use sqlx::{Arguments, Executor, PgPool, Postgres};
use tracing::{instrument, warn};
use uuid::Uuid;
use app_error::AppError;
use crate::pg_row::AFUserIdRow;
/// Updates the user's details in the `af_user` table.
///
/// This function allows for updating the user's name, email, and metadata based on the provided UUID.
@ -104,7 +106,6 @@ pub async fn create_user<'a, E: Executor<'a, Database = Postgres>>(
WITH ins_user AS (
INSERT INTO af_user (uid, uuid, email, name)
VALUES ($1, $2, $3, $4)
ON CONFLICT(email) DO NOTHING
RETURNING uid
),
owner_role AS (

View File

@ -23,28 +23,12 @@ pub async fn verify_token(access_token: &str, state: &AppState) -> Result<bool,
let user_uuid = uuid::Uuid::parse_str(&user.id)?;
let name = name_from_user_metadata(&user.user_metadata);
let is_new = !is_user_exist(&state.pg_pool, &user_uuid).await?;
if !is_new {
return Ok(false);
}
let mut txn = state
.pg_pool
.begin()
.await
.context("acquire transaction to verify token")?;
// To prevent concurrent creation of the same user with the same workspace resources, we lock
// the user row when `verify_token` is called. This means that if multiple requests try to
// create the same user simultaneously, the first request will acquire the lock, create the user,
// and any subsequent requests will wait for the lock to be released. After the lock is released,
// the other requests will proceed and return the result, ensuring that each user is created only once
// and avoiding duplicate entries.
let lock_key = user_uuid.as_u128() as i64;
sqlx::query!("SELECT pg_advisory_xact_lock($1)", lock_key)
.execute(txn.deref_mut())
.await?;
let is_new = !is_user_exist(txn.deref_mut(), &user_uuid).await?;
if is_new {
let new_uid = state.id_gen.write().await.next_id();