Mapper hook for postRollback?

60 views
Skip to first unread message

Jeppe Nejsum Madsen

unread,
Aug 26, 2011, 9:36:41 AM8/26/11
to lif...@googlegroups.com
Hi,

I need to maintain some caches, and it works fine for normal
operations using afterCreate/Update/Delete.

Only problem is if we do a lot of updates (and thus updates the
caches) and a later update fails with a DB exception. In this
situation I would like to invalidate the caches.

But I cant seem to find a way to do this. Any hints?

/Jeppe

David Pollak

unread,
Aug 26, 2011, 9:45:07 AM8/26/11
to lif...@googlegroups.com
DB.performPostCommit:

  /**
   * perform this function post-commit.  THis is helpful for sending messages to Actors after we know
   * a transaction has committed
   */
  def performPostCommit(f: => Unit) {
    postCommit = (() => f) :: postCommit
  }

Is that what you're looking for or do you need something on the Mapper instance itself?


--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.




--
Lift, the simply functional web framework http://liftweb.net

Jeppe Nejsum Madsen

unread,
Aug 26, 2011, 9:52:57 AM8/26/11
to lif...@googlegroups.com
On Fri, Aug 26, 2011 at 3:45 PM, David Pollak
<feeder.of...@gmail.com> wrote:
> DB.performPostCommit:
>
>   /**
>    * perform this function post-commit.  THis is helpful for sending
> messages to Actors after we know
>    * a transaction has committed
>    */
>   def performPostCommit(f: => Unit) {
>     postCommit = (() => f) :: postCommit
>   }
>
> Is that what you're looking for or do you need something on the Mapper
> instance itself?

I saw this, but was thinking it is only called on a successful commit?
What I need is something that is called in the case of a rollback (ie
DB driver throws an exception....)

Or perhaps I should just move the cache updates into a
performPostCommit function...hmm I think that might actually be a
better idea:

Use the afterCreate/Update/Delete hooks to add a function to
performPostCommit that will update the caches on a commit.....

/Jeppe

David Pollak

unread,
Aug 26, 2011, 11:14:44 AM8/26/11
to lif...@googlegroups.com

Personally, this is the approach I'd take.  If you want an explicit post commit/post rollback hook on the Mapper instance, please open a ticket.  If you don't feel comfortable handling the ticket, please assign it to me (or Naftoli who seems about as fluent in Mapper as I am these days if he volunteers to take it.)
 

/Jeppe

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Jeppe Nejsum Madsen

unread,
Aug 31, 2011, 7:33:12 AM8/31/11
to lif...@googlegroups.com
On Fri, Aug 26, 2011 at 5:14 PM, David Pollak

Ok I'm now using the performPostCommit to do the updates since I think
this is probably the right thing to do, but it seems the
performPostCommit functions are always called, even during rollback?

The code seems to reflect this as there are no conditions around the
calls (i believe success is false in the case of a rollback):

private def clearThread(success: Boolean): Unit = {
val ks = info.keySet
if (ks.isEmpty) {
postCommit.foreach(f => tryo(f.apply()))

_postCommitFuncs.remove
threadStore.remove
} else {
ks.foreach(n => releaseConnectionNamed(n, !success))
clearThread(success)
}
}

Is this the way it was supposed to work? If yes, then
"performPostCommit" is probably the wrong name :-)

Looking a little further, the same seems to be true for

/**
* Append a function to be invoked after the commit has taken place
for the given connection identifier
*/
def appendPostFunc(name: ConnectionIdentifier, func: () => Unit) {
info.get(name) match {
case Some(ConnectionHolder(c, n, post)) => info(name) =
ConnectionHolder(c, n, func :: post)
case _ =>
}
}

which is also always called. I think this might be ok, but the
scaladoc should be reworked....

If it's a bug, I can file a ticket and fix it....

/Jeppe

David Pollak

unread,
Aug 31, 2011, 12:06:47 PM8/31/11
to lif...@googlegroups.com

I have no clue as to it being a bug or not. ;-)

But if it's confusing, let's fix the confusion.  Also, it might be reasonable to have a mechanism to determine if the function is executing as part of a commit or a rollback.

I heartily encourage you to take the ticket.

Thanks!
 

/Jeppe

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Jeppe Nejsum Madsen

unread,
Sep 1, 2011, 8:29:39 AM9/1/11
to lif...@googlegroups.com
On Wed, Aug 31, 2011 at 6:06 PM, David Pollak

I've created

