Using Mapper in LiftActors

57 views
Skip to first unread message

Zach Cox

unread,
Jan 12, 2011, 5:19:56 PM1/12/11
to Lift
We've been moving a lot of non-essential processing out of the HTTP
req/resp cycle and into LiftActors, mainly to improve response times
and pursue more of an "event-driven architecture". Most of these
actors create/read/update/delete Mapper instances, and so we'd like
all of that to be done in DB transactions, just like in the HTTP req/
resp cycle using S.addAround(DB.buildLoanWrapper).

I just wanted to run our approach by the Lift community for a sanity
check. If you see obvious mistakes in this, it would be awesome if
you could let us know! Hopefully these can serve as best practices
for others doing the same thing.


1) Every actor that works with mapper instances should extend this
DbActor trait, instead of LiftActor directly:

trait DbActor extends LiftActor {
protected def dbLoanWrapper = DB.buildLoanWrapper
override protected def aroundLoans = List(dbLoanWrapper)
}

object MyActor extends DbActor {
protected def messageHandler = {
case Whatever => //this is inside a DB transaction
}
}

This will ensure (I think?) that the actor's messageHandler will be
wrapped in a database transaction. Defining the aroundLoans in
DbActor just makes each concrete actor simpler so we don't have to do
it in every LiftActor impl.


2) Every message sent to a DbActor that depends on Mapper changes made
in the current actor needs to occur in a DB.performPostCommit:

DB.performPostCommit { MyActor ! Whatever }

This ensures that the other actor executes after the current database
transaction has been committed.


Do these seem like the proper best practices to follow when actors
need to use Mapper instances? Are we missing anything here?

Thanks,
Zach

Timothy Perrett

unread,
Jan 13, 2011, 4:41:23 AM1/13/11
to lif...@googlegroups.com
Hmm interesting. I've never seen it applied like this, but yeah it could work alright. The point of a loan wrapper is to deterministically dispose of a resource, so i guess this is a fit. Alternatively you could do:

DB.use(DefaultConnectionIdentifier){ connection => // database call }

Did you have any specific concerns about using it? Its not something i've ever tried so my thoughts here are fairly speculative. 

Cheers, Tim

David Pollak

unread,
Jan 13, 2011, 8:22:46 AM1/13/11
to lif...@googlegroups.com
On Wed, Jan 12, 2011 at 2:19 PM, Zach Cox <zco...@gmail.com> wrote:
We've been moving a lot of non-essential processing out of the HTTP
req/resp cycle and into LiftActors, mainly to improve response times
and pursue more of an "event-driven architecture".  Most of these
actors create/read/update/delete Mapper instances, and so we'd like
all of that to be done in DB transactions, just like in the HTTP req/
resp cycle using S.addAround(DB.buildLoanWrapper).

I just wanted to run our approach by the Lift community for a sanity
check.  If you see obvious mistakes in this, it would be awesome if
you could let us know!  Hopefully these can serve as best practices
for others doing the same thing.


1) Every actor that works with mapper instances should extend this
DbActor trait, instead of LiftActor directly:

trait DbActor extends LiftActor {
 protected def dbLoanWrapper = DB.buildLoanWrapper
 override protected def aroundLoans = List(dbLoanWrapper)
}

I think you're missing an override in the code above.


 

object MyActor extends DbActor {
 protected def messageHandler = {
   case Whatever => //this is inside a DB transaction
 }
}

This will ensure (I think?) that the actor's messageHandler will be
wrapped in a database transaction.  Defining the aroundLoans in
DbActor just makes each concrete actor simpler so we don't have to do
it in every LiftActor impl.

Yes.
 


2) Every message sent to a DbActor that depends on Mapper changes made
in the current actor needs to occur in a DB.performPostCommit:

DB.performPostCommit { MyActor ! Whatever }

This ensures that the other actor executes after the current database
transaction has been committed.

Yes.


Do these seem like the proper best practices to follow when actors
need to use Mapper instances?  Are we missing anything here?

Nope.  You're doing things that right way.
 

Thanks,
Zach

--
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
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics

Zach Cox

unread,
Jan 13, 2011, 8:28:03 AM1/13/11
to lif...@googlegroups.com
On #1, I just thought the actor's aroundLoans matched S.addAround that
seems to be the common pattern for http requests. Since every http
request is wrapped in a transaction, it seems like a good idea to have
actors that use Mapper also wrapped in transactions. And putting this
in a reusable DbActor trait means we just have to extend it, instead
of having to remember to always wrap in the DB.use function. I
believe DB.buildLoanWrapper uses DB.use eventually too.

#2 seems necessary when you send a message to a DbActor from inside
another transaction, so you know that the next transaction will start
after the current one commits. May not be ideal since the other actor
can't start processing the message immediately, but seems necessary.

Thanks,
Zach

Zach Cox

unread,
Jan 13, 2011, 8:31:41 AM1/13/11
to lif...@googlegroups.com
Great to hear this is the correct path, thanks for validating, David!

That dbLoanWrapper method was new, so subclasses could override/use if
needed. DbActor could also just be:

trait DbActor extends LiftActor {
override protected def aroundLoans = List(DB.buildLoanWrapper)
}

Thanks,
Zach

Zach Cox

unread,
Jan 13, 2011, 9:34:39 AM1/13/11
to lif...@googlegroups.com
I'm glad you added it! ;)


On Thu, Jan 13, 2011 at 8:00 AM, David Pollak
<feeder.of...@gmail.com> wrote:


>
>
> On Thu, Jan 13, 2011 at 5:31 AM, Zach Cox <zco...@gmail.com> wrote:
>>
>> Great to hear this is the correct path, thanks for validating, David!
>>
>> That dbLoanWrapper method was new, so subclasses could override/use if
>> needed.  DbActor could also just be:
>>
>> trait DbActor extends LiftActor {
>>  override protected def aroundLoans = List(DB.buildLoanWrapper)
>> }
>

> Yeah.
>
> I forgot that code with in Actor... I added it when I was doing Stambecco.
> Glad you found it. ;-)

Reply all
Reply to author
Forward
0 new messages