Skip to content

Commit 5d6a1db

Browse files
authored
fix: deadlock and storage tests (#160)
* fix: deadlock tests * fix: storage layer test
1 parent afe34d1 commit 5d6a1db

File tree

2 files changed

+150
-1
lines changed

2 files changed

+150
-1
lines changed

src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@
1818
package io.supertokens.storage.postgresql.test;
1919

2020
import io.supertokens.ProcessState;
21+
import io.supertokens.authRecipe.AuthRecipe;
22+
import io.supertokens.emailpassword.EmailPassword;
23+
import io.supertokens.featureflag.EE_FEATURES;
24+
import io.supertokens.featureflag.FeatureFlagTestContent;
2125
import io.supertokens.passwordless.Passwordless;
2226
import io.supertokens.pluginInterface.KeyValueInfo;
2327
import io.supertokens.pluginInterface.Storage;
28+
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
2429
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
2530
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
2631
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
@@ -599,6 +604,94 @@ public void testConcurrentDeleteAndInsert() throws Exception {
599604
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
600605
}
601606

607+
@Test
608+
public void testLinkAccountsInParallel() throws Exception {
609+
String[] args = {"../"};
610+
611+
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
612+
FeatureFlagTestContent.getInstance(process.getProcess())
613+
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
614+
EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
615+
process.startProcess();
616+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
617+
618+
ExecutorService es = Executors.newFixedThreadPool(1000);
619+
620+
AtomicBoolean pass = new AtomicBoolean(true);
621+
622+
AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
623+
AuthRecipeUserInfo user2 = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
624+
625+
AuthRecipe.createPrimaryUser(process.getProcess(), user1.getSupertokensUserId());
626+
627+
for (int i = 0; i < 3000; i++) {
628+
es.execute(() -> {
629+
try {
630+
AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user1.getSupertokensUserId());
631+
AuthRecipe.unlinkAccounts(process.getProcess(), user2.getSupertokensUserId());
632+
} catch (Exception e) {
633+
if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) {
634+
pass.set(false);
635+
}
636+
}
637+
});
638+
}
639+
640+
es.shutdown();
641+
es.awaitTermination(2, TimeUnit.MINUTES);
642+
643+
assert (pass.get());
644+
assertNull(process
645+
.checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
646+
assertNotNull(process
647+
.checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
648+
649+
process.kill();
650+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
651+
}
652+
653+
@Test
654+
public void testCreatePrimaryInParallel() throws Exception {
655+
String[] args = {"../"};
656+
657+
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
658+
FeatureFlagTestContent.getInstance(process.getProcess())
659+
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
660+
EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
661+
process.startProcess();
662+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
663+
664+
ExecutorService es = Executors.newFixedThreadPool(1000);
665+
666+
AtomicBoolean pass = new AtomicBoolean(true);
667+
668+
AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
669+
670+
for (int i = 0; i < 3000; i++) {
671+
es.execute(() -> {
672+
try {
673+
AuthRecipe.createPrimaryUser(process.getProcess(), user1.getSupertokensUserId());
674+
AuthRecipe.unlinkAccounts(process.getProcess(), user1.getSupertokensUserId());
675+
} catch (Exception e) {
676+
if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) {
677+
pass.set(false);
678+
}
679+
}
680+
});
681+
}
682+
683+
es.shutdown();
684+
es.awaitTermination(2, TimeUnit.MINUTES);
685+
686+
assert (pass.get());
687+
assertNull(process
688+
.checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
689+
assertNotNull(process
690+
.checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
691+
692+
process.kill();
693+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
694+
}
602695
}
603696

604697
/*

src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package io.supertokens.storage.postgresql.test;
22

33
import io.supertokens.ProcessState;
4+
import io.supertokens.authRecipe.AuthRecipe;
5+
import io.supertokens.emailpassword.EmailPassword;
6+
import io.supertokens.featureflag.EE_FEATURES;
7+
import io.supertokens.featureflag.FeatureFlagTestContent;
8+
import io.supertokens.passwordless.Passwordless;
49
import io.supertokens.pluginInterface.STORAGE_TYPE;
10+
import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage;
11+
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
512
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
613
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
714
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
@@ -14,13 +21,14 @@
1421
import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage;
1522
import io.supertokens.storageLayer.StorageLayer;
1623

24+
import io.supertokens.thirdparty.ThirdParty;
1725
import org.junit.AfterClass;
1826
import org.junit.Before;
1927
import org.junit.Rule;
2028
import org.junit.Test;
2129
import org.junit.rules.TestRule;
2230

23-
import static org.junit.Assert.assertNotNull;
31+
import static org.junit.Assert.*;
2432

2533
public class StorageLayerTest {
2634

@@ -94,4 +102,52 @@ public void totpCodeLengthTest() throws Exception {
94102
insertUsedCodeUtil(storage, code);
95103
}
96104

105+
@Test
106+
public void testLinkedAccountUser() throws Exception {
107+
String[] args = {"../"};
108+
109+
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
110+
FeatureFlagTestContent.getInstance(process.getProcess())
111+
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
112+
EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
113+
process.startProcess();
114+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
115+
116+
AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
117+
Thread.sleep(50);
118+
AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid", "[email protected]").user;
119+
Thread.sleep(50);
120+
Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "[email protected]", null, null, null);
121+
AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash, code1.userInputCode, null).user;
122+
Thread.sleep(50);
123+
Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210", null, null);
124+
AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash, code2.userInputCode, null).user;
125+
126+
AuthRecipe.createPrimaryUser(process.getProcess(), user3.getSupertokensUserId());
127+
AuthRecipe.linkAccounts(process.getProcess(), user1.getSupertokensUserId(), user3.getSupertokensUserId());
128+
AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user3.getSupertokensUserId());
129+
AuthRecipe.linkAccounts(process.getProcess(), user4.getSupertokensUserId(), user3.getSupertokensUserId());
130+
131+
String[] userIds = new String[]{
132+
user1.getSupertokensUserId(),
133+
user2.getSupertokensUserId(),
134+
user3.getSupertokensUserId(),
135+
user4.getSupertokensUserId()
136+
};
137+
138+
for (String userId : userIds){
139+
AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage(process.getProcess())).getPrimaryUserById(
140+
new AppIdentifier(null, null), userId);
141+
assertEquals(user3.getSupertokensUserId(), primaryUser.getSupertokensUserId());
142+
assertEquals(4, primaryUser.loginMethods.length);
143+
assertTrue(primaryUser.loginMethods[0].timeJoined < primaryUser.loginMethods[1].timeJoined);
144+
assertTrue(primaryUser.loginMethods[1].timeJoined < primaryUser.loginMethods[2].timeJoined);
145+
assertTrue(primaryUser.loginMethods[2].timeJoined < primaryUser.loginMethods[3].timeJoined);
146+
assertEquals(primaryUser.timeJoined, primaryUser.loginMethods[0].timeJoined);
147+
}
148+
149+
process.kill();
150+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
151+
}
152+
97153
}

0 commit comments

Comments
 (0)