Lifecycle management in RequestVar

34 views
Skip to first unread message

Derek Chen-Becker

unread,
Jul 30, 2008, 11:32:28 AM7/30/08
to lif...@googlegroups.com
Hi all,
    A while back we discussed adding some lifecycle management to RequestVar to facilitate things like per-request entity manager sessions (my case in particular). I don't see anything like that in the latest code, so I was thinking about putting together a patch for it. I was thinking of adding a second constructor on RequestVar that takes a function to be applied when the session ends. This seems a little cleaner than trying to shoehorn in a lifecycle trait, but I'm not sure where this "shutdown" function should be called. My current code injects a LoanWrapper to handle the close, but that seems a little heavy if you had a lot of requestVars. Rather, I was thinking that I could either leverage an existing collection in S (is _requestVar appropriate?) or set up a new collection that would allow RequestVar to register the shutdown handlers. Thoughts?

Thanks,

Derek

Marius

unread,
Jul 30, 2008, 2:02:21 PM7/30/08
to liftweb
Hi Derek,

Wouldn't these hooks from LiftSession server your needs?

var onAboutToShutdownSession: List[LiftSession => Unit] = Nil
var onShutdownSession: List[LiftSession => Unit] = Nil


Br's,
Marius

Derek Chen-Becker

unread,
Jul 30, 2008, 4:11:18 PM7/30/08
to lif...@googlegroups.com
Are those appropriate to a per-request lifecycle? Is a new LiftSession spun up, processed and shutdown all within one request? It's not clear to me from the source code that that's the case. If that's how it works then I'd be happy to use those hooks.

Derek

Marius

unread,
Jul 30, 2008, 5:07:13 PM7/30/08
to liftweb
They are called when the Session is terminated which can happen at:

1. Session timeout see SessionMaster CheckAndPurge
2. Explicit session termination see
SessionToServletBridge#valueUnbound where RemoveSession message is
sent to SessionMaster which is also called after you manually
invalidate the Http session

What is the use-case of knowing "per-request lifecycle" when session
ends? ... If I understood correctly you need a function to be called
when the session is terminated.I guess I'm not clear what does session
lifecycle has to do with request lifecycle besides that requests are
processed in a context described by a session.

Is it so that when you process a request you want to know if the
session is valid or not? ... you can use a SessionVar for that.

Br's,
Marius

On Jul 30, 11:11 pm, "Derek Chen-Becker" <dchenbec...@gmail.com>
wrote:
> Are those appropriate to a per-request lifecycle? Is a new LiftSession spun
> up, processed and shutdown all within one request? It's not clear to me from
> the source code that that's the case. If that's how it works then I'd be
> happy to use those hooks.
>
> Derek
>

David Pollak

unread,
Jul 31, 2008, 12:22:00 AM7/31/08
to lif...@googlegroups.com
Derek,

I know I promised you I'd add some of this.  Can you remind me what your requirements are and I'll work on making it happen.

Thanks,

David
--
lift, the simply functional web framework http://liftweb.net
Collaborative Task Management http://much4.us
Follow me: http://twitter.com/dpp
Git some: http://github.com/dpp

Viktor Klang

unread,
Jul 31, 2008, 4:49:27 AM7/31/08
to lif...@googlegroups.com
Couldn't it be of interest to add the life-cycle management on Var itself?

onVarDeath
onVarBirth

?

the scope of SessionVar should be born when a new session is created, and will die when a session is terminated.
the scope of RequestVar should be born when a new Request comes in, and will die when the response has been sent?

Ideas?

Viktor
--
Viktor Klang
Rogue Software Architect

Marius

unread,
Jul 31, 2008, 8:12:53 AM7/31/08
to liftweb
Hi,

To be honest I think current hooks for session and request lifecycle
are more then enough ... at least to me. The only benefit that I see
with var's lifecycle management is that is allows to do some "manual"
clean up of some state (release some resources) references by that
specific Var ... however I find it quite uncommon to do that inside a
Var.

Of course there might be use-cases that I can not envision now so if
Vars lifecycle management really solves problems otherwise nasty to
solve with current support your proposal Vicktor, sounds quite
appropriate to me.


Br's,
Marius