https://www.assembla.com/spaces/liftweb/tickets/1102-performpostcommit-is-called-even-when-transaction-is-rolledback

But trying to write a test case for this, I see performPostCommit is
only called within the context of buildLoanWrapper, not if you do

DB.use() {...}

is this intentional or should we make this consistent as well?

To sum up my findings:

performPostCommit should be called withing the context of
buildLoanWrapper and the function passed is called whenever a
transaction ends on the wrapped connection (commit or rollback). The
function list is cleared when called.

appendPostFunc can be called within the context of buildLoadnWrapper
or use{} on any connection and the function passed is called whenever
a transaction on the connection ends on the current thread. The
function list is cleared when called.

I have a feeling I'm missing something obvious :-)

My proposal is:
Deprecate (or just remove) performPostCommit and appendPostFunc

Add a new

def performPostTransaction(f:Boolean => Unit)

where Boolean means success (commit) or rollback. This method should
be called within the context of an active connection and the function
passed will be called when the transaction ends on the connection
(either loanWrapper or use). The list of functions is cleared when the
tx ends.

I cannot really see the use case for appendPostFunc on a named
connection when that connection must be current for the call to
succeed anyway....

Thoughts?

/Jeppe

Harry-Anton Talvik

unread,
Sep 5, 2011, 1:12:00 PM9/5/11
to lif...@googlegroups.com
Hi,

So, after https://github.com/lift/framework/commit/b6af3b942309de6894bc79adbe9554b5604a8a0a
I see deprecation warnings:
warning: method performPostCommit in trait DB is deprecated:
Use appendPostTransaction
where
def appendPostTransaction(name: ConnectionIdentifier, func:
Boolean => Unit) { ... }
// def performPostCommit(f: => Unit) { ... } // <-- Deprecated

How shall I proceed updating the code that looks like
mappedInstance.changeItsState()
DB.performPostCommit {
MyLiftActor ! MyMessage(Full(mappedInstance), ...,
S.session.map(_.uniqueId))
}
which currently does not use DB.buildLoanWrapper(), DB.use() nor
anything other like that (which looks to me low-level DB connection
handling, at least compared to the LiftActor messaging level)?

Is there any chance for convenience method maintaining the usage
convenience available so far, which could wrap usage of
DB.buildLoanWrapper() and DefaultConnectionIdentifier?

Cheers,
Harry-A

Jeppe Nejsum Madsen

unread,
Sep 5, 2011, 3:25:12 PM9/5/11
to lif...@googlegroups.com
Harry-Anton Talvik <harry...@gmail.com> writes:

> Hi,
>
> So, after https://github.com/lift/framework/commit/b6af3b942309de6894bc79adbe9554b5604a8a0aI see deprecation warnings:


> warning: method performPostCommit in trait DB is deprecated:
> Use appendPostTransaction
> where
> def appendPostTransaction(name: ConnectionIdentifier, func:
> Boolean => Unit) { ... }
> // def performPostCommit(f: => Unit) { ... } // <-- Deprecated
>
> How shall I proceed updating the code that looks like
> mappedInstance.changeItsState()
> DB.performPostCommit {
> MyLiftActor ! MyMessage(Full(mappedInstance), ...,
> S.session.map(_.uniqueId))
> }
> which currently does not use DB.buildLoanWrapper(), DB.use() nor
> anything other like that (which looks to me low-level DB connection
> handling, at least compared to the LiftActor messaging level)?

Are you sure you don't have a

S.addAround(DB.buildLoanWrapper)

in boot? This is the default for the Lift templates, and afaik the
performPostCommit is only ever called in this scenario.....If you don't
I'll be interested if the above works as expected :-)

> Is there any chance for convenience method maintaining the usage
> convenience available so far, which could wrap usage of
> DB.buildLoanWrapper() and DefaultConnectionIdentifier?

I think the whole "do something on commit" requires the low-level
connection handling to be done by Lift, but as described above I think
this is the case in most scenarios anyway....

I agree the above use case with the code block is nice, but I've
probably written too many financial applications to really appreciate
a method named "performPostCommit" to be called during rollback :-)

How about adding an overload to appendPostTransaction that uses the
DefaultConnectionIdentifier? You would still have to pass a function
instead of a by-name val....

/Jeppe

Harry-Anton Talvik

unread,
Sep 6, 2011, 5:34:14 AM9/6/11
to lif...@googlegroups.com
Hi,

