diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java index 15d8016b..3dd0241f 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java @@ -18,9 +18,14 @@ package io.supertokens.storage.postgresql.test; import io.supertokens.ProcessState; +import io.supertokens.authRecipe.AuthRecipe; +import io.supertokens.emailpassword.EmailPassword; +import io.supertokens.featureflag.EE_FEATURES; +import io.supertokens.featureflag.FeatureFlagTestContent; import io.supertokens.passwordless.Passwordless; import io.supertokens.pluginInterface.KeyValueInfo; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; @@ -599,6 +604,94 @@ public void testConcurrentDeleteAndInsert() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + @Test + public void testLinkAccountsInParallel() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false); + FeatureFlagTestContent.getInstance(process.getProcess()) + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); + process.startProcess(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + ExecutorService es = Executors.newFixedThreadPool(1000); + + AtomicBoolean pass = new AtomicBoolean(true); + + AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "test1@example.com", "password"); + AuthRecipeUserInfo user2 = EmailPassword.signUp(process.getProcess(), "test2@example.com", "password"); + + AuthRecipe.createPrimaryUser(process.getProcess(), user1.getSupertokensUserId()); + + for (int i = 0; i < 3000; i++) { + es.execute(() -> { + try { + AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user1.getSupertokensUserId()); + AuthRecipe.unlinkAccounts(process.getProcess(), user2.getSupertokensUserId()); + } catch (Exception e) { + if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) { + pass.set(false); + } + } + }); + } + + es.shutdown(); + es.awaitTermination(2, TimeUnit.MINUTES); + + assert (pass.get()); + assertNull(process + .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + assertNotNull(process + .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + @Test + public void testCreatePrimaryInParallel() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false); + FeatureFlagTestContent.getInstance(process.getProcess()) + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); + process.startProcess(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + ExecutorService es = Executors.newFixedThreadPool(1000); + + AtomicBoolean pass = new AtomicBoolean(true); + + AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "test1@example.com", "password"); + + for (int i = 0; i < 3000; i++) { + es.execute(() -> { + try { + AuthRecipe.createPrimaryUser(process.getProcess(), user1.getSupertokensUserId()); + AuthRecipe.unlinkAccounts(process.getProcess(), user1.getSupertokensUserId()); + } catch (Exception e) { + if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) { + pass.set(false); + } + } + }); + } + + es.shutdown(); + es.awaitTermination(2, TimeUnit.MINUTES); + + assert (pass.get()); + assertNull(process + .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + assertNotNull(process + .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } } /*