I have a method in CDI bean which is transactional, on error it creates an entry in database with the exception message. This method can be called by RESTendpoint and in multithread way.
We have a SQL constraint to avoid duplicity in database
#Transactional
public RegistrationRuleStatus performCheck(RegistrationRule rule, User user) {
try {
//check if rule is dependent of other rules and if all proved, perform check
List<RegistrationRule> rules = rule.getRuleParentDependencies();
boolean parentDependenciesAreProved = true;
if (!CollectionUtils.isEmpty(rules)) {
parentDependenciesAreProved = ruleDao.areParentDependenciesProved(rule,user.getId());
}
if (parentDependenciesAreProved) {
Object service = CDI.current().select(Object.class, new NamedAnnotation(rule.getProvider().name())).get();
Method method = service.getClass().getMethod(rule.getProviderType().getMethod(), Long.class, RegistrationRule.class);
return (RegistrationRuleStatus) method.invoke(service, user.getId(), rule);
} else {
RegistrationRuleStatus status = statusDao.getStatusByUserAndRule(user, rule);
if (status == null) {
status = new RegistrationRuleStatus(user, rule, RegistrationActionStatus.START, new Date());
statusDao.create(status);
}
return status;
}
} catch (Exception e) {
LOGGER.error("could not perform check {} for provider {}", rule.getProviderType().name(), rule.getProvider().name(), e.getCause()!=null?e.getCause():e);
return statusDao.createErrorStatus(user,rule,e.getCause()!=null?e.getCause().getMessage():e.getMessage());
}
}
create Error method:
#Transactional
public RegistrationRuleStatus createErrorStatus(User user, RegistrationRule rule, String message) {
RegistrationRuleStatus status = getStatusByUserAndRule(user, rule);
if (status == null) {
status = new RegistrationRuleStatus(user, rule, RegistrationActionStatus.ERROR, new Date());
status.setErrorCode(CommonPropertyResolver.getMicroServiceErrorCode());
status.setErrorMessage(message);
create(status);
}else {
status.setStatus(RegistrationActionStatus.ERROR);
status.setStatusDate(new Date());
status.setErrorCode(CommonPropertyResolver.getMicroServiceErrorCode());
status.setErrorMessage(message);
update(status);
}
return status;
}
the problem is method is called twice at same time and the error recorded is DuplicateException but we don't want it. We verify at the beginning if object already exists, but I think it is called at exactly same time.
JAVA8/wildfly/CDI/JPA/eclipselink
Any idea?
I'd suggest you to consider following approaches:
1) Implement retry logic. Catch exception, analyze it. If it indicates an unexpected duplicate (like you described), then don't consider it as an error and just repeat the method call. Now your code will work differently: It will notice that a record already exists and will not create a duplicate.
2) Use isolation level SERIALIZABLE. Then within a single transaction your will "see" a consistent behaviour: If select operation hasn't found a particular record, then till the end of this transaction no other transaction will insert such record and there will be no exception related to duplicates. But the price is that the whole table will be locked for each such transaction. This can degrade the application performance essentially.
Related
We are having trouble to keep a transaction running when a exception occurs and also keep rows locked when this happens.
Firs, a bit of code to clarify.
With this query we are performing a for update skip locked (JobDao):
#NotNull
default Optional<EventEntity> getJobForProcessing(#NotNull final Instant now) {
final List<EventEntity> events = getUnprocessedEvent(now, PageRequest.of(0, 1));
if (events.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(events.get(0));
}
#NotNull
#Query("""
select ee from EventEntity ee
where ee.processed = false
order by ee.createdAt""")
#Lock(LockModeType.PESSIMISTIC_WRITE)
#QueryHints({#QueryHint(name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED + "")})
Optional<EventEntity> getJobForProcessing(#NotNull final Instant now, #NotNull final Pageable pageable);
The following code runs on another thread using #Async (JobTransaction called from scheduler):
#Transactional
public void runInTransaction() {
final Instant now = Now.getInstantUtc();
final Optional<Job> jobForProcessing = dao.getJobForProcessing(now);
if (jobForProcessing.isPresent()) {
final Job job = jobForProcessing.get();
jobProcessor.processJob(job);
} else {
Log.d(TAG, "No more jobs, stopping thread %s.".formatted(Thread.currentThread().getName()));
}
}
The processJob(job) method — simplified — is the following (JobProcessor):
private void processJob(#NotNull final Job job) {
try {
worker.execute(job);
jobService.markJobAsProcessed(job);
} catch (final Throwable throwable) {
Log.e(TAG, "Error to process job: " + job.getId(), throwable);
jobService.rescheduleJobExecution(job, throwable);
throw new RuntimeException(throwable);
}
}
On the above code, the important part is the catch block that calls jobService.rescheduleJobExecution(...).
This is the rescheduleJobExecution (JobService):
public void rescheduleJobExecution(#NotNull final Job job,
#NotNull final Throwable throwable) {
final var nextRetryAt = backoffCalculator.calculateNextRetryAt(job, now);
final var stackTraceAsString = Throwables.getStackTraceAsString(throwable);
final var executionAttempts = job.getExecutionAttempts() + 1;
final var newJob = job.cloneEntity()
.withExecutionAttempts(executionAttempts)
.withErrorLogLastRetry(stackTraceAsString)
.withNextRetryAt(nextRetryAt)
.build();
jobDao.save(newJob);
}
The problem I'm facing is that, when one error occurs and we try to reschedule the job, the following is throw: Transaction silently rolled back because it has been marked as rollback-only.
I understand why this happens, but have not found any way to fix this. What I already tried:
Change the runInTransaction to #Transactional(noRollbackFor = RuntimeException.class);
Add #Transactional(propagation = Propagation.REQUIRES_NEW) on the rescheduleJobExecution method. This solves the problem but brings a new one: since the outer transaction has already failed, the row is available to other threads to select and in a multithread environment this could lead to race conditions (ie: the job is reselected to execution before the reschedule is done).
So, I'm looking for a solution that enables me to deal with job rescheduling and also that keep jobs safe on a multithread environment. The getUnprocessedEvent method is used by many threads and we also like to keep using the for update skip locked that postgres provides.
public WHATTOWRITEHERE test()
{
try
{
transaction.begin();
code which may trigger exception
transaction.commit();
return true;
}
catch (javax.script.ScriptException ex)
{
transaction.rollback();
return ex.getMessage();
}
}
the code above intend to do something, if its OK then return true if not (error happened), this error message string should be returned. It do possible with Php but not with Java
EDIT: expection cant go outside, it has to be handled right here.
You can't return multiple types but you can redesign so you don't have to. Some possibilities:
Don't return an error message. Throw or rethrow an exception instead and let the caller handle it.
Create some class that can encapsulate a success and error state and all related info, return an instance of that.
I recommend option 1. You're already handling an exception, you can see its use for it error handling. No reason to stop it in its tracks there, handle any local cleanup then keep it going up to the caller.
Some hastily constructed examples now that I'm back at a keyboard, intended only to illustrate concepts, not to be exhaustive or necessarily used verbatim:
Cleanup then rethrow:
public boolean test () throws javax.script.ScriptException {
try {
transaction.begin();
...
transaction.commit();
return true;
} catch (javax.script.ScriptException ex) {
transaction.rollback();
throw ex;
}
}
Clean up then rethrow a different exception type if needed:
public boolean test () throws MyGreatException {
try {
transaction.begin();
...
transaction.commit();
return true;
} catch (javax.script.ScriptException ex) {
transaction.rollback();
throw new MyGreatException(ex);
}
}
Return an object that provides status information (this is just a quick example of the general idea):
public class TransactionResult {
private final boolean failed;
private final String reason;
/** Construct a result that represents a successful transaction. */
public TransactionResult () {
failed = false;
reason = null;
}
/** Construct a result that represents a failed transaction with a reason. */
public TransactionResult (String failedReason) {
failed = true;
reason = failedReason;
}
public boolean isFailed () {
return failed;
}
public String getReason () {
return reason;
}
}
And then:
public TransactionResult test () {
TransactionResult result;
try {
transaction.begin();
...
transaction.commit();
result = new TransactionResult();
} catch (javax.script.ScriptException ex) {
transaction.rollback();
result = new TransactionResult(ex.getMessage());
}
return result;
}
Etc.
Don't return anything. Just re-throw the original exception after you roll-back.
public void test()
{
try
{
transaction.begin();
code which may trigger exception
transaction.commit();
}
catch (javax.script.ScriptException ex)
{
transaction.rollback();
throw ex; // re-throw the original exception
}
}
If you insist, you can return Object. In that case, true will be autoboxed to Boolean.TRUE. It’s certainly not recommended, and it will give the caller some extra trouble figuring out whether the returned object is a String or a Boolean. To make matters worse, the caller has no guarantee that return types are limited to the mentioned two, but should also take into account that it could be yet another class.
Better options depend on the situation, so I probably cannot tell you what’s best. A couple of ideas spring to mind, but please don’t use uncritically: (1) Return String, and return null instead of true on success. (2) Design your own return class; for instance, it may hold both a boolean and a message string.
UGLY Workaround but if you really want to do this you can always define a Helper class which wraps status and Error Message, but I would prefer #JsonC's approach.
// Helper class
class Pair<First,Second>{
private First first;
private Second second;
Pair(First first,Second second){
this.first = first;
this.second = second;
}
public First getFirst(){ return this.first; }
public First getSecond(){ return this.second; }
}
// Function returning two types
public Pair<boolean,String> returnSomething(){
try {
return new Pair<boolean,String>(true,null);
}catch(Exception e){
return new Pair<boolean,String>(false,e.getMessage());
}
}
// Calling this method would look like this
Pair<String,boolean> result = returnSomething();
// Retrieve status
boolean status = result.getFirst();
// Retrieve error message (This is null if an exception was caught!)
String errorMessage = result.getSecond();
Exceptions can't go outside, it has to be handled here.
I must say this restriction can only make the interface more difficult to use. Assume you want to return something for the caller to check whether an exception happened in this method, while the caller can ignore the returned value no matter what. So I guess you want to give the caller some flexibility: that he/she doesn't need to bother with the final result if possible. But with the exception approach the caller can still do that, with empty (not recommended) catch clauses.
Exception is the best approach here. Unless "outside" is an environment where exceptions are not supported. Then you have no choice but to come up with something like Try in Scala.
In your case, exceptions should probably be used, not hidden. It's not a result but an error. Learn how to do exception handling in transactions!
Functional programming fanboys will advocate a Monad-like structure, as you can find in the Optional<T> API of Java 8.
I.e. you could return Optional<String> and leave it unset on success (if you do not have a return false and a return true).
For clarity it would be better to build something like this instead with custom classes:
interface Result {}
class BooleanResult implements Result {
boolean result;
public boolean getResult() { return result; }
}
class ErrorResult implements Result {
Exception cause;
public Exception getCause() { return cause; }
}
You could emulate Optional with null values (if you have only one boolean result). On success, return null. Non-null values indicate errors.
String perform() {
try{
...
return null; // No error
} except(Exception e) { // bad code style
return e.getMessage(); // Pray this is never null
}
}
String err = perform();
if (err != null) { throw up; }
Similar APIs are fairly common in old C libraries. Any return value except 0 is an error code. On success, the results are written to a pointer provided at the method call.
You could use Object.
public Object perform() {...}
Object o = perform();
if (o instanceof Boolean) { ...
This is 1980s programming style. This is what PHP does, so it actually is possible in Java! It's just bad because it is no lpnger type safe. This is the worst choice.
I suugest your try 1., 3., 2., 4., 5. in this preference. Or better, only consider the options 1 and 3 at all.
As for option 1. you really should learn how to use try-with-resources. Your transaction is a resource.
When done right, your code will look like this:
try(Transaction a = connection.newTransaction()) {
doSomethingThatMayFail(a);
a.commit();
} // No except here, let try handle this properly
Java will call a.close() even if an exception occurs. Then it will throw the exception upwards. Sour transaction class should have code like this to take care of the rollback:
public void close() {
if (!committed) rollback();
}
This is the most elegant and shortest and safe-to-use approach, as Java ensures close() is called. Throw the Exception, then properly handle it. The code snipped you showed above is an anti-pattern, and known to be very error prone.
If you are using Java 8 you could return an Optional<String>. Then if the code succeeds you return an empty Optional and if there is a failure you return an optional wrapping the failure message.
It has been known that you must use the following pattern in order to update an order in ATG Form-Handlers that doesn't inherit from the PurchaseProcessFormHanlder:
boolean acquireLock = false;
ClientLockManager lockManager = getLocalLockManager();
try {
acquireLock = !lockManager.hasWriteLock(profile.getRepositoryId(), Thread.currentThread());
if (acquireLock) {
lockManager.acquireWriteLock(profile.getRepositoryId(), Thread.currentThread());
}
boolean shouldRollback = false;
TransactionDemarcation transactionDemarcation = new TransactionDemarcation();
TransactionManager transactionManager = getTransactionManager();
transactionDemarcation.begin(transactionManager, TransactionDemarcation.REQUIRED);
try {
synchronized (getOrder()) {
...
...
...
}
} catch (final Exception ex) {
shouldRollback = true;
vlogError(ce, "There has been an exception during processing of order: {0}", getOrder().getId());
} finally {
try {
transactionDemarcation.end(shouldRollback);
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "TransactionDemarcationException during finally: {0}", tde.getMessage());
} finally {
vlogDebug("Ending Transaction for orderId: {0}", order.getId());
}
}
} catch (final DeadlockException de) {
vlogError(de, "There has been an exception during processing of order: {0}", order.getId());
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "There has been an exception during processing of order: {0}", order.getId());
} finally {
try {
if (acquireLock) {
lockManager.releaseWriteLock(getOrder().getProfileId(), Thread.currentThread(), true);
}
} catch (final Throwable th) {
vlogError(th, "There has been an error during release of write lock: {0}", th.getMessage());
}
}
In theory, any FormHandler that inherits from the PurchaseProcessFormHandler already implements the following steps OOTB:
Acquire LocalLockManager in order to avoid concurrent threads to modify the same order:
try {
acquireLock = !lockManager.hasWriteLock(profile.getRepositoryId(), Thread.currentThread());
if (acquireLock) {
lockManager.acquireWriteLock(profile.getRepositoryId(), Thread.currentThread());
}
} catch (final DeadlockException de) {
vlogError(de, "There has been an exception during processing of order: {0}", order.getId());
}
Create a new Transaction:
try {
TransactionDemarcation transactionDemarcation = new TransactionDemarcation();
TransactionManager transactionManager = getTransactionManager();
transactionDemarcation.begin(transactionManager, TransactionDemarcation.REQUIRED);
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "There has been an exception during processing of order: {0}", order.getId());
}
Ending the Transaction being used:
try {
TransactionManager transactionManager = getTransactionManager();
Transaction transaction = transactionManager.getTransaction();
// If transaction is elegible for commiting:
transactionManager.commit();
transaction.commit();
// otherwise
transactionManager.rollback();
transaction.rollback();
} catch (final Exception ex) {
error = true;
vlogError(ex, "There has been an exception during processing of order: {0}", order.getId());
} finally {
// handle the error
}
Release the lock being used for the transaction:
finally {
ClientLockManager lockManager = getLocalLockManager();
lockManager.releaseWriteLock(profile.getRepositoryId(), Thread.currentThread(), true);
}
As per ATG documentation, the following methods implement the behaviour descripted above:
Method: beforeSet
Called before any setX methods on this form are set when a form that modifies properties of this form handler is submitted. Creates a transaction if necessary at the beginning of the form submission process, optionally obtaining a local lock to prevent multiple forms from creating transactions that may modify the same order.
Steps: 1 & 2
Method: afterSet
Called after any setX methods on this form are set when a form that modifies properties of this form handler is submitted. Commits or rolls back any transaction created in beforeSet, and releases any lock that was acquired at the time.
Steps: 3 & 4
Such as you will only have to handle the following procedures in order to update the order:
Syncronize the block of code that's going to be used for order updating in order to avoid thread concurrency.
synchronized (getOrder()) {
...
...
...
}
Perform order modifications:
synchronized (getOrder()) {
getOrder().setXXX();
getOrder().removeXXX();
}
Update the order (updateOrder pipeline chain will be invoked):
synchronized (getOrder()) {
...
...
...
getOrderManager().updateOrder(order);
}
This is pretty straightforward, unless you have to edit an order in any of the following scenarios:
Form handlers or custom form handler that are not in the PurchaseProcessFormHandler's hierachy.
Helpers or Tools classes.
Processors
ATG REST Web Services
&c
If so, you will have to implement the Transactional Pattern within your components.
Questions!
Is there any other pattern known to use instead of using the transactional pattern?
Would it be possible to implement/override the beforeSet & afterSet methods in FormHandlers just the same way ATG does it in PurchaseProcessFormHandler
Are you aware of any other approach?
The series of steps you have outlined above is the prescribed series of steps for updating an order.
Feel free to factor it out in any way you find useful. Just ensure that when you update an order, you, or your inherited code, have performed the requisite steps.
One common way that ATG does similar factoring is for a given method, say X(...), you would have a preX(...), doX(...), and postX(...) method. You can create an abstract class with all your boilerplate code in the preX() and postX() methods, maybe even declared final, and have the doX() declared abstract. Your component then will inherit from the abstract class and must implement the doX() method. You may need to handle exceptions explicitly as well.
This is, essentially, what the standard form handlers do (under different names).
For example;
public final void X(...) {
preX(...); // call the pre method
try {
doX(...); // call the do method
} catch (XException xe) {
// handle error
}
postX(...); // call the post method
}
protected final void preX(...) {
// do everything you need to do before your customer code
}
protected final void postX(...) {
// do everything you need to do after your customer code
}
protected abstract void doX(...) throws XException;
Another thing you could do, instead of inheriting from an abstract class, is to define an annotation that has all the boilerplate code.
A third thing you could do, in a similar way, but a lot harder to shoehorn into your ATG code, might be to define an aspect or a method invocation interceptor using third party frameworks.
However, once again, whatever you do, and however you do it, just ensure that you follow all the steps.
Is there a difference between:
Object o = new Object();
session.save(o)
and
session.save(new Object());
I ask because I am finding sometimes the objects are mixed up in my implementation. The discrepancy is found in production database. So it is hard to test.
Here is the edited code:
pubic class Product {
Logger logger = Logger.getRootLogger();
private String globalId;
public void service() {
this.gobalId = getVariable("GLOBALID"); //from Asterisk
Transaction tran = null;
try {
Session session = HibernateUtil.getCurrentSession();
int charge=0;
//set charge
Transaction tran = session.beginTransaction();
session.save(new Packet(globalId, charge));
tran.commit();
} catch (Exception e) {
if (tran != null) tran.rollback();
logger.error("Error", e);
} finally {
HibernateUtil.closeCurrentSession();
}
}
}
At run time, I see in the Packet table duplicate globalId rows with different values for the charge column. The globalId is supposed to be unique, even though it is not the primary key in the table. There could be a couple of explanations:
Asterisk is sending a wrong value for globalId in the getVariable() method
The service method needs to be synchrnozied (the Asterisk Java API says the Product class is implemented like a Servlet)
I don't see any errors in the logs. So no exceptions are thrown in the code.
Any help is appreciated.
Thanks
Is the following code considered a bad practice? Do you think it can be done otherwise?
The goal is to always update the status, either with success (i.e invocation to service.invoke(id);returns normally ) or with failure...
#Autowired
private Service service;
public void onMessage(Message message) {
String id = null;
String status = "FAILED";
try {
id = ((TextMessage) message).getText();
status = service.invoke(id); //can throw unchecked exception
} catch (final JMSException e) {
throw new RuntimeException(e);
} finally {
if (StringUtils.isNumeric(id)) {
service.update(id, status);
}
}
}
It depends on your Use-case, whether you have to perform a step or not based on previous step. Using finally may execute your second step regardless what exception you may receive.
I would recommend having the second step outside try...catch block so that you'll update only when you have got any exception you've Expected and continue to your second step, else, your method will throw and exit.
i think you should not use implementation of message listener , you should wire them independent of spring tech . just pojo based . use <jms:listener-container > with <jms:listener>