On Mon, Sep 5, 2011 at 22:25, Jeppe Nejsum Madsen <je...@ingolfs.dk> wrote:
> Are you sure you don't have a
>
> S.addAround(DB.buildLoanWrapper)
>
> in boot? This is the default for the Lift templates, and afaik the
> performPostCommit is only ever called in this scenario.....If you don't
> I'll be interested if the above works as expected :-)

I do indeed have it in boot. For some reason I was under impression
that I would need to use a DB.buildLoanWrapper() for each
DB.appendPostTransaction() when used instead DB.performPostCommit().
Ups.


> How about adding an overload to appendPostTransaction that uses the
> DefaultConnectionIdentifier? You would still have to pass a function
> instead of a by-name val....

Yes, that would be great. I believe people updating their code would
also appreciate some kind of minimal example in source comments, near
performPostCommit() deprecation message how to transfer
performPostCommit()-usage to appendPostTransaction() -usage.

Anyways, big thanks for test cases, Jeppe!

Minor typo there: I assume that
https://github.com/lift/framework/commit/b6af3b942309de6894bc79adbe9554b5604a8a0a#L1R113
should say
"call postTransaction functions with false if transaction is not committed"
or
"call postTransaction functions with false if transaction is rolled back"

Cheers,
Harry-A.

Jeppe Nejsum Madsen

unread,
Sep 6, 2011, 8:07:37 AM9/6/11
to lif...@googlegroups.com

I've pushed a few changes for this, but the jenkins build seem to fail
during test, even if framework builds fine for me locally.

Are you able to build locally?

/Jeppe

Harry-Anton Talvik

unread,
Sep 6, 2011, 11:45:33 AM9/6/11
to lif...@googlegroups.com
Hi,

On Tue, Sep 6, 2011 at 15:07, Jeppe Nejsum Madsen <je...@ingolfs.dk> wrote:
> I've pushed a few changes for this, but the jenkins build seem to fail
> during test, even if framework builds fine for me locally.

Thanks!
Is this Jenkins building Lift master somewhere publicly available for
the community to browse/follow?


> Are you able to build locally?