On Jul 31, 11:49 am, "Viktor Klang" <viktor.kl...@gmail.com> wrote:
> Couldn't it be of interest to add the life-cycle management on Var itself?
>
> onVarDeath
> onVarBirth
>
> ?
>
> the scope of SessionVar should be born when a new session is created, and
> will die when a session is terminated.
> the scope of RequestVar should be born when a new Request comes in, and will
> die when the response has been sent?
>
> Ideas?
>
> Viktor
>
> On Thu, Jul 31, 2008 at 6:22 AM, David Pollak <feeder.of.the.be...@gmail.com
>
>
>
> > wrote:
> > Derek,
>
> > I know I promised you I'd add some of this. Can you remind me what your
> > requirements are and I'll work on making it happen.
>
> > Thanks,
>
> > David
>
> > On Wed, Jul 30, 2008 at 1:11 PM, Derek Chen-Becker <dchenbec...@gmail.com>wrote:
>
> >> Are those appropriate to a per-request lifecycle? Is a new LiftSession
> >> spun up, processed and shutdown all within one request? It's not clear to me
> >> from the source code that that's the case. If that's how it works then I'd
> >> be happy to use those hooks.
>
> >> Derek
>
> > Collaborative Task Managementhttp://much4.us

David Pollak

unread,
Jul 31, 2008, 8:37:36 AM7/31/08
to lif...@googlegroups.com


Viktor Klang wrote:
Couldn't it be of interest to add the life-cycle management on Var itself?

