With the following code anyone could easily get a DB deadlock within a single thread: just select an object twice in nested transactions using pessimistic LockMode.
def deadlockWithinSingleThread() {
long id = ...
Dog.withNewTransaction {
getIt(id)
Dog.withNewTransaction {
getIt(id)
}
}
}
private Dog getIt(long id) {
Dog.withCriteria(uniqueResult: true) {
idEq id
lockMode LockMode.PESSIMISTIC_WRITE
maxResults 1
}
}
Is there a way to detect such a simple kind of deadlocks? Alternatively, could I know (by request) if an object is locked by the same thread already?
Personally I'd prefer the 2nd get() to fail with an exception. I believe it's not a big trick to detect such situations for Spring, for example. However there is no a warning even.
Grails 2.2.0, PostgreSQL 9, Spring, Hibernate 3.2
Related
What is the correlation between Spring org.springframework.transaction.annotation.Transactional annotation and Neo4j OGM org.neo4j.ogm.session.Session.getTransaction() method.
I'm trying to access the current transaction via session.getTransaction() inside of the method annotated with Spring #Transactional but always getting null.
I have added a following code inside of my Spring MVC RestController method:
Transaction tx = session.beginTransaction();
try {
for (int i = 0; i < 10; i++) {
initializeNode(node);
}
}
tx.commit();
} catch (Throwable th) {
logger.error("Error while inserting mock data", th);
th.printStackTrace();
} finally {
tx.close();
}
in case of the following method:
private void initializeNode(TestNode node) {
System.out.println(session.getTransaction());
}
it prints current tx - so far everything is okay.
But in case of the following method:
private void initializeNode(TestNode node) {
System.out.println(session.getTransaction());
User admin = userDao.findByUsername("admin");
}
first time it prints current tx and then null... transaction disappear before commit for a some reason..
this is findByUsername method:
#Service
#Transactional
public class UserDaoImpl implements UserDao {
#Override
#Transactional(readOnly = true)
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
...
}
Right after that on commit I'm getting a following exception:
org.neo4j.ogm.exception.TransactionManagerException: Transaction is not current for this thread
at org.neo4j.ogm.session.transaction.DefaultTransactionManager.commit(DefaultTransactionManager.java:100)
at org.neo4j.ogm.transaction.AbstractTransaction.commit(AbstractTransaction.java:83)
at org.neo4j.ogm.drivers.embedded.transaction.EmbeddedTransaction.commit(EmbeddedTransaction.java:77)
What am I doing wrong ? Why transaction disappears ?
There are several issues and themes going on in this question. I will try and break them down and hopefully at the end it will all make sense.
As of the latest release of Spring Data Neo4j (4.1.x) there is no correlation between Spring's #Transactional and the Neo4j OGM's Session.getTransaction() or Session.beginTransaction() when called directly.
In your first two code blocks you are completely managing your OGM session lifecycle directly. Spring is not involved at all at this point and as you say it executes as expected.
In your updated third code block you are now expecting the session that you have manually opened to work with your Spring managed DAO. What will happen here is depends on the Neo4j Driver you are using with SDN but essentially because your DAO has the #Transactional annotation, Spring will intercept the call and start a brand new transaction all on its own on top of the one you are manually managing. At this point, we can't make any guarantees about the behaviour but the easiest explanation would be to say that it will be unexpected (again, depending on the driver used).
So how can you fix this?
I'm going to assume you want to use Spring Transactions and Spring Data Neo4j. If that's the case you will want to start by:
Changing your DAO to use Spring Data Repositories. This gives you a lot of free persistence functionality like finders, saves, deletes etc.
Putting the #Transactional annotation around the unit of work you want to accomplish. You might have a method that calls userRepository.findByUserName(), modifies that user and calls userRepository.save(user). In a web environment this is typically some sort of service method.
Removing any code that manually starts or ends an OGM session transaction.
You can find a very short code sample here and a longer code sample here.
A more comprehensive guide can also be found here.
In Spring Data Neo4j 4.2.x we hope to introduce some more powerful and friendlier #Transactional behaviour so keep posted for that update.
We need to be able to rollback a complex transaction in a service, without throwing an exception to the caller. My understanding is that the only way to achieve this is to use withTransaction.
The question is:
why do I have to call this on a domain object, such as Books.withTransaction
What if there is no relevant domain object, what is the consequence of picking a random one?
Below is more or less what I am trying to do. The use case is for withdrawing from an account and putting it onto a credit card. If the transfer fails, we want to rollback the transaction, but not the payment record log, which must be committed in a separate transaction (using RequiresNew). In any case, the service method must return a complex object, not an exception.
someService.groovy
Class SomeService {
#NotTransactional
SomeComplexObject someMethod() {
SomeDomainObject.withTransaction{ status ->
DomainObject ob1 = new DomainObject.save()
LogDomainObject ob2 = insertAndCommitLogInNewTransaction()
SomeComplexObject ob3 = someAction()
if (!ob3.worked) {
status.setRollbackOnly() // only rollback ob1, not ob2!
}
return ob3
}
}
}
The above is flawed - I assume "return ob3" wont return ob3 from the method, as its in a closure. Not sure how to communicate from inside a closure to outside it.
To your primary question: you can pick a random domain object if you want, it won't do any harm. Or, if you prefer, you can find the current session and open a transaction on that instead:
grailsApplication.sessionFactory.currentSession.withTransaction { /* Do the things */ }
Stylistically I don't have a preference here. Others might.
Not sure how to communicate from inside a closure to outside it.
In general this could be hard; withTransaction could in principle return anything it wants, no matter what its closure argument returns. But it turns out that withTransaction returns the value returned by its closure. Here, watch:
groovy> println(MyDomainObject.withTransaction { 2 + 2 })
4
By convention, all withFoo methods which take a closure should work this way, precisely so that you can do the thing you're trying to do.
I'm assuming this question was from a grails 2 application and this problem from 2015 has been fixed before now.
I can't find this in any of the grails 2 documentation, but services have a magic transactionStatus variable injected into their methods. (at least in grails 2.3.11)
You can just leave all the annotations off and use that injected variable.
Class SomeService {
SomeComplexObject someMethod() {
DomainObject ob1 = new DomainObject.save()
LogDomainObject ob2 = insertAndCommitLogInNewTransaction()
SomeComplexObject ob3 = someAction()
if (!ob3.worked) {
transactionStatus.setRollbackOnly() // transactionStatus is magically injected.
}
return ob3
}
}
This feature is in grails 2, but not documented. It is documented in grails 3.
https://docs.grails.org/latest/guide/services.html#declarativeTransactions
search for transactionStatus.
I have an action that handle a critical transaction and I am not sure what would be the best way to handle the transaction.
Here is a simplified example of what I would need to do:
[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
// Not sure what isolation level I sould use here to start with...
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
using(new TransactionScope(isolationLevel)){
// Retreive the order
var order = GetExistingOrder(orderKey);
// Validate that the order can be processed
var validationResult = ValidateOrder(order);
if (!validationResult.Successful)
{
// Order cannot be processed, returning
return View("ErrorOpeningOrder");
}
// Important stuff going on here, but I must be sure it
// will never be called twice for the same order
BeginOrderProcess(order);
return View("OrderedProcessedSuccessfully");
}
}
First thing I would ask is: in this kind of operation, where we can have multiple requests at the same time for the same order (i.e.:quick requests from browser for same order), should I use pessimistic locking to really ensure one transaction at the time or there is a way to make sure BeginOrderProcess would never be called twice with two concurrent requests for the same order almost at the same time with optimistic locking (considering that it would probably be faster)?
Second thing: Am I doing it completely the wrong way and there is a better way to handle cases like this? In other words, how should I handle this? :)
Ok, after some research, I think I've found what I wanted.
For a case like this, it would be overkill to use pessimistic lock with nhibernate (by using session.Lock(order))
I've opted for optimistic lock simply because I didn't know how to use it before.
Here's what the code should look like:
[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
// I confirm, here I really want ReadCommit since I need optimistic lock
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
using(var tx = new TransactionScope(isolationLevel)){
// Retreive the order
var order = GetExistingOrder(orderKey);
// Validate that the order can be processed
var validationResult = ValidateOrder(order);
if (!validationResult.Successful)
{
// Order cannot be processed, returning
return View("ErrorOpeningOrder");
}
// Important stuff going on here, but I must be sure it
// will never be called twice for the same order
BeginOrderProcess(order);
// The main difference is here
// I need to do an explicit commit here to catch the stale object exception
// and handle it properly. Before that,
// I was handling the commit in the dispose of my TransactionScope
// Since my transaction scope is in ReadCommit, no one but this request
// should be able to read the modified data whatever the changes are
try{
try{
tx.Commit();
}catch(Exception){
tx.RollBack();
throw;
}
}catch(StaleObjectStateException){
return View("OrderIsCurrentlyBeeingProcessedBySomeoneElse");
}
return View("OrderedProcessedSuccessfully");
}
}
As my comment shows, the main difference is that I handle my commit manually and then handle the exception as it would. With this implementation, I wont worry about blocking other users requests and I can handle the exception as I need.
I am using fluent nhibernate and I've configured my entities to use version in my mappings:
OptimisticLock.Version();
Version(x => x.Version)
.Column("EntityVersion")
.Generated.Never()
.Default(0)
.UnsavedValue("null");
With this, when I am doing my commit, NHibernate will look at the version and throw a StaleObjectStateException if the commit doesn't match the right version.
Happy NHibernating :)
All of this is on Grails 2.2.3.
I have two classes in a One-to-many relationship, and a service which removes a list of ids
class Box {
String name
static hasMany = [items:ItemDomain]
static constraints = {
items(nullable:true)
}
}
and
class ItemDomain { String name Box box
static belongsTo = Box
static constraints = {
name(blank:false,unique:['box'], maxSize:127)
box(nullable:false) } }
In the service, here's the problem section:
def itemsToDelete = params.itemsToDelete //List of DB ids
List<ItemDomain> items= []
items.addAll(box.items) //Copy the list to avoid concurrent mod exception
for(ItemDomain item : items)
{
if(itemsToDelete.contains(item.id))
{
box.removeFromItems(item)
item.delete()
}
box.save(flush: true)
}
This works fine when running the application, but from integration testing it fails with
InvalidDataAccessApiUsageException: deleted object would be re-saved by cascade (remove deleted object from associations)
If I take out the flush, and eventually it will fail with:
Field error in object 'mypackage.ItemDomain' on field 'box': rejected value [null];
Adding logging, I see the size of box.items before entering the loop is the same as it is after exiting the loop, printing the items in the loop before and after shows that the item.box field for the deleted items changes to null. I've tried messing with the cascade mapping in the parent class... I'm at a loss as to whether I'm doing something wrong or if this is an issue with integration testing. The only similar issues I found were against grails 1.1 and had no resolution in the threads that I found.
I appreciate any feedback.
So, not surprisingly, I was doing something wrong. It turns out that my equals() and hashCode() implementations on the ItemDomain class were including a field that was never supposed to change, but due to requirements creep, was now changing and the methods never got updated properly.
I'm writing a multi-threaded application in Grails and the additional threads need access to GORM/Hibernate. When they try to access GORM I get the error "org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here".
OK fair enough, can someone guide me on the best way to set the threads up to have access? The error message almost sounds like you just need to change some config options yet I sense, it is not so simple...
There is a bean in Grails applications called “persistenceInterceptor” that can be used for this.
See this example from the JMS plugin on how to use it:
http://github.com/gpc/grails-jms/blob/master/src/groovy/grails/plugin/jms/listener/adapter/PersistenceContextAwareListenerAdapter.groovy#L21
Here is the interface:
https://github.com/grails/grails-core/blob/master/grails-core/src/main/groovy/grails/persistence/support/PersistenceContextInterceptor.java
And Hibernate impl:
https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm-support/src/main/groovy/org/grails/orm/hibernate4/support/HibernatePersistenceContextInterceptor.java
You need to put any GORM calls in a withTransaction closure. An example taken from a discussion of multi threading at
https://fbflex.wordpress.com/2010/06/11/writing-batch-import-scripts-with-grails-gsql-and-gpars/
Single threaded
user = User.findByUsername( photo.username )
multi threaded
User.withTransaction{
user = User.findByUsername( photo.username )
}
withNewSession will also work. In my case, I have low priority updates where the last update can always "win". version: false is also important here in order to avoid the StaleObjectException:
Thread.start {
try {
Widget.withNewSession {
xxx()
log.info "Asynchronously did some updates."
}
} catch (Exception ex) {
log.error "Failed to asynchronously do something...", ex
}
}
Luke Daley gave the right answer. Unfortunately, the links have changed. Thus, I'll update his answer and provide a code example to make this answer self-contained.
There is a bean in Grails applications called persistenceInterceptor that can be used for initializing the persistence context / session for Hibernate. You can inject the bean into one of your controller / service classes and start a new thread, e.g. using the following code snippet.
class YourControllerOrService {
PersistenceContextInterceptor persistenceInterceptor
def someOperation() {
...
Runnable yourTask = { ->
try {
if (persistenceInterceptor) {
persistenceInterceptor.init()
}
// execute the hibernate operations here in a transaction,
// e.g. call a method annotated with #Transactional
...
} catch (Exception e) {
log.error('Your error message', e)
} finally {
if (persistenceInterceptor) {
persistenceInterceptor.flush()
persistenceInterceptor.destroy()
}
}
}
Thread workerThread = new Thread(yourTask)
workerThread.start()
...
}
}
You'll find an exemplary implementation in the Grails JMS plug-in on GitHub.
The PersistenceContextInterceptor interface can be found on GitHub, too.