Skip to content

Commit e5dcad6

Browse files
committed
fix: change to read committed
1 parent 26fc82d commit e5dcad6

File tree

6 files changed

+104
-12
lines changed

6 files changed

+104
-12
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.supertokens.storage.postgresql;
18+
19+
public class LockFailure extends Exception {
20+
public LockFailure() {
21+
super("Failed to acquire advisory lock");
22+
}
23+
24+
public LockFailure(String message) {
25+
super(message);
26+
}
27+
}

src/main/java/io/supertokens/storage/postgresql/Start.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ public void initStorage(boolean shouldWait, List<TenantIdentifier> tenantIdentif
342342
@Override
343343
public <T> T startTransaction(TransactionLogic<T> logic)
344344
throws StorageTransactionLogicException, StorageQueryException {
345-
return startTransaction(logic, TransactionIsolationLevel.SERIALIZABLE);
345+
return startTransaction(logic, TransactionIsolationLevel.READ_COMMITTED);
346346
}
347347

348348
@Override
@@ -384,6 +384,7 @@ public <T> T startTransaction(TransactionLogic<T> logic, TransactionIsolationLev
384384
// We could get here if the new logic hits a false negative,
385385
// e.g., in case someone renamed constraints/tables
386386
boolean isDeadlockException = actualException instanceof SQLTransactionRollbackException
387+
|| actualException instanceof LockFailure
387388
|| exceptionMessage.toLowerCase().contains("concurrent update")
388389
|| exceptionMessage.toLowerCase().contains("concurrent delete")
389390
|| exceptionMessage.toLowerCase().contains("the transaction might succeed if retried") ||

src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,12 @@ public static KeyValueInfo getKeyValue(Start start, TenantIdentifier tenantIdent
875875
public static KeyValueInfo getKeyValue_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier,
876876
String key)
877877
throws SQLException, StorageQueryException {
878+
879+
io.supertokens.storage.postgresql.queries.Utils.takeAdvisoryLock(
880+
con, tenantIdentifier.getAppId() + "~" + tenantIdentifier.getTenantId() + "~" + key);
881+
878882
String QUERY = "SELECT value, created_at_time FROM " + getConfig(start).getKeyValueTable()
879-
+ " WHERE app_id = ? AND tenant_id = ? AND name = ? FOR UPDATE";
883+
+ " WHERE app_id = ? AND tenant_id = ? AND name = ?";
880884

881885
return execute(con, QUERY, pst -> {
882886
pst.setString(1, tenantIdentifier.getAppId());

src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig)
243243
Connection sqlCon = (Connection) con.getConnection();
244244
{
245245
try {
246+
{
247+
io.supertokens.storage.postgresql.queries.Utils.takeAdvisoryLock(
248+
sqlCon, tenantConfig.tenantIdentifier.getConnectionUriDomain() + "~" + tenantConfig.tenantIdentifier.getAppId() + "~" + tenantConfig.tenantIdentifier.getTenantId());
249+
}
246250
{
247251
String QUERY = "DELETE FROM " + getConfig(start).getTenantConfigsTable()
248252
+ " WHERE connection_uri_domain = ? AND app_id = ? AND tenant_id = ?;";

src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,27 @@
1616

1717
package io.supertokens.storage.postgresql.queries;
1818

19+
import java.sql.Connection;
20+
import java.sql.SQLException;
21+
import java.util.ArrayList;
22+
import java.util.HashMap;
23+
import java.util.List;
24+
import java.util.Map;
25+
1926
import com.google.gson.JsonObject;
2027
import com.google.gson.JsonParser;
28+
2129
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
2230
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
2331
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
2432
import io.supertokens.storage.postgresql.PreparedStatementValueSetter;
33+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
34+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.executeBatch;
35+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
2536
import io.supertokens.storage.postgresql.Start;
2637
import io.supertokens.storage.postgresql.config.Config;
27-
import io.supertokens.storage.postgresql.utils.Utils;
28-
29-
import java.sql.Connection;
30-
import java.sql.SQLException;
31-
import java.util.ArrayList;
32-
import java.util.HashMap;
33-
import java.util.List;
34-
import java.util.Map;
35-
36-
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.*;
3738
import static io.supertokens.storage.postgresql.config.Config.getConfig;
39+
import io.supertokens.storage.postgresql.utils.Utils;
3840

3941
public class UserMetadataQueries {
4042

@@ -121,6 +123,7 @@ public static void setMultipleUsersMetadatas_Transaction(Start start, Connection
121123
public static JsonObject getUserMetadata_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
122124
String userId)
123125
throws SQLException, StorageQueryException {
126+
io.supertokens.storage.postgresql.queries.Utils.takeAdvisoryLock(con, appIdentifier.getAppId() + "~" + userId);
124127
String QUERY = "SELECT user_metadata FROM " + getConfig(start).getUserMetadataTable()
125128
+ " WHERE app_id = ? AND user_id = ? FOR UPDATE";
126129
return execute(con, QUERY, pst -> {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.supertokens.storage.postgresql.queries;
18+
19+
import java.sql.Connection;
20+
import java.sql.SQLException;
21+
22+
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
23+
import io.supertokens.storage.postgresql.LockFailure;
24+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
25+
26+
public class Utils {
27+
28+
/**
29+
* Acquires a PostgreSQL advisory lock using two string keys.
30+
* Uses pg_try_advisory_xact_lock which is transaction-scoped (automatically released on commit/rollback).
31+
*
32+
* @param con The database connection (must be within a transaction)
33+
* @param key Key for the lock (e.g., appId)
34+
* @throws SQLException If a database error occurs
35+
* @throws StorageQueryException If a query error occurs
36+
* @throws LockFailure If the lock could not be acquired
37+
*/
38+
public static void takeAdvisoryLock(Connection con, String key)
39+
throws SQLException, StorageQueryException {
40+
String LOCK_QUERY = "SELECT pg_try_advisory_xact_lock(hashtext(?))";
41+
boolean lockAcquired = execute(con, LOCK_QUERY, pst -> {
42+
pst.setString(1, key);
43+
}, result -> {
44+
if (result.next()) {
45+
return result.getBoolean(1);
46+
}
47+
return false;
48+
});
49+
if (!lockAcquired) {
50+
throw new StorageQueryException(new LockFailure());
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)