Issue Summary
Rate limiting plugin does not work correctly with the jTDS driver, which results in rolling back the transaction in which all plugin upgrade tasks are ran. As a result plugin upgrade task results are not persisted to the database, plugin upgrade task execution status is not written to the database, resulting in repeated runs on every Crowd startup and functionality dependent on plugin upgrade task results, such as 2-Step Verification, does not work correctly.
Steps to Reproduce
- Configure Crowd to use MS SQL database with jTDS driver
- Start up Crowd
Expected Results
Plugin upgrade tasks run normally.
Actual Results
The transaction in which plugin upgrade tasks are executed is rolled back. Any database changes performed in plugin upgrade tasks are not persisted and plugin upgrade task execution status is not stored, meaning that plugin upgrade tasks are retried on every Crowd startup. One known symptom of is inability to configure 2SV due to the encryption key not being configured.
The following exception will be present in the logs, indicating the root cause:
2025-03-12 19:05:53,109 main ERROR [sal.core.lifecycle.DefaultLifecycleManager] LifecycleAware.onStart() failed for component with class 'com.atlassian.ratelimiting.internal.crowd.CrowdRateLimitModificationSettingsService' from plugin 'com.atlassian.ratelimiting.rate-limiting-plugin' java.lang.AbstractMethodError: null at net.sourceforge.jtds.jdbc.JtdsConnection.getSchema(JtdsConnection.java:2881) ~[jtds-1.3.1.jar:1.3.1] at com.mchange.v2.c3p0.impl.NewProxyConnection.getSchema(NewProxyConnection.java:1669) ~[c3p0-0.9.5.5.jar:0.9.5.5] at com.atlassian.crowd.ratelimiting.CrowdConnectionProvider.getSchemaName(CrowdConnectionProvider.java:34) ~[crowd-server-6.2.3.jar:?] at com.atlassian.sal.spring.connection.SpringHostConnectionAccessor.getSchemaName(SpringHostConnectionAccessor.java:69) ~[sal-spring-6.0.5.jar:?] at com.atlassian.sal.core.rdbms.DefaultTransactionalExecutor.getSchemaName(DefaultTransactionalExecutor.java:73) ~[sal-core-6.0.5.jar:?] at com.atlassian.pocketknife.internal.querydsl.schema.ProductSchemaProvider.getProductSchema(ProductSchemaProvider.java:24) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.schema.DefaultSchemaProvider.getProductSchema(DefaultSchemaProvider.java:55) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.dialect.DefaultDialectConfiguration.enrich(DefaultDialectConfiguration.java:80) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.dialect.DefaultDialectConfiguration.buildTemplates(DefaultDialectConfiguration.java:117) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.dialect.DefaultDialectConfiguration.detect(DefaultDialectConfiguration.java:72) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.util.MemoizingResettingReference.lambda$get$0(MemoizingResettingReference.java:57) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.util.MemoizingResettingReference$SmarterMemoizingSupplier.get(MemoizingResettingReference.java:148) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.util.MemoizingResettingReference.safelyGetT(MemoizingResettingReference.java:69) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.util.MemoizingResettingReference.get(MemoizingResettingReference.java:61) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.dialect.DefaultDialectConfiguration.getDialectConfig(DefaultDialectConfiguration.java:61) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.DatabaseConnectionConverterImpl.getDialectConfig(DatabaseConnectionConverterImpl.java:46) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.DatabaseConnectionConverterImpl.convertImpl(DatabaseConnectionConverterImpl.java:40) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.DatabaseConnectionConverterImpl.convertExternallyManaged(DatabaseConnectionConverterImpl.java:34) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.DatabaseAccessorImpl.lambda$execute$0(DatabaseAccessorImpl.java:68) ~[?:?] at com.atlassian.sal.core.rdbms.DefaultTransactionalExecutor.executeInternal(DefaultTransactionalExecutor.java:111) ~[sal-core-6.0.5.jar:?] at com.atlassian.sal.core.rdbms.DefaultTransactionalExecutor.lambda$execute$0(DefaultTransactionalExecutor.java:66) ~[sal-core-6.0.5.jar:?] at com.atlassian.sal.spring.connection.SpringHostConnectionAccessor.lambda$execute$0(SpringHostConnectionAccessor.java:62) ~[sal-spring-6.0.5.jar:?] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.39-atlassian-3.jar:5.3.39-atlassian-3] at com.atlassian.sal.spring.connection.SpringHostConnectionAccessor.execute(SpringHostConnectionAccessor.java:56) ~[sal-spring-6.0.5.jar:?] at com.atlassian.sal.core.rdbms.DefaultTransactionalExecutor.execute(DefaultTransactionalExecutor.java:65) ~[sal-core-6.0.5.jar:?] at com.atlassian.pocketknife.internal.querydsl.DatabaseAccessorImpl.execute(DatabaseAccessorImpl.java:67) ~[?:?] at com.atlassian.pocketknife.internal.querydsl.DatabaseAccessorImpl.runInTransaction(DatabaseAccessorImpl.java:43) ~[?:?] at com.atlassian.ratelimiting.db.internal.dao.QDSLSystemRateLimitingSettingsDao.initializeDbIfNeeded(QDSLSystemRateLimitingSettingsDao.java:39) ~[?:?] at com.atlassian.ratelimiting.internal.configuration.DefaultSystemPropertiesService.initializeData(DefaultSystemPropertiesService.java:64) ~[?:?] at com.atlassian.ratelimiting.internal.settings.RateLimitModificationSettingsService.onStart(RateLimitModificationSettingsService.java:92) ~[?:?] ...
Workaround
Disabling Atlassian Rate Limiting - Plugin (for example via the Manage apps menu) and restarting Crowd will result in a fresh run of the upgrade tasks, which should complete successfully. The plugin can be enabled afterwards, however new plugin upgrade tasks (for example ones from newly installed or upgraded plugins) will not function in such case.