Grails 2.3.6 not throwing optimistic exceptions - grails

I have a scaffolded domain class and I am trying to get it to throw an optimistic locking exception, but it won't. I put the same instance in edit mode in two different browsers. I edit a field in browser 1 and click update. I verified that the version increments. I then edit in browser 2 and click update. It just overwrites the browser 1 changes with the browser 2 changes. I've seen many questions here about people getting the exception when they don't want it, so I am wondering if they turned it off in Hibernate somehow. I even tried changing the update method so all it does is execute instance.save(). Am I not understanding what optimistic locking is supposed to do?
package demo
class issue {
String synopsis
String details
static constraints = {
synopsis()
details(size: 0..1000)
}
}

I haven't tried Grails 2.3.x, but in versions up to 2.2.4 the version check is happening exactly in update action and not deep inside hibernate:
def update(Long id, Long version) {
def priceInstance = Price.get(id)
//..
if (version != null) {
if (priceInstance.version > version) {
priceInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'price.label', default: 'Price')] as Object[],
"Another user has updated this Price while you were editing")
render(view: "edit", model: [priceInstance: priceInstance])
return
}
}
priceInstance.properties = params
if (!priceInstance.save(flush: true)) {
render(view: "edit", model: [priceInstance: priceInstance])
return
}
...
}
if you are not using that or similar code, GORM stores the instance as it is without checking for version

Related

Grails domain instance not saving

I have a grails domain object that I validate and then save as such:
if(foo.hasErrors()) {
transactionStatus.setRollbackOnly()
respond foo.errors, view: 'create'
return
}
foo.save flush:true, failOnError:true
println(foo)
There are no errors given on the save. But when I call the println, it says my object is unsaved. However, if I check the database, it has indeed been persisted. Is there something I can check to tell me why grails is telling me it is unsaved. The grails version is 3.0.9.
After more testing, it looks like it is related to the way I specify the key in my domain mapping.
static mapping = {
version false
autoTimestamp false
id name:'foo_id', generator:'increment'
}
If I remove the id field and let Grails handle it by default the object is saved properly. If I add the id field back, it has the issue described above. Is there a reason for it?
I wouldn't be too concerned about the format of toString(). You're calling save with failOnError: true, so it's safe to assume that an exception would be thrown if persistence fails. If you don't specify this argument, you can check that persistence succeeds like so
if (foo.save()) {
println 'it worked'
} else {
println 'if failed'
}
try
foo = foo.save flush:true, failOnError:true
println(foo)

How to detect DB deadlocks caused by a single thread?

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

Grails 2.1 dateCreated null intermittently on domain save

tl:dr; This is a bit involved of a problem, any advice is welcome, appreciate reading in advance :)
My coworkers and I have been struggling a bit with an odd behavior in our batch processing application. We recently upgraded it from Grails 1.3.7 to 2.1
The stacktrace is showing the following error:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException:
Cannot insert the value NULL into column 'date_created',
table 'dev.dbo.notification_log'; column does not allow nulls. INSERT fails.
...
[quartzScheduler_Worker-1] [||] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)
[quartzScheduler_Worker-1] [||] ERROR listeners.SessionBinderJobListener - Cannot flush Hibernate Sesssion, error will be ignored
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)
Here is the code of that particular Domain Object (NotificationLog)
class NotificationLog implements Serializable{
Date dateCreated
Notification notification
NotificationDeliveryState deliveryState
String message
static mapping = {
message type: 'text'
}
}
What's strange, however, is this error doesn't occur EVERY time that domain object is persisted, and we only have one place in the code that object is ever persisted, shown below:
class NotificationLogService {
boolean transactional = true
def logNotification(Notification notification, message, deliveryState) {
def notificationLog = new NotificationLog(
notification: notification,
deliveryState: deliveryState,
message:message
)
try{
notificationLog.save(failOnError:true)
} catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
}
}
}
We've found a 'hack' of a workaround in the below SO question where we are no longer periodically seeing the error in the logs, by adding this to the Domain Object
static mapping = {
autoTimestamp true
}
But this isn't the only domain we're seeing with the SAME periodic failure to save (thus, I need to add the mapping to other domains), and if this truly is necessary for dateCreated to function properly in Grails 2.1, I need to add it to a LOT more domains!
Worse, I can't reproduce it in a Unit or Integration test, its only happening on our running Dev and QA instances.
So, 2 Questions:
Does anyone know why this error might be periodically occurring?
If not, is there a way I can globally add this autoTimestamp true mapping to ALL of my project's domain objects (I've been unable to find documentation for how to add it at all, other than to set it to false)
Relevant SO Question:
dateCreated, lastUpdated fields in Grails 2.0
Relevant Grails Maillist discussion
http://grails.1312388.n4.nabble.com/dateCreated-lastUpdated-in-Grails-2-0-td4337894.html
Relevant Grails docs on autoTimestamp GORM properties
http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping
To answer both of the questions:
EDIT Try flush: true while save otherwise autoTimestamp true is the last resort. I have not researched to find out the cause of this issue.
You can set this property in Config.groovy to make it applicable for all domain classes.
grails.gorm.default.mapping = {
autoTimestamp true //or false based on your need
}
Have you tried to manually insert the date when creating a new NotificationLog?
Like this:
class NotificationLogService {
boolean transactional = true
def logNotification(Notification notification, message, deliveryState) {
def notificationLog = new NotificationLog(
dateCreated: new Date(),
notification: notification,
deliveryState: deliveryState,
message:message
)
try{
notificationLog.save(failOnError:true)
} catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
}
}
}