Yes, seems so.
What I did was (based on
http://www.assembla.com/spaces/liftweb/wiki/Building_Lift):
* Cloned https://github.com/lift/framework.git
* Tweaked liftsh (-XX:+CMSClassUnloadingEnabled and
-XX:+UseConcMarkSweepGC were no good, and -XX:MaxPermSize=512M was
ignored by JRockit)
* Executed: liftsh update package
It seemed to me that building phase was OK, finally the process run
into memory issues (somewhere in the middle of running tests).

Cheers,
Harry-A

Jeppe Nejsum Madsen

unread,
Sep 6, 2011, 11:53:15 AM9/6/11
to lif...@googlegroups.com
On Tue, Sep 6, 2011 at 5:45 PM, Harry-Anton Talvik <harry...@gmail.com> wrote:
> Hi,
>
> On Tue, Sep 6, 2011 at 15:07, Jeppe Nejsum Madsen <je...@ingolfs.dk> wrote:
>> I've pushed a few changes for this, but the jenkins build seem to fail
>> during test, even if framework builds fine for me locally.
>
> Thanks!
> Is this Jenkins building Lift master somewhere publicly available for
> the community to browse/follow?

Yes: http://hudson.scala-tools.org/job/lift-framework

>> Are you able to build locally?
>
> Yes, seems so.
> What I did was (based on
> http://www.assembla.com/spaces/liftweb/wiki/Building_Lift):
>  * Cloned https://github.com/lift/framework.git
>  * Tweaked liftsh (-XX:+CMSClassUnloadingEnabled and
> -XX:+UseConcMarkSweepGC were no good, and -XX:MaxPermSize=512M was
> ignored by JRockit)
>  * Executed: liftsh update package
> It seemed to me that building phase was OK, finally the process run
> into memory issues (somewhere in the middle of running tests).

It fails during testing, but only for Scala 2.8.0...For me it fails to
build locally for Scala 2.8.0, welcome to Scala dependency hell :-(

Any hints appreciated :-)

/Jeppe

Harry-Anton Talvik

unread,
Sep 6, 2011, 2:55:41 PM9/6/11
to lif...@googlegroups.com
Hi,

On Tue, Sep 6, 2011 at 18:53, Jeppe Nejsum Madsen <je...@ingolfs.dk> wrote:
> Yes: http://hudson.scala-tools.org/job/lift-framework
...


> It fails during testing, but only for Scala 2.8.0...For me it fails to
> build locally for Scala 2.8.0, welcome to Scala dependency hell :-(
> Any hints appreciated :-)

Yes, fails for 2.8.0 since build #261:
http://hudson.scala-tools.org/job/lift-framework/261/consoleText

There seems to be a mockito-related mocking usage issue with Scala
2.8.0 (not sure what specifically..).

[info] == lift-db / net.liftweb.db.DBSpec ==
[info] DBSpec
[info] eager buildLoanWrapper should
02:38:19.133 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor - Created
new pool entry. name=ConnectionIdentifier(lift), poolSize=1
02:38:19.180 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor -
Released connection. poolSize=1
[info] x call postTransaction functions with true if transaction is committed
[info] The mock was not called as expected:
[info] Argument passed to verify() should be a mock but is null!
[info] Examples of correct verifications:
[info] verify(mock).someMethod();
[info] verify(mock, times(10)).someMethod();
[info] verify(mock, atLeastOnce()).someMethod();
[info] Also, if you use @Mock annotation don't miss initMocks()
(Mockito.scala:174)
02:38:19.217 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor - Created
new pool entry. name=ConnectionIdentifier(lift), poolSize=1
02:38:19.223 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor -
Released connection. poolSize=1
[info] + call postCommit functions with false if transaction is rolledback

Cheers,
Harry-A.

Jeppe Nejsum Madsen

unread,
Sep 6, 2011, 3:05:11 PM9/6/11
to lif...@googlegroups.com

The strange thing is, it fails to compile for me with 2.8.0 (also
related to mockito)

/Jeppe

Indrajit Raychaudhuri

unread,
Sep 7, 2011, 2:37:14 AM9/7/11
to lif...@googlegroups.com
Folks,

Things should be back to normal now.

- Indrajit


On Wednesday 7 September 2011 at 12:35 AM, Jeppe Nejsum Madsen wrote:

> On Tue, Sep 6, 2011 at 8:55 PM, Harry-Anton Talvik <harry...@gmail.com (mailto:harry...@gmail.com)> wrote:
> > Hi,
> >
> > On Tue, Sep 6, 2011 at 18:53, Jeppe Nejsum Madsen <je...@ingolfs.dk (mailto:je...@ingolfs.dk)> wrote:
> > > Yes: http://hudson.scala-tools.org/job/lift-framework
> > ...
> > > It fails during testing, but only for Scala 2.8.0...For me it fails to
> > > build locally for Scala 2.8.0, welcome to Scala dependency hell :-(
> > > Any hints appreciated :-)
> >
> > Yes, fails for 2.8.0 since build #261:
> > http://hudson.scala-tools.org/job/lift-framework/261/consoleText
> >
> > There seems to be a mockito-related mocking usage issue with Scala
> > 2.8.0 (not sure what specifically..).
> >

> > [info] == lift-db / net.liftweb.db.DBSpec (http://web.db.DBSpec) ==


> > [info] DBSpec
> > [info] eager buildLoanWrapper should

> > 02:38:19.133 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor (http://web.db.ProtoDBVendor) - Created


> > new pool entry. name=ConnectionIdentifier(lift), poolSize=1

> > 02:38:19.180 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor (http://web.db.ProtoDBVendor) -


> > Released connection. poolSize=1
> > [info] x call postTransaction functions with true if transaction is committed
> > [info] The mock was not called as expected:
> > [info] Argument passed to verify() should be a mock but is null!
> > [info] Examples of correct verifications:
> > [info] verify(mock).someMethod();
> > [info] verify(mock, times(10)).someMethod();
> > [info] verify(mock, atLeastOnce()).someMethod();
> > [info] Also, if you use @Mock annotation don't miss initMocks()
> > (Mockito.scala:174)

> > 02:38:19.217 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor (http://web.db.ProtoDBVendor) - Created


> > new pool entry. name=ConnectionIdentifier(lift), poolSize=1

> > 02:38:19.223 [Thread-400] DEBUG net.liftweb.db.ProtoDBVendor (http://web.db.ProtoDBVendor) -


> > Released connection. poolSize=1
> > [info] + call postCommit functions with false if transaction is rolledback
>
> The strange thing is, it fails to compile for me with 2.8.0 (also
> related to mockito)
>
> /Jeppe
>

> --
> You received this message because you are subscribed to the Google Groups "Lift" group.

> To post to this group, send email to lif...@googlegroups.com (mailto:lif...@googlegroups.com).
> To unsubscribe from this group, send email to liftweb+u...@googlegroups.com (mailto:liftweb+u...@googlegroups.com).

Reply all
Reply to author
Forward
0 new messages