I want to build a custom AutoCloseable class so I can turn this:
try {
begin();
doThings();
commit();
} finally {
if (transactionIsActive()) rollback();
}
into the easier
try (Transaction t = begin()) { // too bad I have to store it in t though I don't use it
doThings();
}
Transaction would be the AutoCloseable here and in close() it would commit or rollback the transaction as appropriate.
But to make that work, I would need to detect in Transaction.close() whether an exception occurred inside the try block or it completed normally. Is this possible at all?
If it requires parsing the stack trace from a new exception, that's OK. Easier programming would be worth the tiny performance hit that brings.
The closest I could come up with still requires marking the success of the transaction manually as the last statement of the block:
class Transaction implements AutoCloseable {
private boolean rollback = true;
public void success() {
rollback = false;
}
public void close() {
if (rollback) doRollback();
else doCommit();
// …
}
}
class Main {
public static void main(String[] args) {
try (Transaction t = new Transaction()) {
doThings();
t.success();
}
}
}
Although my code is different from yours, I had a similar need to automatically commit (most) transactions and rollback on errors.
Most of the time my code is sprinkled with simple queries that get rolled back automatically, like this:
try(Transaction t : database.beginTransaction()) {
return t.selectUnique(Employee.class, "id=?", 200);
} // implicit rollback here
Some databases donot like queries being rolled back like this, so I've solved this by distinguishing between "write" and "read" transactions. If it is a read transaction, close() will commit it, otherwise rollback. It will also check you are not doing any write actions when you created a read-only transaction. So now I can write:
try(Transaction t : database.beginReadOnlyTransaction()) {
return t.selectUnique(Employee.class, "id=?", 200);
} // implicit commit here
Write transactions still need to call commit themselves at the end, but this is a minority of cases.
I realize this is not what you asked for, but perhaps it is useful still.
The closest I've been able to get is to explicitly call commit(), and assume that any code that exits the transaction block without doing so should be rolled back. This is consistent with transactions in other languages. While you can forget to call commit() (as I often do), at least this part of the code is very likely to get tested. And, it is impossible to forget to rollback on exception, which is less likely to have test coverage.
This is similar to millimoose's idea of setting a flag:
try (Transaction t = new Transaction()) {
doThings();
t.success();
}
except that you just use the active state as the flag. Same amount of code, no new flag necessary. This assumes that any transaction for which commit() has not been explicitly called should be rolled back, resulting in code like this:
try (Transaction t = new Transaction()) {
doThings();
t.commit(); // marks the transaction as successful...
}
class Transaction implements AutoCloseable {
public void close() {
if (isActive())
doRollback();
}
...
}
I still can't believe there is not a cleaner solution to this in the core language.
Related
In our application situation is, user has requested to update 2 fields say (A, B) for an account.
An account has multiple stores to which updated fields to be pushed. One of the store is marked as default store.
Field A has some validations to be done for default store (say limit on quantity.).
If validation fails I am throwing exception.
On success field value is added to store_space_table
Field B must be pushed to all stores. Pushing to store can throw exception when that store is down or unreachalbe.
Currently I have written this code in finally block.
I don't want to rollback the first operation on the exception in the second step. Rather I want to consolidate the exception of step 1 and step 2 and propagate that.
void validateFieldAndPushToStore(List<Field> inputFieldList, Account account) throws ServiceException {
List<Store> allStoresOfAccount = getAllStoresOfAccount(account);
Set<Store> storeListToPushData = new HashSet<>();
try{
if(ifFieldAUpdated(inputFieldList)) {
// get default store from list of stores of an account,
Store defaultStore = getDefaultStore(allStoresOfAccount)
// Validate space availability of A on default store, if validation is successful, then update data in store_space_table
validateSpaceOnDefaultStoreForFieldA(defaultStore);
storeListToPushData.add(defaultStore);
}
} finally {
if( ifFieldBUpdated(inputFieldList) ) {
storeListToPushData.addAll(allStoresOfAccount);
}
if( ! storeListToPushData.isEmpty()) {
// This operation reads fields A from DB (store_space_table), reads field B from field_tbl and push to stores.
pushUpdatesToStores(account, storeListToPushData);
}
}
}
As I read on multiple forums, such handling in finally is not correct/efficient. So I am looking for alternative or better approach to handle this situation.
The two updates should be wrapped in a transaction.
#Transaction is a nutshell.
Your service should be structured as follows.
#Transactional
public void validateFieldAndPushToStore(A a, B b) {
serviceA.validateAndPushA(a);
serviceB.validateAndPushB(b);
}
Where the implementations for serviceA and serviceB would be.
#Transactional
public void validateAndPushA(A a){
validate(a); // can throw validation exception from here
persist(a); // can throw persistence exception from here
}
#Transactional
public void validateAndPushB(B b){
validate(b); // can throw validation exception from here
persist(b); // can throw persistence exception from here
}
Please note the #Transactional on top of validateAndPushA and validateAndPushB.
The persist methods should be annotated with #Transactional as well.
If you would structure your code in this way, if any validation or persistence exception would occur, all database changes will be rolled back. This happens because #Transactional has a property called propagationLevel, which if left on the default value will execute any inner transaction (for example those of the persist operations) in a single outer transaction (i.e. validateAndPushA, validateAndPushB, validate and persist will all execute in the same transaction - so any exception thrown by those methods will result in the whole transaction being rollbacked).
#Transactional allows for a lot of fine tuning, such as for which exceptions the transaction should not be rolled back. Please consult the documentation for all the details.
Hope this helps!
As you have 2 operations which are supposed to get executed even if one of the fails and you also want to propagate the exception/error to above layer, following approach can be used-
void methodPerformingTwoOperations() throws Exception {
Exception firstOperationErrror = null;
Exception secondOperationError = null;
try {
performFirstOperation();
} catch(Exception e) {
firstOperationError = e;
}
try {
performSecondOperation();
} catch(Exception e) {
secondOperationError = e;
}
throwExceptionAsPerErrors(firstOperationError, secondOperationError);
}
void throwExceptionAsPerErrors(Exception firstOperationError, Exception secondOperationError) {
// depending upon exceptions compose and throw new exception from here
}
Update
Note that when using #Transactional always validate what parameters you are passing to this annotation. By default propagation property is set to REQUIRED which means all the transactions will run in the same transaction and if exception specified all will be reverted.
If you want to persist data from one operation even if other one fails then you can apply Transactional on the inner methods(but not on the main method). Please refer to the code from the answer by #alexrolea.
Capture the exception instead of using finally:
boolean failed = false;
try {
} catch (YourException ex) {
failed = true;
}
if (failed) {
}
If you want the exception to be propagated you can store it an a variable and then re-throw it.
So, here is some background info: I'm currently working at a company providing SaaS and my work involves writing methods using JDBC to retrieve and process data on a database. Here is the problem, most of the methods comes with certain pattern to manage connection:
public Object someMethod(Object... parameters) throws MyCompanyException{
try{
Connection con = ConnectionPool.getConnection();
con.setAutoCommit(false);
// do something here
con.commit();
con.setAutoCommit(true);
}
catch(SomeException1 e){
con.rollback();
throw new MyCompanyException(e);
}
catch(SomeException2 e){
con.rollback();
throw new MyCompanyException(e);
}
// repeat until all exception are catched and handled
finally {
ConnectionPool.freeConnection(con);
}
// return something if the method is not void
}
It had been already taken as a company standard to do all methods like this, so that the method would rollback all changes it had made in case of any exception is caught, and the connection will also be freed asap. However, from time to time some of us may forget to do some certain routine things when coding, like releasing connection or rollback when error occurs, and such mistake is not quite easily detectable until our customers complaint about it. So I've decided to make these routine things be done automatically even it is not declared in the method. For connection initiation and set up, it can be done by using the constructor easily.
public abstract SomeAbstractClass {
protected Connection con;
public SomeAbstractClass() {
con = CoolectionPool.getConnection();
con.setAutoCommit(false);
}
}
But the real problem is to make connection to be released automatically immediately after finishing the method. I've considered using finalize() to do so, but this is not what I'm looking for as finalize() is called by GC and that means it might not finalize my object when the method is finished, and even when the object will never be referenced. finalize() is only called when JVM really run out of memory to go on.
Is there anyway to free my connection automatically and immediately when the method finishes its job?
Use "try with resources". It is a programming pattern such that you write a typical looking try - catch block, and if anything goes wrong or you exit it, the resources are closed.
try (Connection con = ConnectionPool.getConnection()) {
con.doStuff(...);
}
// at here Connection con is closed.
It works by Connection extending Closeable, and if any class within the "resource acquisition" portion of the try statement implements Closeable then the object's close() method will be called before control is passed out of the try / catch block.
This prevents the need to use finally { ... } for many scenarios, and is actually safer than most hand-written finally { ... } blocks as it also accommodates exceptions throw in the catch { ... } and finally { ... } blocks while still closing the resource.
One of the standard ways to do this is using AOP. You can look at Spring Framework on how it handles JDBC tansactions and connections and manages them using MethodInterceptor. My advice is to use Spring in your project and not reinvent the wheel.
The idea behind MethodInterceptor is that you add a code that creates and opens connection before JDBC related method is called, puts the connection into the thread local so that your method can get the connection to make SQL calls, and then closes it after the method is executed.
You could add a method to your ConnectionPool class for example:
public <T> T execute(Function<Connection, T> query,
T defaultValue,
Object... parameters) {
try {
Connection con = ConnectionPool.getConnection();
con.setAutoCommit(false);
Object result = query.apply(conn);
con.commit();
con.setAutoCommit(true);
return result;
} catch(SomeException1 e) {
con.rollback();
throw new MyCompanyException(e);
}
//etc.
finally {
ConnectionPool.freeConnection(con);
}
return defaultValue;
}
And you call it from the rest of your code with:
public Object someMethod(Object... parameters) throws MyCompanyException {
return ConnectionPool.execute(
con -> { ... }, //use the connection and return something
null, //default value
parameters
);
}
I have this scenario:
fetch (read and delete) a record from IncomingMessage table
read record content
insert something to some tables
if an error (any exception) occurred in steps 1-3, insert an error-record to OutgoingMessage table
otherwise, insert an success-record to OutgoingMessage table
So steps 1,2,3,4 should be in a transaction, or steps 1,2,3,5
My process starts from here (it is a scheduled task):
public class ReceiveMessagesJob implements ScheduledJob {
// ...
#Override
public void run() {
try {
processMessageMediator.processNextRegistrationMessage();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
My main function (processNextRegistrationMessage) in ProcessMessageMediator:
public class ProcessMessageMediatorImpl implements ProcessMessageMediator {
// ...
#Override
#Transactional
public void processNextRegistrationMessage() throws ProcessIncomingMessageException {
String refrenceId = null;
MessageTypeEnum registrationMessageType = MessageTypeEnum.REGISTRATION;
try {
String messageContent = incomingMessageService.fetchNextMessageContent(registrationMessageType);
if (messageContent == null) {
return;
}
IncomingXmlModel incomingXmlModel = incomingXmlDeserializer.fromXml(messageContent);
refrenceId = incomingXmlModel.getRefrenceId();
if (!StringUtil.hasText(refrenceId)) {
throw new ProcessIncomingMessageException(
"Can not proceed processing incoming-message. refrence-code field is null.");
}
sqlCommandHandlerService.persist(incomingXmlModel);
} catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
// send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,
ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
// send success outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId, ProcessResultStateEnum.SUCCEED.getCode());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
}
private void saveOutgoingMessage(OutgoingXmlModel outgoingXmlModel, MessageTypeEnum messageType)
throws ProcessIncomingMessageException {
String xml = outgoingXmlSerializer.toXml(outgoingXmlModel, messageType);
OutgoingMessageEntity entity = new OutgoingMessageEntity(messageType.getCode(), new Date());
try {
outgoingMessageService.save(entity, xml);
} catch (SaveOutgoingMessageException e) {
throw new ProcessIncomingMessageException("Can not proceed processing incoming-message.", e);
}
}
// ...
}
As i said If any exception occurred in steps 1-3, i want insert an error-record:
catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
//send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
It's SqlCommandHandlerServiceImpl.persist() method:
public class SqlCommandHandlerServiceImpl implements SqlCommandHandlerService {
// ...
#Override
#Transactional
public void persist(IncomingXmlModel incomingXmlModel) {
Collections.sort(incomingXmlModel.getTables());
List<ParametricQuery> queries = generateSqlQueries(incomingXmlModel.getTables());
for (ParametricQuery query : queries) {
queryExecuter.executeQuery(query);
}
}
// ...
}
But when sqlCommandHandlerService.persist() throws exception (here a org.hibernate.exception.ConstraintViolationException exception), after inserting an error-record in OutgoingMessage table, when the transaction want to be committed , i get UnexpectedRollbackException. I can't figure out where is my problem:
Exception in thread "null#0" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at ir.tamin.branch.insuranceregistration.services.schedular.ReceiveMessagesJob$$EnhancerByCGLIB$$63524c6b.run(<generated>)
at ir.asta.wise.core.util.timer.JobScheduler$ScheduledJobThread.run(JobScheduler.java:132)
I'm using hibernate-4.1.0-Final, My database is oracle, and Here is my transaction-manager bean:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
This is the normal behavior and the reason is that your sqlCommandHandlerService.persist method needs a TX when being executed (because it is marked with #Transactional annotation). But when it is called inside processNextRegistrationMessage, because there is a TX available, the container doesn't create a new one and uses existing TX. So if any exception occurs in sqlCommandHandlerService.persist method, it causes TX to be set to rollBackOnly (even if you catch the exception in the caller and ignore it).
To overcome this you can use propagation levels for transactions. Have a look at this to find out which propagation best suits your requirements.
Update; Read this!
Well after a colleague came to me with a couple of questions about a similar situation, I feel this needs a bit of clarification.
Although propagations solve such issues, you should be VERY careful about using them and do not use them unless you ABSOLUTELY understand what they mean and how they work. You may end up persisting some data and rolling back some others where you don't expect them to work that way and things can go horribly wrong.
EDIT Link to current version of the documentation
The answer of Shyam was right. I already faced with this issue before. It's not a problem, it's a SPRING feature. "Transaction rolled back because it has been marked as rollback-only" is acceptable.
Conclusion
USE REQUIRES_NEW if you want to commit what did you do before exception (Local commit)
USE REQUIRED if you want to commit only when all processes are done (Global commit) And you just need to ignore "Transaction rolled back because it has been marked as rollback-only" exception. But you need to try-catch out side the caller processNextRegistrationMessage() to have a meaning log.
Let's me explain more detail:
Question: How many Transaction we have? Answer: Only one
Because you config the PROPAGATION is PROPAGATION_REQUIRED so that the #Transaction persist() is using the same transaction with the caller-processNextRegistrationMessage(). Actually, when we get an exception, the Spring will set rollBackOnly for the TransactionManager so the Spring will rollback just only one Transaction.
Question: But we have a try-catch outside (), why does it happen this exception?
Answer Because of unique Transaction
When persist() method has an exception
Go to the catch outside
Spring will set the rollBackOnly to true -> it determine we must
rollback the caller (processNextRegistrationMessage) also.
The persist() will rollback itself first.
Throw an UnexpectedRollbackException to inform that, we need to rollback the caller also.
The try-catch in run() will catch UnexpectedRollbackException and print the stack trace
Question: Why we change PROPAGATION to REQUIRES_NEW, it works?
Answer: Because now the processNextRegistrationMessage() and persist() are in the different transaction so that they only rollback their transaction.
Thanks
I am running a Play! application and am debugging a deadlock.
The error messages I see logged from Play! are:
Deadlock found when trying to get lock; try restarting transaction
Could not synchronize database state with session
org.hibernate.exception.LockAcquisitionException: Could not execute JDBC batch update
From the Play! Documentation
Play will automatically manage transactions for you. It will start a transaction for each HTTP request and commit it when the HTTP response is sent. If your code throws an exception, the transaction will automatically rollback.
From the MySQL Documentation
you must write your applications so that they are always prepared to re-issue a transaction if it gets rolled back because of a deadlock.
My question:
How and where in my Play! application can I catch these rolled back transactions, and handle them (choose to reissue them, ignore them, etc...)?
Updated
While I ended up taking the advice in the accepted answer, and looking for the cause of the deadlock (did you know MySQL foreign key constraints increase the chance of deadlocking? Now I do!), here is some code that was working for me to catch and reissue a failed save.
boolean success = false;
int tries = 0;
while (!success && tries++ < 3) {
try {
updated.save();
success = true;
} catch (javax.persistence.PersistenceException e) {
pause(250);
}
}
You can use custom Enhancer and enhance all controllers in your plugin.
For example, enhancer add catch block and restart request invocation. It is more reliable for restart request then only part of logic inside controller:
package plugins;
..
final public class ReliableTxPlugin extends PlayPlugin {
public void enhance(final ApplicationClasses.ApplicationClass applicationClass) throws Exception {
new TxEnhancer().enhanceThisClass(applicationClass);
}
}
package enhancers;
..
class TxEnhancer extends Enhancer {
public static void process(PersistenceException e) throws PersistenceException {
final Throwable cause = e.getCause();
if (cause instanceof OptimisticLockException || cause instanceof StaleStateException) {
final EntityTransaction tx = JPA.em().getTransaction();
if (tx.isActive()) {
tx.setRollbackOnly();
}
Http.Request.current().isNew = false;
throw new Invoker.Suspend(250);
}
throw e;
}
public void enhanceThisClass(final ApplicationClass applicationClass) throws Exception {
// .. general encahcer code
ctMethod.addCatch("enhancers.TxEnhancer.process(_e);return;",
classPool.makeClass("javax.persistence.PersistenceException"), "_e");
//..
}
}
In most cases of a deadlock you can only do a rollback and try a restart. How ever in a Webapp this should be a really unusual case. What you can do in Play is
catch the exception
handle the Transaction in your code
With JPA.em() you will get the EntityManager. You can look into JPAPlugin to see how play handle the transaction. How ever first of all I would evaluate why there is a deadlock and if this is really a realistic situation which can the server handle intelligent.
I built a play1 module based on #xedon work doing the retry for you.
I have a cache that gets loaded upfront with a large amount of data (by a background thread) and is unusable until full (it will also get reloaded every so often and be unusable during that load). I want the classes that use it to check a flag isLoaded() before accesses. I use a ReentrantReadWriteLock (I omit this in the code for simplicity) for access control like this:
public class Cache {
private volatile boolean loaded = false; //starts false
private static String[] cache;
private static Lock readLock;
private static Lock writeLock;
public Object get(Object key) {
if (!readLock.tryLock()) throw IllegalStateException(...);
try {
... do some work
} finally {
readLock.unlock();
}
}
// called by background thread
private void loadFull() {
loaded = false;
writeLock.lock()
try {
cache = new String[];
... fill cache
} finally {
writeLock.unlock();
loaded = true;
}
}
....
}
Now in my other class I have a block like this:
if (cache.isLoaded()) {
try {
Object x = cache.get(y);
} catch (IllegalStateException iex) {
// goto database for object
}
} else {
// goto database for object
}
Do I really need the try/catch? Is it ever possible that the flag will be set to false and the readLock try() will fail? Should I even bother with the flag and jut catch the Exception (since I basically do the same code if the Exception is thrown as if the flag is false). I just feel like I am doing something slightly wrong but I can't put my finger on it. Thanks.
Do I really need the try/catch? Is it
ever possible that the flag will be
set to false and the readLock try()
will fail?
Yes, you need it. Between the time cache.isLoaded() and cache.get() are called, a writer can come in and get the write lock - in which case cache.isLoaded() will return true, but cache.get() will throw the exception.
Should I even bother with the flag and
jut catch the Exception (since I
basically do the same code if the
Exception is thrown as if the flag is
false).
From the code you have shown, the exception is thrown only in cases where the get fails to acquire the read lock. Acquisition of the read lock fails only if there is a concurrent writer at the time. isLoaded also returns false in precisely this scenario. So just relying on the exception would suffice. Also, consider creating a specialized CacheStaleException.
The tryLock will fail if some other thread has already acquired that lock. This typically means that an exception would be thrown if a client fails to acquire a lock due to high contention (multiple clients accessing the same cache). Is there any fallback strategy you have implemented in your client layer which deals with such situations?
Also, why static locks? I think that even though your cache is typically used in the application as a singleton, there is no need to limit its usability by making Locks static.
No, but to be honest your paradigm is confusing. Presumably it is expensive to go to the actual database and that is the purpose of the cache. In the case that the cache is being reloaded, is it not better to just wait until it is?
Assuming you really do want to go to the database if the read lock is not immediately available, I would do this:
public Object get(Object key) {
Object returnValue;
if (readLock.tryLock()) {
try {
... do some work
returnValue = ...
} finally {
readLock.unlock();
}
} else {
//go to database
returnValue = ...
}
return returnValue;
}