Grails GORM auto update issue

Updated post:
In a Controller if I do this:
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
//a singleton service with nothing to do with obj
testService.dostuff()
/*
"obj" gets persisted to the database right here
even before the next println
*/
println "done"
Can anyone please explain me why is this happening with Grails 1.3.7 and not with Grails 2? What is the reason?
I know I could use discard() and basically restructure the code but I am interested in what and why is happening behind the scenes. Thanks!
Old post:
I have a test Grails application. I have one domain class test.Test:
package test
class Test {
String name
static constraints = {}
}
Also I have a service test.TestService:
package test
class TestService {
static scope = "singleton"
static transactional = true
def dostuff() {
println "test service was called"
}
}
And one controller test.TestController:
package test
class TestController {
def testService
def index = {
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
testService.dostuff()
println "done"
}
}
So what I do:
Create a domain object
Change one of it's properties
Call a singleton service method
What I would expect:
Nothing gets persisted to the db unless I call obj.save()
What happens instead:
Right after the service call Grails will do an update query to the database.
I have tried the following configuration from this url: http://grails.1312388.n4.nabble.com/Turn-off-autosave-in-gorm-td1378113.html
hibernate.flush.mode="manual"
But it didn't help.
I have tested it with Grails 1.3.7, Grails 2.0.3 does not have this issue.
Could anyone please give me a bit more information on what is exactly going on? It seems like the current session has to be terminated because of the service call and because the object is dirty it is getting automatically persisted to the database after the service call. What I don't understand that even with the manual flush mode configuration in Hibernate does not help.
Thanks in advance!
I'm not sure what about that thread you linked to made you think it would work. They all said it wouldn't work, the ticket created has been closed as won't fix. The solution here is to use discard() as the thread stated.

Grails: Accessing GORM from a background thread

I have been struggling with this error for a week now, and I am seriously losing my mind over this! I have tried multible implementations and work-arounds and hacks and what not, but I just keep stubling into just another exception.
I am using the Executor plugin to run a method asynchroniously:
runAsync{
run(...)
}
The method initially deletes some objects:
page.delete(flush:true)
And then later possibly recreating those objects:
def page = new Page(type : Page.TYPE_TABLE, domain : domainVersion.domain, identifier : tableName)
page.save(flush: true, failOnError: true)
But that fails with the following exception:
Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.ramboll.egs.ohs.domain.Domain#1]
The relationship between the Page and Domain is simply implemented by Page having a Domain attribute. No hasMany og belongsTo - as I was discouraged from this in an earlier post due to performance issues.
I think I have tried all thinkable combinations of save, merge, withTransachtion and PersistenceContextInterceptor...
How is this supposed to work? Examples please.
Thanks in advance!
It doesn't appear that working in a new thread is the issue, it looks like a standard validation problem. It's saying that the Page is null, which indicates a validation error since save() returns the instance if it was successful, or null if there's a one or more validation errors. There are a few options:
def page = new Page(type : Page.TYPE_TABLE,
domain: dbUpdate.domainVersion.domain, identifier: tableName)
page.save(flush:true)
if (page.hasErrors()) {
// handle errors
}
else {
def pageVersion = createPageVersion(page, dbUpdate.domainVersion,
con, tableName, dbUpdate.author).save(flush:true)
}
or use failOnError to throw an exception:
def page = new Page(type : Page.TYPE_TABLE, identifier: tableName,
domain: dbUpdate.domainVersion.domain).save(flush:true, failOnError: true)
def pageVersion = createPageVersion(page, dbUpdate.domainVersion,
con, tableName, dbUpdate.author).save(flush:true)

Resources