onVarDeath
onVarBirth
What does the function signature look like?  How do you deal with type erasure?  How do you deal with 10 different vars all of the same type (there's no meaningful or guaranteed stable name for the vars)?

Viktor Klang

unread,
Jul 31, 2008, 8:49:31 AM7/31/08
to lif...@googlegroups.com
On Thu, Jul 31, 2008 at 2:37 PM, David Pollak <d...@athena.com> wrote:


Viktor Klang wrote:
Couldn't it be of interest to add the life-cycle management on Var itself?

onVarDeath
onVarBirth
What does the function signature look like? 

var onVarDeath : => Unit
var onVarBirth : => Unit
 
How do you deal with type erasure? 

Why do I need to deal with type erasure here?
 
How do you deal with 10 different vars all of the same type (there's no meaningful or guaranteed stable name for the vars)?

The stable identifier of the Var is it's Java Identity.


Consider: (pseudo code)

object MyJPASession extends RequestVar[Session](null, { set(MyEntityMgr.createSession) }, { get.close; remove })
 
class RequestVar[T](initial : T, onBirth : => Unit, onDeath : => Unit )
{
    set(initial)

    if(onBirth != null)
       LiftRules.onRequestStart( onBirth )

    if(onDeath != null)
       LiftRules.onRequestDone( onDeath )
}

Comments?

Cheers,
-V

David Pollak

unread,
Jul 31, 2008, 9:08:14 AM7/31/08
to lif...@googlegroups.com


Viktor Klang wrote:


On Thu, Jul 31, 2008 at 2:37 PM, David Pollak <d...@athena.com> wrote:


Viktor Klang wrote:
Couldn't it be of interest to add the life-cycle management on Var itself?

onVarDeath
onVarBirth
What does the function signature look like? 

var onVarDeath : => Unit
var onVarBirth : => Unit
 
How do you deal with type erasure? 

Why do I need to deal with type erasure here?
 
How do you deal with 10 different vars all of the same type (there's no meaningful or guaranteed stable name for the vars)?

The stable identifier of the Var is it's Java Identity.


Consider: (pseudo code)

object MyJPASession extends RequestVar[Session](null, { set(MyEntityMgr.createSession) }, { get.close; remove })
 
class RequestVar[T](initial : T, onBirth : => Unit, onDeath : => Unit )
{
    set(initial)

    if(onBirth != null)
       LiftRules.onRequestStart( onBirth )

    if(onDeath != null)
       LiftRules.onRequestDone( onDeath )
}

Comments?
Yeah.  You didn't read the code for RequestVar/SessionVar/AnyVar.  In the future, before making a suggestion, please look at the existing code and the existing implementation.  By looking at what exists, you'll have a better chance of making a helpful suggestion.

Specifically:
  • Anything that has a null in it is automatically suspect.
  • The value of the request var is set by the application and the default (not initial) value is calculated if the var is read before being set.  It has to be set within the "current" context because, even though it looks like a global, its backing store is in the LiftSession.
  • The "birth" of a RequestVar is at first access, not at the beginning of a request.  This is for efficiency as a RequestVar may not be accessed during a given request and thus it's a waste to have it "born" at the beginning of the request.  Additionally, there's no case that I can think of where the default value (which, BTW is a => T, not a T) cannot properly calculate a "birth" value.  Same for SessionVar
  • There are similar inefficiencies with cycling through all RequestVars at the end of the request and performing an onDeath that may or may not be helpful.
  • In the particular case you outline (getting and setting an entity manager for each and every request), this can be achieved with S.addAround(List[LoanWrapper]) Your LoanWrapper can create and close the entity manager.

It would also help to wait to understand what Derek's requirements are.

Viktor Klang

unread,
Jul 31, 2008, 1:08:49 PM7/31/08
to lif...@googlegroups.com

As I said, pseudocode.
 
  • The value of the request var is set by the application and the default (not initial) value is calculated if the var is read before being set.  It has to be set within the "current" context because, even though it looks like a global, its backing store is in the LiftSession.

The inital value bit wasn't actually discussed, and is of no relevance actually. so just stike that part.
 
  • The "birth" of a RequestVar is at first access, not at the beginning of a request.  This is for efficiency as a RequestVar may not be accessed during a given request and thus it's a waste to have it "born" at the beginning of the request.  Additionally, there's no case that I can think of where the default value (which, BTW is a => T, not a T) cannot properly calculate a "birth" value.  Same for SessionVar
 That's totally okay, "birth" is first access within a request.
This would also be okay for SessionVar
  • There are similar inefficiencies with cycling through all RequestVars at the end of the request and performing an onDeath that may or may not be helpful.

This is easily added by just adding the death-trigger if the born-trigger is ever executed. (if it hasn't been born, it cannot die)
 
  • In the particular case you outline (getting and setting an entity manager for each and every request), this can be achieved with S.addAround(List[LoanWrapper]) Your LoanWrapper can create and close the entity manager.
Yes, it's perfectly possible, but is it optimal? (everything we do in scala is possible from Java, but the reason we do it in Scala is because it is more convenient)

 


It would also help to wait to understand what Derek's requirements are.

Okay, then read Dereks mail and we can continue the discussion.

Cheers,
Viktor
 

Derek Chen-Becker

unread,
Aug 3, 2008, 4:44:11 PM8/3/08
to lif...@googlegroups.com
Sorry to take so long to respond on this. The original use case that David and I discussed was for the lifecycle of a JPA EntityManager instance. Typically this is not something that you would want "live" across a session, rather only across the request that's using it. You want to create the instance at the start of the request, and at the end of the request you flush and close the EM to force persisting to the database for anything that's changed during the request handling. Currently I'm using a LoanWrapper and a helper object (see http://liftweb.net/index.php/Lift_and_JPA_(javax.persistence)#Per-session_Entity_Manager ) to make it available across the duration of the request. I feel like it would be cleaner to be able to specify the setup and teardown functions directly on the request variable so that all of the state and its handlers can be colocated.

Derek

David Pollak

unread,
Aug 4, 2008, 7:09:58 PM8/4/08
to lif...@googlegroups.com
I just committed up some changes.

LiftSession.addSessionCleanup(f: LiftSession => Unit) - adds a function that is called during session cleanup (Note the "S" is not in scope)
S.addCleanupFunc(f: () => Unit) - adds a function to the current request that is called after the request has been serviced as the S scope is being wound down (you've got access to S methods, but changes to things like cookies will not work)

To clean up a RequestVar (or a SessionVar), do the following:

object myVar extends RequestVar(JPA.createContext) {
  override def cleanupFunc: () => Unit = Full(this.is.close())
}

The cleanupFunc will only be called if the variable was accessed during the request.

Please give this a try and let me know how it goes.

Thanks,

David

Derek Chen-Becker

unread,
Aug 7, 2008, 6:33:13 PM8/7/08
to lif...@googlegroups.com
Works great! Thanks!

Derek
Reply all
Reply to author
Forward
0 new messages