From 8507d379b6fd547705b752542098ad7b62dbbb01 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 7 Nov 2025 11:54:24 +0100 Subject: [PATCH 1/7] experiment: Deadlock logger --- .../java/io/supertokens/DeadlockLogger.java | 80 +++++++++++++++++++ src/main/java/io/supertokens/Main.java | 3 + 2 files changed, 83 insertions(+) create mode 100644 src/main/java/io/supertokens/DeadlockLogger.java diff --git a/src/main/java/io/supertokens/DeadlockLogger.java b/src/main/java/io/supertokens/DeadlockLogger.java new file mode 100644 index 000000000..713f812ee --- /dev/null +++ b/src/main/java/io/supertokens/DeadlockLogger.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; + +public class DeadlockLogger { + + private static final DeadlockLogger INSTANCE = new DeadlockLogger(); + private static Main MAIN; + + public static DeadlockLogger getINSTANCE() { + return INSTANCE; + } + + public void start(){ + Thread deadlockLoggerThread = new Thread(deadlockDetector, "DeadlockLogger"); + deadlockLoggerThread.setDaemon(true); + deadlockLoggerThread.start(); + } + + private final Runnable deadlockDetector = new Runnable() { + @Override + public void run() { + System.out.println("DeadlockLogger started!"); + while (true) { + System.out.println("DeadlockLogger - checking"); + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + long[] threadIds = bean.findDeadlockedThreads(); // Returns null if no threads are deadlocked. + System.out.println("DeadlockLogger - DeadlockedThreads: " + Arrays.toString(threadIds)); + if (threadIds != null) { + ThreadInfo[] infos = bean.getThreadInfo(threadIds); + boolean deadlockFound = false; + for (ThreadInfo info : infos) { + System.out.println("DEADLOCK found!"); + StackTraceElement[] stack = info.getStackTrace(); + System.out.println("ThreadName: " + info.getThreadName()); + System.out.println("LockName: " + info.getLockName()); + System.out.println("LockOwnerName: " + info.getLockOwnerName()); + System.out.println("LockedMonitors: " + Arrays.toString(info.getLockedMonitors())); + System.out.println("LockInfo: " + info.getLockInfo()); + System.out.println("Stack: " + Arrays.toString(stack)); + System.out.println(); + deadlockFound = true; + } + if(deadlockFound) { + break; + } + } + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + }; + + + public static void setMAIN(Main MAIN) { + DeadlockLogger.MAIN = MAIN; + } +} diff --git a/src/main/java/io/supertokens/Main.java b/src/main/java/io/supertokens/Main.java index 95e0b0d9f..7df0fa5af 100644 --- a/src/main/java/io/supertokens/Main.java +++ b/src/main/java/io/supertokens/Main.java @@ -182,6 +182,9 @@ private void init() throws IOException, StorageQueryException { // init file logging Logging.initFileLogging(this); + DeadlockLogger.setMAIN(this); + DeadlockLogger.getINSTANCE().start(); + // initialise cron job handler Cronjobs.init(this); From 1b1b2b2bdf405570e3f597086ec03d694ca2a423 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 12 Nov 2025 16:22:42 +0530 Subject: [PATCH 2/7] fix: race issue with oauth refresh (#1199) * fix: race issue with oauth refresh * fix: review comment * fix: remove print --- CHANGELOG.md | 4 + build.gradle | 2 +- .../webserver/api/oauth/OAuthTokenAPI.java | 98 +++++++++++++------ ...reshTokenFlowWithTokenRotationOptions.java | 73 ++++++++++++++ 4 files changed, 145 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a05213c..323a88f83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [11.2.1] + +- Fixes race issues with Refreshing OAuth token + ## [11.2.0] - Adds opentelemetry-javaagent to the core distribution diff --git a/build.gradle b/build.gradle index 6720cd723..8401b44d6 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ java { } } -version = "11.2.0" +version = "11.2.1" repositories { mavenCentral() diff --git a/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java b/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java index aad3fb727..5d88181ce 100644 --- a/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java +++ b/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java @@ -62,6 +62,8 @@ import java.security.spec.InvalidKeySpecException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; public class OAuthTokenAPI extends WebserverAPI { @@ -90,6 +92,32 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I String authorizationHeader = InputParser.parseStringOrThrowError(input, "authorizationHeader", true); + if (grantType.equals("refresh_token")) { + String refreshTokenForLock = InputParser.parseStringOrThrowError(bodyFromSDK, "refresh_token", false); + NamedLockObject entry = lockMap.computeIfAbsent(refreshTokenForLock, k -> new NamedLockObject()); + try { + entry.refCount.incrementAndGet(); + synchronized (entry.obj) { + handle(req, resp, authorizationHeader, bodyFromSDK, grantType, iss, accessTokenUpdate, + idTokenUpdate, + useDynamicKey); + } + } finally { + entry.refCount.decrementAndGet(); + if (entry.refCount.get() == 0) { + lockMap.remove(refreshTokenForLock, entry); + } + } + + } else { + handle(req, resp, authorizationHeader, bodyFromSDK, grantType, iss, accessTokenUpdate, idTokenUpdate, + useDynamicKey); + } + } + + private void handle(HttpServletRequest req, HttpServletResponse resp, String authorizationHeader, + JsonObject bodyFromSDK, String grantType, String iss, JsonObject accessTokenUpdate, + JsonObject idTokenUpdate, boolean useDynamicKey) throws ServletException, IOException { Map headers = new HashMap<>(); if (authorizationHeader != null) { headers.put("Authorization", authorizationHeader); @@ -127,19 +155,19 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I formFieldsForTokenIntrospect.put("token", internalRefreshToken); HttpRequestForOAuthProvider.Response response = OAuthProxyHelper.proxyFormPOST( - main, req, resp, - appIdentifier, - storage, - null, // clientIdToCheck - "/admin/oauth2/introspect", // pathProxy - true, // proxyToAdmin - false, // camelToSnakeCaseConversion - formFieldsForTokenIntrospect, - new HashMap<>() // headers + main, req, resp, + appIdentifier, + storage, + null, // clientIdToCheck + "/admin/oauth2/introspect", // pathProxy + true, // proxyToAdmin + false, // camelToSnakeCaseConversion + formFieldsForTokenIntrospect, + new HashMap<>() // headers ); if (response == null) { - return; // proxy helper would have sent the error response + return; } JsonObject refreshTokenPayload = response.jsonResponse.getAsJsonObject(); @@ -147,14 +175,14 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I try { OAuth.verifyAndUpdateIntrospectRefreshTokenPayload(main, appIdentifier, storage, refreshTokenPayload, refreshToken, oauthClient.clientId); } catch (StorageQueryException | TenantOrAppNotFoundException | - FeatureNotEnabledException | InvalidConfigException e) { + FeatureNotEnabledException | InvalidConfigException e) { throw new ServletException(e); } if (!refreshTokenPayload.get("active").getAsBoolean()) { // this is what ory would return for an invalid token OAuthProxyHelper.handleOAuthAPIException(resp, new OAuthAPIException( - "token_inactive", "Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed.", 401 + "token_inactive", "Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed.", 401 )); return; } @@ -163,20 +191,21 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I } HttpRequestForOAuthProvider.Response response = OAuthProxyHelper.proxyFormPOST( - main, req, resp, - getAppIdentifier(req), - enforcePublicTenantAndGetPublicTenantStorage(req), - clientId, // clientIdToCheck - "/oauth2/token", // proxyPath - false, // proxyToAdmin - false, // camelToSnakeCaseConversion - formFields, - headers // headers + main, req, resp, + getAppIdentifier(req), + enforcePublicTenantAndGetPublicTenantStorage(req), + clientId, // clientIdToCheck + "/oauth2/token", // proxyPath + false, // proxyToAdmin + false, // camelToSnakeCaseConversion + formFields, + headers // headers ); if (response != null) { try { - response.jsonResponse = OAuth.transformTokens(super.main, appIdentifier, storage, response.jsonResponse.getAsJsonObject(), iss, accessTokenUpdate, idTokenUpdate, useDynamicKey); + response.jsonResponse = OAuth.transformTokens(super.main, appIdentifier, storage, response.jsonResponse.getAsJsonObject(), + iss, accessTokenUpdate, idTokenUpdate, useDynamicKey); if (grantType.equals("client_credentials")) { try { @@ -215,15 +244,15 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I formFieldsForTokenIntrospect.put("token", newRefreshToken); HttpRequestForOAuthProvider.Response introspectResponse = OAuthProxyHelper.proxyFormPOST( - main, req, resp, - getAppIdentifier(req), - enforcePublicTenantAndGetPublicTenantStorage(req), - null, // clientIdToCheck - "/admin/oauth2/introspect", // pathProxy - true, // proxyToAdmin - false, // camelToSnakeCaseConversion - formFieldsForTokenIntrospect, - new HashMap<>() // headers + main, req, resp, + getAppIdentifier(req), + enforcePublicTenantAndGetPublicTenantStorage(req), + null, // clientIdToCheck + "/admin/oauth2/introspect", // pathProxy + true, // proxyToAdmin + false, // camelToSnakeCaseConversion + formFieldsForTokenIntrospect, + new HashMap<>() // headers ); if (introspectResponse != null) { @@ -288,4 +317,11 @@ private void updateLastActive(AppIdentifier appIdentifier, String sessionHandle) // ignore } } + + + private static class NamedLockObject { + final Object obj = new Object(); + final AtomicInteger refCount = new AtomicInteger(0); + } + private static final ConcurrentHashMap lockMap = new ConcurrentHashMap<>(); } diff --git a/src/test/java/io/supertokens/test/oauth/api/TestRefreshTokenFlowWithTokenRotationOptions.java b/src/test/java/io/supertokens/test/oauth/api/TestRefreshTokenFlowWithTokenRotationOptions.java index 065bec46a..3a786c7a9 100644 --- a/src/test/java/io/supertokens/test/oauth/api/TestRefreshTokenFlowWithTokenRotationOptions.java +++ b/src/test/java/io/supertokens/test/oauth/api/TestRefreshTokenFlowWithTokenRotationOptions.java @@ -40,6 +40,10 @@ import java.net.URL; import java.net.URLDecoder; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.*; @@ -399,6 +403,75 @@ public void testRefreshTokenWithRotationIsDisabledAfter() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + @Test + public void testParallelRefreshTokenWithoutRotation() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + FeatureFlag.getInstance(process.getProcess()) + .setLicenseKeyAndSyncFeatures(TotpLicenseTest.OPAQUE_KEY_WITH_MFA_FEATURE); + FeatureFlagTestContent.getInstance(process.getProcess()) + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.OAUTH}); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + // Create client with token rotation disabled + JsonObject client = createClient(process.getProcess(), false); + JsonObject tokens = completeFlowAndGetTokens(process.getProcess(), client); + + String refreshToken = tokens.get("refresh_token").getAsString(); + + // Setup parallel execution: 16 threads, each making 1000 refresh calls + int numberOfThreads = 16; + int refreshCallsPerThread = 25; + ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); + AtomicInteger successCount = new AtomicInteger(0); + AtomicInteger failureCount = new AtomicInteger(0); + List exceptions = Collections.synchronizedList(new ArrayList<>()); + + // Execute refresh token calls in parallel + for (int i = 0; i < numberOfThreads; i++) { + executor.execute(() -> { + for (int j = 0; j < refreshCallsPerThread; j++) { + try { + JsonObject refreshResponse = refreshToken(process.getProcess(), client, refreshToken); + if ("OK".equals(refreshResponse.get("status").getAsString())) { + successCount.incrementAndGet(); + } else { + failureCount.incrementAndGet(); + exceptions.add(new RuntimeException("Refresh failed: " + refreshResponse.toString())); + } + } catch (Exception e) { + System.out.println(e.getMessage()); + failureCount.incrementAndGet(); + exceptions.add(e); + } + } + }); + } + + executor.shutdown(); + boolean terminated = executor.awaitTermination(5, TimeUnit.MINUTES); + assertTrue("Executor did not terminate within timeout", terminated); + + // Verify all refresh calls succeeded + int totalExpectedCalls = numberOfThreads * refreshCallsPerThread; + assertEquals("All refresh token calls should succeed", totalExpectedCalls, successCount.get()); + assertEquals("No refresh token calls should fail", 0, failureCount.get()); + assertTrue("No exceptions should occur", exceptions.isEmpty()); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + private static Map splitQuery(URL url) throws UnsupportedEncodingException { Map queryPairs = new LinkedHashMap<>(); String query = url.getQuery(); From 1312069b0dd05b80f2036ca6593ca1adcace5de4 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 12 Nov 2025 17:01:52 +0530 Subject: [PATCH 3/7] fix: deadlock in resource distributor (#1197) --- CHANGELOG.md | 1 + .../io/supertokens/ResourceDistributor.java | 28 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 323a88f83..2b248fcc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [11.2.1] +- Fixes deadlock issue with `ResourceDistributor` - Fixes race issues with Refreshing OAuth token ## [11.2.0] diff --git a/src/main/java/io/supertokens/ResourceDistributor.java b/src/main/java/io/supertokens/ResourceDistributor.java index be202acb9..c722f94bc 100644 --- a/src/main/java/io/supertokens/ResourceDistributor.java +++ b/src/main/java/io/supertokens/ResourceDistributor.java @@ -51,12 +51,12 @@ public static TenantIdentifier getAppForTesting() { return appUsedForTesting; } - public synchronized SingletonResource getResource(AppIdentifier appIdentifier, @Nonnull String key) + public SingletonResource getResource(AppIdentifier appIdentifier, @Nonnull String key) throws TenantOrAppNotFoundException { return getResource(appIdentifier.getAsPublicTenantIdentifier(), key); } - public synchronized SingletonResource getResource(TenantIdentifier tenantIdentifier, @Nonnull String key) + public SingletonResource getResource(TenantIdentifier tenantIdentifier, @Nonnull String key) throws TenantOrAppNotFoundException { // first we do exact match SingletonResource resource = resources.get(new KeyClass(tenantIdentifier, key)); @@ -70,14 +70,6 @@ public synchronized SingletonResource getResource(TenantIdentifier tenantIdentif throw new TenantOrAppNotFoundException(tenantIdentifier); } - MultitenancyHelper.getInstance(main).refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true); - - // we try again.. - resource = resources.get(new KeyClass(tenantIdentifier, key)); - if (resource != null) { - return resource; - } - // then we see if the user has configured anything to do with connectionUriDomain, and if they have, // then we must return null cause the user has not specifically added tenantId to it for (KeyClass currKey : resources.keySet()) { @@ -101,11 +93,11 @@ public synchronized SingletonResource getResource(TenantIdentifier tenantIdentif } @TestOnly - public synchronized SingletonResource getResource(@Nonnull String key) { + public SingletonResource getResource(@Nonnull String key) { return resources.get(new KeyClass(appUsedForTesting, key)); } - public synchronized SingletonResource setResource(TenantIdentifier tenantIdentifier, + public SingletonResource setResource(TenantIdentifier tenantIdentifier, @Nonnull String key, SingletonResource resource) { SingletonResource alreadyExists = resources.get(new KeyClass(tenantIdentifier, key)); @@ -116,7 +108,7 @@ public synchronized SingletonResource setResource(TenantIdentifier tenantIdentif return resource; } - public synchronized SingletonResource removeResource(TenantIdentifier tenantIdentifier, + public SingletonResource removeResource(TenantIdentifier tenantIdentifier, @Nonnull String key) { SingletonResource singletonResource = resources.get(new KeyClass(tenantIdentifier, key)); if (singletonResource == null) { @@ -126,18 +118,18 @@ public synchronized SingletonResource removeResource(TenantIdentifier tenantIden return singletonResource; } - public synchronized SingletonResource setResource(AppIdentifier appIdentifier, + public SingletonResource setResource(AppIdentifier appIdentifier, @Nonnull String key, SingletonResource resource) { return setResource(appIdentifier.getAsPublicTenantIdentifier(), key, resource); } - public synchronized SingletonResource removeResource(AppIdentifier appIdentifier, + public SingletonResource removeResource(AppIdentifier appIdentifier, @Nonnull String key) { return removeResource(appIdentifier.getAsPublicTenantIdentifier(), key); } - public synchronized void clearAllResourcesWithResourceKey(String inputKey) { + public void clearAllResourcesWithResourceKey(String inputKey) { List toRemove = new ArrayList<>(); resources.forEach((key, value) -> { if (key.key.equals(inputKey)) { @@ -149,7 +141,7 @@ public synchronized void clearAllResourcesWithResourceKey(String inputKey) { } } - public synchronized Map getAllResourcesWithResourceKey(String inputKey) { + public Map getAllResourcesWithResourceKey(String inputKey) { Map result = new HashMap<>(); resources.forEach((key, value) -> { if (key.key.equals(inputKey)) { @@ -160,7 +152,7 @@ public synchronized Map getAllResourcesWithResource } @TestOnly - public synchronized SingletonResource setResource(@Nonnull String key, + public SingletonResource setResource(@Nonnull String key, SingletonResource resource) { return setResource(appUsedForTesting, key, resource); } From bce323172f5bfcedaf79d93ca53fa31a6486017f Mon Sep 17 00:00:00 2001 From: Supertokens Bot <> Date: Wed, 12 Nov 2025 11:42:19 +0000 Subject: [PATCH 4/7] adding dev-v11.2.1 tag to this commit to ensure building --- cli/jar/cli.jar | Bin 48513 -> 48513 bytes downloader/jar/downloader.jar | Bin 15260 -> 15260 bytes ee/jar/ee.jar | Bin 14365 -> 14365 bytes jar/{core-11.2.0.jar => core-11.2.1.jar} | Bin 44260043 -> 44261453 bytes 4 files changed, 0 insertions(+), 0 deletions(-) rename jar/{core-11.2.0.jar => core-11.2.1.jar} (99%) diff --git a/cli/jar/cli.jar b/cli/jar/cli.jar index 00ba5bf572ca2fe56e7c007e2662ec9f0cb84758..20a90d9d7df7d8fa64b53592333af1996c60cd86 100644 GIT binary patch delta 923 zcmZ9KZAepL6vyv&r>re!V>TFL4w}iZv=>8bE(>X9YvI=0K8%9OOu=ov%ulPxDDZ=j z@xVl=$Xr^W+ugm}+?&08ieMk2vOtIk^P^Bkfh?kX|Brph3-|t?^E>A}&pGG0110%D zNnYVT%qprFMyX`lPxalcsAVN-a@LCCW5h=;eZ+?q-`?Ohmia$nVk;8nwo4)?Ww%t) zg4|KJ0lZf)$^i-*8^DTYMF#M5!#;p!ZH9uPrIEOKC8^h~hr_PBT8>5DU{E0Cqv;TU zrFxYJ)MCA@I(UrD2<5u<}0D##kFJc%?)+&7gP-MX(Jg6 zScx&K7asIqQfZ``22EquE#g>Tv&0=W2u(Mf|3*|p{5e(Snh^^xZpO=yILy!5e~I@H zQj*VVgBWWae>RBG2rq}sbnpuiI~rg1&2u}&LyQWBv61hQO(dK*3jPs0&Jw0ZeKcMb z-HnzU(RJui@I3uM%c+|xNgiUIUWS@tyP(HoxHuAtHNbuq!_5*!ycM=1-T@ts<1WZT zyc@QQqkM!r3;Q>Bgz`iWG$t6y+XUUKlJ5lz^ECG>?}gne_*Y|3K+BUf?sD=p?03mg zXfPFqE~Sn_+tTNtPtwPrf70in-7{yPODQ8+oN30|oT0H2dzhhYewFRd(tHre!V>TFL4w}iZv=>8bP77&ebK%z8KHLZ@GljPGGC!>%qreYB z#sd?fB6DehZg;!e+?&08ieMk2vOtIk^P^Bkfh?kX|Brph3-|t?^E>A}&pGG0110%D zNnYXZWEE8mqf|1TttanR)UuK^Icr7X81a!yAMs(uw=b}bW&Tf?*ouU??UD#e*)5f{ zAa~U50Pod{a)5%y2C$-8kpcYNuos|No1vg+X(aB1lGN+g!(rE5Eyp5nFes4n(R2vF zQaw=yP_!Hb(ARY90j}BH0B>y1Wm>6&+mrV$1jY7>bqwPsJE}JmKGh)nuwSo2e3!qG zOnS7)$$Re8T{*7c*6dElt#pS^Oa!W;@Yurb3@&U^D2hlcugr*zLeRBG2rq}sbnpvdI~rf+=J}oCAw~tm*vR+rCKAdU1>cArX9-iI9F12+ zcB3UnWF2}GJWoH+a_Xi^l80EQm!YQUF6gl+E{=qw4X|HDakE4bYlH2GbwGz>xC^on z>w)d!DIej_!2Zo2raayYjS5EcHct1dO!R?;5;XT~!V9}i@U6xkhn6R4+~wpc*zb~~ z&_F5#T}mB+wx`cRpQMjM|D?}BduC2Umr_QuIMa-^IYVP5_Ao=){3_d*rTH%2NaERg w#DB@s+FEk7)<_P2DfyOj!rz*wXFbkyu-U=^sH;HtdQ><8yHsd_?wRfQ3jne>P5=M^ diff --git a/downloader/jar/downloader.jar b/downloader/jar/downloader.jar index bcd4d5380476c9bd0c7272eb1950f9642380da8c..ef6ef7562575c713bfd4cfc65aef321342cd3214 100644 GIT binary patch delta 431 zcmbPJKBt^Fz?+#xgn@yBgTXyEXCki}Gl(+tQUg-a3_!pHB7lGyh%@u`nHf+uEYksN zxM~ifHuE#Kaf29>XYe?J1@7>$Gl3b;_*SwBg3JurRuf_fH1rM7K>f*rMzWLFS@CZ^ zBkBlNH(5Z!8LU1`BA69yl7NKvRGLpFx_Gv1*RXG2ZCu+iwr1Vdh%`yGcfy$MGlxwwoC%k ZCoFxzw1gEzZ>bf;tgBW5U~vuW5&&38d`kcT delta 431 zcmbPJKBt^Fz?+#xgn@yBgTco(W+JZ|Gl(+tQUg-a3_!pHB7lGyh%@u`nHf+uEYksN zxM~ifHuE#Kaf29>XYe?J1@7>$Gl3b;_*SwBg3JurRuf_fH1rM7K>f*rMzWLFS@CZ^ zBkBlNH(5Z!8LU1`BA69yl7NKvRGLpFx_Gv1*RXG2ZCu+iwr1Vdh%`yGcfy$MGlxwwoC%k ZCoFxzw1gEzZ>bf;tgBW5U~vuW5&&n%b&~)9 diff --git a/ee/jar/ee.jar b/ee/jar/ee.jar index a3cf62771afd5206c0740c89f2c3b76a2d7acb1f..b0dfb705db2094ff358dc6e9b308c8cd1a1e7f7d 100644 GIT binary patch delta 216 zcmbPRFt>m=z?+#xgn@yBgTW&b@I4b)iIoXNX^u%FnE%1V1Wc=&+Jos#Q#~*} n%hU=?UpKV{)8b}sU^>Ap3QX@c3joss=0RXO)f}R3sd)teQp-DO delta 216 zcmbPRFt>m=z?+#xgn@yBgTdD}W+JZ|Gl(+tQUg-a3_!pHB7lGyh%@u`nHf+uEYksN zxT+7LCbKhIfauLmj7M0&3{Ey1Z7{>b@I4b)iIoXNX^u%FnE%1V1Wc=&+Jos#Q#~*} n%hU=?UpKV{)8b}sU^>Ap3QX@c3joss=0RXO)f}R3sd)te_((Um diff --git a/jar/core-11.2.0.jar b/jar/core-11.2.1.jar similarity index 99% rename from jar/core-11.2.0.jar rename to jar/core-11.2.1.jar index 87a68dcd2fd15b9d3cd312541441e8c96d64f926..1802e10fae6bf54f79da623de7ba0cf4db2401ba 100644 GIT binary patch delta 42165 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 OM=m=#-TkZpo$LRmp(Kj{ From 60694e31ff5079d5b1cc2bd6f41b80238539482e Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Thu, 13 Nov 2025 12:54:31 +0100 Subject: [PATCH 5/7] fix: add deadlock logger --- config.yaml | 3 ++ devConfig.yaml | 5 +++- src/main/java/io/supertokens/Main.java | 9 ++++-- .../io/supertokens/config/CoreConfig.java | 11 ++++++++ .../deadlocklogger}/DeadlockLogger.java | 28 +++++++++++-------- .../telemetry/TelemetryProvider.java | 2 +- 6 files changed, 41 insertions(+), 17 deletions(-) rename src/main/java/io/supertokens/{ => cronjobs/deadlocklogger}/DeadlockLogger.java (77%) diff --git a/config.yaml b/config.yaml index 4459cbaad..8fe3974dc 100644 --- a/config.yaml +++ b/config.yaml @@ -186,3 +186,6 @@ core_config_version: 0 # (OPTIONAL | Default: null) string value. The URL of the OpenTelemetry collector to which the core # will send telemetry data. This should be in the format http://: or https://:. # otel_collector_connection_uri: + +# (OPTIONAL | Default: false) boolean value. Enables or disables the deadlock logger. +# deadlock_logger_enable: diff --git a/devConfig.yaml b/devConfig.yaml index fe55683b6..589fb2212 100644 --- a/devConfig.yaml +++ b/devConfig.yaml @@ -185,4 +185,7 @@ disable_telemetry: true # (OPTIONAL | Default: null) string value. The URL of the OpenTelemetry collector to which the core # will send telemetry data. This should be in the format http://: or https://:. -# otel_collector_connection_uri: \ No newline at end of file +# otel_collector_connection_uri: + +# (OPTIONAL | Default: false) boolean value. Enables or disables the deadlock logger. +# deadlock_logger_enable: \ No newline at end of file diff --git a/src/main/java/io/supertokens/Main.java b/src/main/java/io/supertokens/Main.java index 7df0fa5af..3f680009a 100644 --- a/src/main/java/io/supertokens/Main.java +++ b/src/main/java/io/supertokens/Main.java @@ -23,6 +23,7 @@ import io.supertokens.cronjobs.bulkimport.ProcessBulkImportUsers; import io.supertokens.cronjobs.cleanupOAuthSessionsAndChallenges.CleanupOAuthSessionsAndChallenges; import io.supertokens.cronjobs.cleanupWebauthnExpiredData.CleanUpWebauthNExpiredDataCron; +import io.supertokens.cronjobs.deadlocklogger.DeadlockLogger; import io.supertokens.cronjobs.deleteExpiredAccessTokenSigningKeys.DeleteExpiredAccessTokenSigningKeys; import io.supertokens.cronjobs.deleteExpiredDashboardSessions.DeleteExpiredDashboardSessions; import io.supertokens.cronjobs.deleteExpiredEmailVerificationTokens.DeleteExpiredEmailVerificationTokens; @@ -182,9 +183,6 @@ private void init() throws IOException, StorageQueryException { // init file logging Logging.initFileLogging(this); - DeadlockLogger.setMAIN(this); - DeadlockLogger.getINSTANCE().start(); - // initialise cron job handler Cronjobs.init(this); @@ -281,6 +279,11 @@ private void init() throws IOException, StorageQueryException { Cronjobs.addCronjob(this, CleanUpWebauthNExpiredDataCron.init(this, uniqueUserPoolIdsTenants)); + // starts the DeadlockLogger if + if (!Config.getBaseConfig(this).isDeadlockLoggerEnabled()) { + DeadlockLogger.getInstance().start(); + } + // this is to ensure tenantInfos are in sync for the new cron job as well MultitenancyHelper.getInstance(this).refreshCronjobs(); diff --git a/src/main/java/io/supertokens/config/CoreConfig.java b/src/main/java/io/supertokens/config/CoreConfig.java index 1b103d7cf..b896e09ec 100644 --- a/src/main/java/io/supertokens/config/CoreConfig.java +++ b/src/main/java/io/supertokens/config/CoreConfig.java @@ -412,6 +412,13 @@ public class CoreConfig { "null)") private String otel_collector_connection_uri = null; + @EnvName("DEADLOCK_LOGGER_ENABLE") + @ConfigYamlOnly + @JsonProperty + @ConfigDescription( + "Enables or disables the deadlock logger. (Default: false)") + private boolean deadlock_logger_enable = false; + @IgnoreForAnnotationCheck private static boolean disableOAuthValidationForTest = false; @@ -663,6 +670,10 @@ public String getOtelCollectorConnectionURI() { return otel_collector_connection_uri; } + public boolean isDeadlockLoggerEnabled() { + return deadlock_logger_enable; + } + private String getConfigFileLocation(Main main) { return new File(CLIOptions.get(main).getConfigFilePath() == null ? CLIOptions.get(main).getInstallationPath() + "config.yaml" diff --git a/src/main/java/io/supertokens/DeadlockLogger.java b/src/main/java/io/supertokens/cronjobs/deadlocklogger/DeadlockLogger.java similarity index 77% rename from src/main/java/io/supertokens/DeadlockLogger.java rename to src/main/java/io/supertokens/cronjobs/deadlocklogger/DeadlockLogger.java index 713f812ee..ea5b79089 100644 --- a/src/main/java/io/supertokens/DeadlockLogger.java +++ b/src/main/java/io/supertokens/cronjobs/deadlocklogger/DeadlockLogger.java @@ -14,7 +14,7 @@ * under the License. */ -package io.supertokens; +package io.supertokens.cronjobs.deadlocklogger; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; @@ -24,14 +24,16 @@ public class DeadlockLogger { private static final DeadlockLogger INSTANCE = new DeadlockLogger(); - private static Main MAIN; - public static DeadlockLogger getINSTANCE() { + private DeadlockLogger() { + } + + public static DeadlockLogger getInstance() { return INSTANCE; } public void start(){ - Thread deadlockLoggerThread = new Thread(deadlockDetector, "DeadlockLogger"); + Thread deadlockLoggerThread = new Thread(deadlockDetector, "DeadlockLoggerThread"); deadlockLoggerThread.setDaemon(true); deadlockLoggerThread.start(); } @@ -48,19 +50,26 @@ public void run() { if (threadIds != null) { ThreadInfo[] infos = bean.getThreadInfo(threadIds); boolean deadlockFound = false; + System.out.println("DEADLOCK found!"); for (ThreadInfo info : infos) { - System.out.println("DEADLOCK found!"); - StackTraceElement[] stack = info.getStackTrace(); System.out.println("ThreadName: " + info.getThreadName()); + System.out.println("Thread ID: " + info.getThreadId()); System.out.println("LockName: " + info.getLockName()); System.out.println("LockOwnerName: " + info.getLockOwnerName()); System.out.println("LockedMonitors: " + Arrays.toString(info.getLockedMonitors())); System.out.println("LockInfo: " + info.getLockInfo()); - System.out.println("Stack: " + Arrays.toString(stack)); + System.out.println("Stack: " + Arrays.toString(info.getStackTrace())); System.out.println(); deadlockFound = true; } + System.out.println("*******************************"); if(deadlockFound) { + System.out.println(" ==== ALL THREAD INFO ==="); + ThreadInfo[] allThreads = bean.dumpAllThreads(true, true, 100); + for (ThreadInfo threadInfo : allThreads) { + System.out.println("THREAD: " + threadInfo.getThreadName()); + System.out.println("StackTrace: " + Arrays.toString(threadInfo.getStackTrace())); + } break; } } @@ -72,9 +81,4 @@ public void run() { } } }; - - - public static void setMAIN(Main MAIN) { - DeadlockLogger.MAIN = MAIN; - } } diff --git a/src/main/java/io/supertokens/telemetry/TelemetryProvider.java b/src/main/java/io/supertokens/telemetry/TelemetryProvider.java index cf9450c86..fba159c7a 100644 --- a/src/main/java/io/supertokens/telemetry/TelemetryProvider.java +++ b/src/main/java/io/supertokens/telemetry/TelemetryProvider.java @@ -48,7 +48,7 @@ public class TelemetryProvider extends ResourceDistributor.SingletonResource imp private final OpenTelemetry openTelemetry; - public static synchronized TelemetryProvider getInstance(Main main) { + public static TelemetryProvider getInstance(Main main) { TelemetryProvider instance = null; try { instance = (TelemetryProvider) main.getResourceDistributor() From d37eb68e1305c830fe43b61136f9c5d4baf23bae Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Thu, 13 Nov 2025 13:21:02 +0100 Subject: [PATCH 6/7] fix: changelog and build version --- CHANGELOG.md | 5 +++++ build.gradle | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b248fcc7..e23066065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [11.2.2] + +- Fixes potential deadlock issue with `TelemetryProvider` +- Adds DeadlockLogger as an utility for discovering deadlock issues + ## [11.2.1] - Fixes deadlock issue with `ResourceDistributor` diff --git a/build.gradle b/build.gradle index 8401b44d6..4473fd235 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ java { } } -version = "11.2.1" +version = "11.2.2" repositories { mavenCentral() From 782d01026d772ba7d84d6ac4a2e913c1127ff14f Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Thu, 13 Nov 2025 13:34:10 +0100 Subject: [PATCH 7/7] fix: only start deadlocklogger if it's enabled --- src/main/java/io/supertokens/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/Main.java b/src/main/java/io/supertokens/Main.java index 3f680009a..4c99306a7 100644 --- a/src/main/java/io/supertokens/Main.java +++ b/src/main/java/io/supertokens/Main.java @@ -280,7 +280,7 @@ private void init() throws IOException, StorageQueryException { Cronjobs.addCronjob(this, CleanUpWebauthNExpiredDataCron.init(this, uniqueUserPoolIdsTenants)); // starts the DeadlockLogger if - if (!Config.getBaseConfig(this).isDeadlockLoggerEnabled()) { + if (Config.getBaseConfig(this).isDeadlockLoggerEnabled()) { DeadlockLogger.getInstance().start(); }