-
Notifications
You must be signed in to change notification settings - Fork 1.1k
GH-3733 Configure TxManager for DefLockRepository #3782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2016-2021 the original author or authors. | ||
* Copyright 2016-2022 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -21,22 +21,25 @@ | |
|
||
import javax.sql.DataSource; | ||
|
||
import org.springframework.beans.BeansException; | ||
import org.springframework.beans.factory.InitializingBean; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.SmartInitializingSingleton; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.context.ApplicationContextAware; | ||
import org.springframework.dao.DuplicateKeyException; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.stereotype.Repository; | ||
import org.springframework.transaction.annotation.Isolation; | ||
import org.springframework.transaction.annotation.Propagation; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.transaction.PlatformTransactionManager; | ||
import org.springframework.transaction.TransactionDefinition; | ||
import org.springframework.transaction.support.DefaultTransactionDefinition; | ||
import org.springframework.transaction.support.TransactionTemplate; | ||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* The default implementation of the {@link LockRepository} based on the | ||
* table from the script presented in the {@code org/springframework/integration/jdbc/schema-*.sql}. | ||
* <p> | ||
* This repository can't be shared between different {@link JdbcLockRegistry} instances. | ||
* Otherwise it opens a possibility to break {@link java.util.concurrent.locks.Lock} contract, | ||
* Otherwise, it opens a possibility to break {@link java.util.concurrent.locks.Lock} contract, | ||
* where {@link JdbcLockRegistry} uses non-shared {@link java.util.concurrent.locks.ReentrantLock}s | ||
* for local synchronizations. | ||
* | ||
|
@@ -49,8 +52,8 @@ | |
* | ||
* @since 4.3 | ||
*/ | ||
@Repository | ||
public class DefaultLockRepository implements LockRepository, InitializingBean { | ||
public class DefaultLockRepository | ||
implements LockRepository, InitializingBean, ApplicationContextAware, SmartInitializingSingleton { | ||
|
||
/** | ||
* Default value for the table prefix property. | ||
|
@@ -89,12 +92,17 @@ public class DefaultLockRepository implements LockRepository, InitializingBean { | |
|
||
private String renewQuery = "UPDATE %sLOCK SET CREATED_DATE=? WHERE REGION=? AND LOCK_KEY=? AND CLIENT_ID=?"; | ||
|
||
private ApplicationContext applicationContext; | ||
|
||
private PlatformTransactionManager transactionManager; | ||
|
||
private TransactionTemplate defaultTransactionTemplate; | ||
|
||
/** | ||
* Constructor that initializes the client id that will be associated for | ||
* all the locks persisted by the store instance to a random {@link UUID}. | ||
* @param dataSource the {@link DataSource} used to maintain the lock repository. | ||
*/ | ||
@Autowired | ||
public DefaultLockRepository(DataSource dataSource) { | ||
this(dataSource, UUID.randomUUID().toString()); | ||
} | ||
|
@@ -124,21 +132,37 @@ public void setRegion(String region) { | |
} | ||
|
||
/** | ||
* Specify the prefix for target data base table used from queries. | ||
* @param prefix the prefix to set (default INT_). | ||
* Specify the prefix for target database table used from queries. | ||
* @param prefix the prefix to set (default {@code INT_}). | ||
*/ | ||
public void setPrefix(String prefix) { | ||
this.prefix = prefix; | ||
} | ||
|
||
/** | ||
* Specify the time (in milliseconds) to expire dead locks. | ||
* @param timeToLive the time to expire dead locks. | ||
* Specify the time (in milliseconds) to expire dead-locks. | ||
* @param timeToLive the time to expire dead-locks. | ||
*/ | ||
public void setTimeToLive(int timeToLive) { | ||
this.ttl = timeToLive; | ||
} | ||
|
||
/** | ||
* Set a {@link PlatformTransactionManager} for operations. | ||
* Otherwise, a primary {@link PlatformTransactionManager} bean is obtained | ||
* from the application context. | ||
* @param transactionManager the {@link PlatformTransactionManager} to use. | ||
* @since 6.0 | ||
*/ | ||
public void setTransactionManager(PlatformTransactionManager transactionManager) { | ||
this.transactionManager = transactionManager; | ||
} | ||
|
||
@Override | ||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { | ||
this.applicationContext = applicationContext; | ||
} | ||
|
||
@Override | ||
public void afterPropertiesSet() { | ||
this.deleteQuery = String.format(this.deleteQuery, this.prefix); | ||
|
@@ -150,50 +174,72 @@ public void afterPropertiesSet() { | |
this.renewQuery = String.format(this.renewQuery, this.prefix); | ||
} | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW) | ||
@Override | ||
public void afterSingletonsInstantiated() { | ||
if (this.transactionManager == null) { | ||
this.transactionManager = this.applicationContext.getBean(PlatformTransactionManager.class); | ||
} | ||
this.defaultTransactionTemplate = | ||
new TransactionTemplate(this.transactionManager, | ||
new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW)); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
this.template.update(this.deleteAllQuery, this.region, this.id); | ||
this.defaultTransactionTemplate.executeWithoutResult( | ||
transactionStatus -> this.template.update(this.deleteAllQuery, this.region, this.id)); | ||
} | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW) | ||
@Override | ||
public void delete(String lock) { | ||
this.template.update(this.deleteQuery, this.region, lock, this.id); | ||
this.defaultTransactionTemplate.executeWithoutResult( | ||
transactionStatus -> this.template.update(this.deleteQuery, this.region, lock, this.id)); | ||
} | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) | ||
@Override | ||
public boolean acquire(String lock) { | ||
if (this.template.update(this.updateQuery, this.id, new Date(), this.region, lock, this.id, | ||
new Date(System.currentTimeMillis() - this.ttl)) > 0) { | ||
return true; | ||
} | ||
try { | ||
return this.template.update(this.insertQuery, this.region, lock, this.id, new Date()) > 0; | ||
} | ||
catch (DuplicateKeyException e) { | ||
return false; | ||
} | ||
DefaultTransactionDefinition transactionDefinition = | ||
new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW); | ||
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not a field? |
||
return new TransactionTemplate(this.transactionManager, transactionDefinition) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we store this template in a field too? |
||
.execute(transactionStatus -> { | ||
if (this.template.update(this.updateQuery, this.id, new Date(), this.region, lock, this.id, | ||
new Date(System.currentTimeMillis() - this.ttl)) > 0) { | ||
return true; | ||
} | ||
try { | ||
return this.template.update(this.insertQuery, this.region, lock, this.id, new Date()) > 0; | ||
} | ||
catch (DuplicateKeyException e) { | ||
return false; | ||
} | ||
}); | ||
} | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) | ||
@Override | ||
public boolean isAcquired(String lock) { | ||
return this.template.queryForObject(this.countQuery, Integer.class, // NOSONAR query never returns null | ||
this.region, lock, this.id, new Date(System.currentTimeMillis() - this.ttl)) == 1; | ||
DefaultTransactionDefinition transactionDefinition = | ||
new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW); | ||
transactionDefinition.setReadOnly(true); | ||
return new TransactionTemplate(this.transactionManager, transactionDefinition) | ||
.execute(transactionStatus -> | ||
this.template.queryForObject(this.countQuery, // NOSONAR query never returns null | ||
Integer.class, this.region, lock, this.id, new Date(System.currentTimeMillis() - this.ttl)) | ||
== 1); | ||
} | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW) | ||
@Override | ||
public void deleteExpired() { | ||
this.template.update(this.deleteExpiredQuery, this.region, new Date(System.currentTimeMillis() - this.ttl)); | ||
this.defaultTransactionTemplate.executeWithoutResult( | ||
transactionStatus -> | ||
this.template.update(this.deleteExpiredQuery, this.region, | ||
new Date(System.currentTimeMillis() - this.ttl))); | ||
} | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW) | ||
@Override | ||
public boolean renew(String lock) { | ||
return this.template.update(this.renewQuery, new Date(), this.region, lock, this.id) > 0; | ||
return this.defaultTransactionTemplate.execute( | ||
transactionStatus -> this.template.update(this.renewQuery, new Date(), this.region, lock, this.id) > 0); | ||
} | ||
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe try/catch with a friendly message to indicate if there is not one unique TM in the context, the registry needs one to be passed in?