soliciting help/tips for dealing with session timeout

169 views
Skip to first unread message

Luke Nezda

unread,
Mar 11, 2010, 11:35:40 PM3/11/10
to Lift
Hello -

For context, I'm a fairly green webapp developer who is also new to
Scala and Lift. I have a basic search app which currently gets input
from a sole SHtml.ajaxText input field. It has no explicit login and
is only stateful for convenience, using ajax only to update/replace
page content in place (via JavaScript callback SetHtml). My app is
built with lift 2.0-M3 and hosted on Google App Engine. The usage
scenario I am having a problem with is the following:

1. Type query, hit enter (fires onBlur), see results/answer appended
to page under the search box, rejoice
2. Leave browser tab open [15-500 minutes passes...]
3. Return to page/tab, type another query, hit enter
4. [Nothing happens]
Firebug shows request encodes ajaxText unqiue identifier and query,
but responses from server are basically empty, as in:

HTTP/1.1 200 OK
Content-Type: text/javascript; charset=utf-8
X-Lift-Version: 2.0-M3
Content-Encoding: gzip
Date: Fri, 12 Mar 2010 02:43:09 GMT
Server: Google Frontend
Cache-Control: private, x-gzip-ok=""
Content-Length: 21
X-XSS-Protection: 0

A "hard" refresh of page restores all normal functionality, but that's
lame -- feels broken.

I read various threads about others having similar issues ([1][2][3])
and the cause seems to be rooted in combination of lift's secure
JavaScript unique-id based session garbage collection heartbeat
mechanism; client side includes stuff like:

jQuery(document).ready(function()
{liftAjax.lift_successRegisterGC();});
var lift_page = "F1142522731262NM5";

and / or the closure responsible for servicing each SHtml.ajaxText
session being gone for various reasons (timeout, reboot, etc.); client
side includes stuff like:

liftAjax.lift_ajaxHandler('F1142522731265M3X=' +
encodeURIComponent(this.value), null, null, null)

Is there a mechanism to get the client to just re-negotiate for a
"fresh" session rather than ceasing to be functional (without hard
refresh) ? (ideally, the ajaxStart condition could remain until the
connection was rectified) Maybe if the lift server receives a request
from an unknown session, it could return some kind of error to the
client which would trigger a re-negotiation? Maybe I have some faulty
assumptions? Any advice would be appreciated.

Thanks in advance,
- Luke

[1] handling session timeout
http://groups.google.com/group/liftweb/browse_frm/thread/8885cbdb6d24dfcd/5f10d13816663dfb
[2] occasional unresponsiveness after a long period of inactivity
http://groups.google.com/group/liftweb/browse_frm/thread/91a1b06187a45d4c/1c96e0d509ef7556
[3] Setting Session Timeout?
http://groups.google.com/group/liftweb/browse_frm/thread/d97a3552c3c24593/5c8b2c21a681f7a5

Naftoli Gugenheim

unread,
Mar 11, 2010, 11:43:53 PM3/11/10
to lif...@googlegroups.com
Garbage collection doesn't extend session lifetime?

-------------------------------------
Luke Nezda<lne...@gmail.com> wrote:

Hello -

--
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.

Luke Nezda

unread,
Mar 12, 2010, 9:04:14 AM3/12/10
to Lift
I guess the question, more succinctly, is how to smoothly deal with
orphaned sessions which might be caused by server restarts or
situations where the client loses connectivity for an extended period
of time (suspends laptop, goes to lunch / meeting / train / plane).

>  http://groups.google.com/group/liftweb/browse_frm/thread/8885cbdb6d24...


> [2] occasional unresponsiveness after a long period of inactivity

>  http://groups.google.com/group/liftweb/browse_frm/thread/91a1b06187a4...
> [3] Setting Session Timeout?
>  http://groups.google.com/group/liftweb/browse_frm/thread/d97a3552c3c2...

Marius

unread,
Mar 12, 2010, 9:51:47 AM3/12/10
to Lift
LiftSession is not bound to the physical connection. As long as the
browser sends up the proper cookie (jsessionid if you're using jee
containers such as jetty, tomcat etc) the server will pick up the
right session ... of course assuming that the session on server side
didn't timeout or was not terminated explicitly.

LiftSession provides hooks so that you know when the session
terminates or other events. But depends on what you mean by "smoothly
deal with orphaned sessions".

Regarding server restarts since the sessions are kept in memory
(sticky sessions) that context is lost. Nonetheless if depends on your
application design to cope in some extent with this case. For instance
you could use some state that you serialize/deserialize in/from DB and
use your own cookies to correlate the state. Thus even if the
HTTPSession is different your state would remain. However you would
loose bound functions which would be recreated when the page
refreshes.

Br's,
Marius

Naftoli Gugenheim

unread,
Mar 12, 2010, 1:11:08 PM3/12/10
to lif...@googlegroups.com
I think there are two questions.
One is why don't GC heartbeats extend the session lifetime (or do they)?
To have the function bound to the search box survive server restarts is probably related to clusters -- it involves the same problem of serialization functions.
Here is one possible partial solution. I'm sure it has been thought of (maybe I read it and forgot); if that's the case I'd appreciate being told if it's viable or why not.
Define a custom trait that extends Function0 with some extra abstract methods to perform serialization and deserialization. Then, when Lift binds such a function it should save a copy of it, and when it expires, delete it. When lift starts up any saved functions will be reloaded into memory.
Finally, define factories for composable prefabricated serializable functions. This would ultimately have to include a library of common operations. Also it should include an easy syntax for a function that declares its dependencies. Something like this:
SerializableFunction(var1, var2, var3) {
case (var1, var2, var3) =>
// logic that makes no assumptions about state other than its declared values
}
This would allow to write lift code that binds functions which can exist in "two instances of the same session."


-------------------------------------

David Pollak

unread,
Mar 12, 2010, 1:56:53 PM3/12/10
to lif...@googlegroups.com
On Fri, Mar 12, 2010 at 6:04 AM, Luke Nezda <lne...@gmail.com> wrote:
I guess the question, more succinctly, is how to smoothly deal with
orphaned sessions which might be caused by server restarts or
situations where the client loses connectivity for an extended period
of time (suspends laptop, goes to lunch / meeting / train / plane).

Lift has a heart-beat back to the server every 75 seconds.  This heartbeat keeps the session alive as well as telling Lift about what pages are visible (and thus, what GUIDs on the client are associated with what functions on the client... we sometimes call this "Garbage Collection" but it differs from JVM GC.)  So, as long as your server is up and the browser is pointed to a page in your app, you will retain your session.

If your server gets restarted or you sleep your machine and then unsleep (or some other thing that causes the session to be destroyed), the next time the browser does a heartbeat operation, Lift will send a command back to the browser that will cause the current page to be reloaded so all the GUIDs will be reassocaited with the right functions.

Please take a look at http://foursquare.com  There's a lot of Ajax that they do.  Open a 4sq page and go get lunch and come back, the Ajax links are still valid.  Sleep your laptop and take the train home.  Open your laptop.  The page will reload and will be active and correct.

If you find that things don't work this way for you, please put together an example of the failure and we'll work through it with you.

Thanks,

David

PS -- You can also do stateless Ajax calls with Lift... just not using the built-in SHtml stuff.
 



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Marius

unread,
Mar 12, 2010, 2:04:03 PM3/12/10
to Lift
Naftoli, I welcome you to try it out. If you want to do an analysis
in this area by all means go for it. Nonetheless the question still
remains: user's functions may capture other references (and users may
not even be fully aware of this) that of course may not be
*serializable* (in the broader sense). Detecting these object trees is
not not without challenges. I was thinking of compiler plugin the
determines object trees. Speaking with Martin this is quite tricky but
potentially doable. He pointed me to a fellow doing this type of
research but never got his feedback.

One not very user friendly solution is to extend indeed FunctionX and
add read/write methods to it and let the user to implement it. As I
said : not user friendly. Terracotta options were considered and this
is a route that we did not abandoned yet. Hopefully Jonas Boner could
help here as well.

Regarding GC heart beats these requests are PER SESSION since it's
about cleaning up unused bound function and bound function are always
per session.

On Mar 12, 8:11 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> I think there are two questions.
> One is why don't GC heartbeats extend the session lifetime (or do they)?
> To have the function bound to the search box survive server restarts is probably related to clusters -- it involves the same problem of serialization functions.
> Here is one possible partial solution. I'm sure it has been thought of (maybe I read it and forgot); if that's the case I'd appreciate being told if it's viable or why not.
> Define a custom trait that extends Function0 with some extra abstract methods to perform serialization and deserialization. Then, when Lift binds such a function it should save a copy of it, and when it expires, delete it. When lift starts up any saved functions will be reloaded into memory.
> Finally, define factories for composable prefabricated serializable functions. This would ultimately have to include a library of common operations. Also it should include an easy syntax for a function that declares its dependencies. Something like this:
> SerializableFunction(var1, var2, var3) {
>   case (var1, var2, var3) =>
>     // logic that makes no assumptions about state other than its declared values}
>
> This would allow to write lift code that binds functions which can exist in "two instances of the same session."
>
> -------------------------------------
>

David Pollak

unread,
Mar 12, 2010, 2:10:58 PM3/12/10
to lif...@googlegroups.com
On Fri, Mar 12, 2010 at 11:04 AM, Marius <marius...@gmail.com> wrote:
Naftoli, I welcome you to try it out. If you want  to do an analysis
in this area by all means go for it. Nonetheless the question still
remains: user's functions may capture other references (and users may
not even be fully aware of this) that of course may not be
*serializable* (in the broader sense). Detecting these object trees is
not not without challenges. I was thinking of compiler plugin the
determines object trees. Speaking with Martin this is quite tricky but
potentially doable. He pointed me to a fellow doing this type of
research but never got his feedback.

I spoke with Martin and Tiark (the continuations guy in Martin's group) about function/continuation serialization.  It's a very, very difficult issue, especially with Scala singletons.  Right now, serializing anything beyond the most trivial function (like Ian Clarke did in the Swarm demo) is nearly intractable unless one writes their own Scala-friendly serializer.
 

One not very user friendly solution is to extend indeed FunctionX and
add read/write methods to it and let the user to implement it. As I
said : not user friendly. Terracotta options were considered and this
is a route that we did not abandoned yet. Hopefully Jonas Boner could
help here as well.

Yep... Terracotta is the best solution for a mesh of Lift.
 
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Naftoli Gugenheim

unread,
Mar 12, 2010, 3:40:48 PM3/12/10
to lif...@googlegroups.com
Regarding tracking references, you are correct of course. My suggestion relies on people that use it, not using any references other than those declared (which could be required to extend some trait etc.).
As far as user friendliness, my suggestion (which I apologize for the brevity) takes a three pronged approach:
1) The most basic and flexible usage requires user-implemented serializer/deserializer functions. This is very flexible but very not user friendly.
2) The next level would be like the example code I gave, where you declare your dependencies and let Lift pass them to you. It's your responsibility to not use outside state. (Of course, a compiler plugin to enforce it would be nice but not mandatory.)
3) The highest level would be prefabricated composable pieces of server-sided functionality. Sort of like a server sided DSL parallel to the javascript DSL.
It's not a perfect solution, but at the end of the day I think we agree that a perfect solution (any function transferrable across clusters for example) is at least nearly impossible. But the three prongs may make most use cases doable.
Before we discuss writing code ;) I'd like to hear from the experienced experts whether this approach is a viable solution in the first place. In the abstract it sound to me like a good idea but my experience is negligible so it may not make sense in actuality.
-------------------------------------
Marius<marius...@gmail.com> wrote:

Naftoli, I welcome you to try it out. If you want to do an analysis
in this area by all means go for it. Nonetheless the question still
remains: user's functions may capture other references (and users may
not even be fully aware of this) that of course may not be
*serializable* (in the broader sense). Detecting these object trees is
not not without challenges. I was thinking of compiler plugin the
determines object trees. Speaking with Martin this is quite tricky but
potentially doable. He pointed me to a fellow doing this type of
research but never got his feedback.

One not very user friendly solution is to extend indeed FunctionX and


add read/write methods to it and let the user to implement it. As I
said : not user friendly. Terracotta options were considered and this
is a route that we did not abandoned yet. Hopefully Jonas Boner could
help here as well.

Regarding GC heart beats these requests are PER SESSION since it's

Marius

unread,
Mar 12, 2010, 4:00:41 PM3/12/10
to Lift
Personally I'm hesitant about these solutions as being actual
solutions to the problem. Assume that a user only binds functions that
do not interact with outside in terms of direct references (so there
is no capturing involved). They get a value (say from an input text),
do some computation and return something. In this case we only need a
mechanism to replicate functions ID's across cluster nodes and
*serialize* the function object (replication would be done on function
creation from AFuncHolder probably). Since such function do not really
hold any state, serialization should be fast ... furthermore function
ID is just a string. You could use AKKA actors to achieve this or just
just persist this pair into a data-store. Thus the same function ID
exists on all nodes thus ANY node could successfully process the
request.

I'm pretty sure that this would work but I don't see it as a solid
solution to the problem (partial solution ... eeeh maybe) nonetheless
I'd love if you could prove me wrong.

On Mar 12, 10:40 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Regarding tracking references, you are correct of course. My suggestion relies on people that use it, not using any references other than those declared (which could be required to extend some trait etc.).
> As far as user friendliness, my suggestion (which I apologize for the brevity) takes a three pronged approach:
> 1) The most basic and flexible usage requires user-implemented serializer/deserializer functions. This is very flexible but very not user friendly.
> 2) The next level would be like the example code I gave, where you declare your dependencies and let Lift pass them to you. It's your responsibility to not use outside state. (Of course, a compiler plugin to enforce it would be nice but not mandatory.)
> 3) The highest level would be prefabricated composable pieces of server-sided functionality. Sort of like a server sided DSL parallel to the javascript DSL.
> It's not a perfect solution, but at the end of the day I think we agree that a perfect solution (any function transferrable across clusters for example) is at least nearly impossible. But the three prongs may make most use cases doable.
> Before we discuss writing code ;) I'd like to hear from the experienced experts whether this approach is a viable solution in the first place. In the abstract it sound to me like a good idea but my experience is negligible so it may not make sense in actuality.
> -------------------------------------
>

Luke Nezda

unread,
Mar 13, 2010, 2:32:38 PM3/13/10
to Lift
First, I appreciate all this useful information that's been offered.
In particular, I'm intrigued by the stateless Ajax calls option.

I realize that server-side state in bound functions will be lost on
server restart without my taking special (function/state
serialization) measures; for now, for my app, this is no big deal. It
is unfortunate that on page refresh (e.g., due to server restart), all
client side forms are cleared.

I have been working on finding a formula to replicate this issue using
a local server, but have had no success. As far as I can tell,
everything is working as advertised / designed.

Here's some of the tests I conducted:

Scenario 1: restart server
0. start local server
1. start session, issue search, see response/result, rejoice
2. kill local server
( see 75s hearbeat timeout, and subsequent 15s failure mode heartbeats
timeout )
4. start local server
5. when 15s failure mode heartbeat finds connectivity, client page
refreshes (wiping my current page including its form contents which
kinda sucks), but otherwise the page is functional
If I type input before heartbeat finds connectivity, basically same
thing happens: input is ignored, but page is refreshed, and after
retyping input, response is returned

Scenario 2: suspend server
1. start session, issue search, see response/result
2. suspending my local server process (CTRL+Z) for 20 minutes
3. resume server process
4. worked pretty well; ignored input and refreshed page

Scenario 3: suspend server and client
* working on doing this right -- want to do actual extended suspend to
trip various timers

Scenario 4: kill server, suspend client, start server, resume client
* working on doing this right -- want to do actual extended suspend to
trip various timers

Scenario 5: manually delete JSESSIONID cookie (don't think I ever
intentionally did this)
Essentially same as Scenarios 1 and 2; worked pretty well; ignored
input and refreshed page

I definitely saw the scenario I described multiple times when working
with Google App Engine (GAE) hosted versions of my app, but I still
have no reproducible test case, so for now this failure mode might as
well be a figment of my imagination which is maddening. I will be on
the lookout for this to happen again and try to characterize it better
with more diagnostics.

Other details which might or might not trigger any advice on tracking
this down...

*** I remember thinking it was strange that when looking at the DOM of
the non-functional page in Firebug, it said it couldn't get /
ajax_request/liftAjax.js . I bet this was the cause of my problem. I
will screenshot this next time :)
<script type="text/javascript" src="/ajax_request/liftAjax.js"></
script>
** I think the last time the failure occurred, the heartbeat was in
failure mode, as I believe it was at 15s intervals rather than healthy
75s intervals, but if I recall correctly, it wasn't timing out?
Sadly, I didn't store enough diagnostics the last time I saw this
issue to be sure. Maybe the server didn't respond fast enough or its
reply was somehow incomplete/malformed ?
** At this point, it seems likely to be something peculiar to the GAE
environment.
* I frequently have multiple sessions open in tabs in one or more
browsers, and suspend the whole computer.
* I sometimes have some tabs with sessions to a locally hosted version
and others pointing to the GAE hosted version.
* I always run my app in development mode, not production mode.
* Could Firebug (maybe network activity trace) be breaking the
session? I sometimes have the Firebug Network tab open which shows
the checked option "Disable Browser Cache"

Kind regards,
- Luke

On Mar 12, 12:56 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:


> On Fri, Mar 12, 2010 at 6:04 AM, Luke Nezda <lne...@gmail.com> wrote:
> > I guess the question, more succinctly, is how to smoothly deal with
> > orphaned sessions which might be caused by server restarts or
> > situations where the client loses connectivity for an extended period
> > of time (suspends laptop, goes to lunch / meeting / train / plane).
>
> Lift has a heart-beat back to the server every 75 seconds.  This heartbeat
> keeps the session alive as well as telling Lift about what pages are visible
> (and thus, what GUIDs on the client are associated with what functions on
> the client... we sometimes call this "Garbage Collection" but it differs
> from JVM GC.)  So, as long as your server is up and the browser is pointed
> to a page in your app, you will retain your session.
>
> If your server gets restarted or you sleep your machine and then unsleep (or
> some other thing that causes the session to be destroyed), the next time the
> browser does a heartbeat operation, Lift will send a command back to the
> browser that will cause the current page to be reloaded so all the GUIDs
> will be reassocaited with the right functions.
>

> Please take a look athttp://foursquare.com There's a lot of Ajax that they

> > liftweb+u...@googlegroups.com<liftweb%2Bunsu...@googlegroups.com>


> > .
> > > For more options, visit this group athttp://
> > groups.google.com/group/liftweb?hl=en.
>
> > --
> > 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<liftweb%2Bunsu...@googlegroups.com>


> > .
> > For more options, visit this group at
> >http://groups.google.com/group/liftweb?hl=en.
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net

> Beginning Scalahttp://www.apress.com/book/view/1430219890

Marius

unread,
Mar 13, 2010, 2:52:07 PM3/13/10
to Lift
On scenario 1 you see this behavior because when the connectivity is
re-established the ajax requests are propagated with a JSESSIONID that
is unknown by the server thus a new server session is created and your
context lost. Under such circumstances a page refresh is pretty close
to the best thing possible. Note that the heartbeat ajax requests do
not carry information (such as form fields), just the page id. It
couldn't carry form data as it is not part of the application logic,
pages can have multiple forms etc.

BTW do you have any comet in your page?

Luke Nezda

unread,
Mar 13, 2010, 4:22:00 PM3/13/10
to Lift
I think my expectations about form fields being "preserved" may be
naive. Nevertheless, my observation is when a server session dies
(but client doesn't), and meanwhile, client side, form input is typed,
stored in the DOM tree, and finally posted to the server, it is
seemingly ignored in lieu of a full page refresh. Maybe it would be
cool if the server created the fresh session, and then I guess
"replayed" the POST input it received. This would only work if client
input beat the page-refreshing heartbeat indicating restored
connectivity. That case raises other questions. Is it possible for a
"page" to "get associated with" a new cookie without a refresh? Is it
possible for a page / page component to "get associated with"
different GUIDs without a refresh? Probably not, or I presume it
would just be implemented this way, effectively letting the browser
maintain "transient" page state like form fields.

I think David's suggestion of using stateless Ajax calls would suit my
application better, avoids heartbeat timeout flushing page content.
Do you have any pointers / Lift examples of this?

As to your question, no the page currently uses no Comet; that would
be cool (e.g., recent searches), but GAE doesn't currently let apps
spawn threads and its my understanding this means no Lift Comet. For
reference, the current iteration of my app is hosted at
http://yawni-online.appspot.com/ . The source is all available (git://
yawni.git.sourceforge.net/gitroot/yawni/yawni in Maven module
'online').

Kind regards,
- Luke

> ...
>
> read more »

David Pollak

unread,
Mar 13, 2010, 4:37:29 PM3/13/10
to lif...@googlegroups.com
A couple of quick things... I think I know why you're seeing phantom behavior with GAE... GAE does not preserve the same JVM across requests and all the stuff in the JVM that was not explicitly placed in a servlet session variable will be lost.  We do not store Lift sessions in servlet sessions, but in parallel to servlet sessions (some servlets barf if stuff in sessions is not serializable).  Thus, it appeared that the session was live, but the Lift stuff was lost.

But, I have a simple cure for this... don't use GAE... it sucks.  It's neither scalable (contact me privately for information about Google engineers avoiding BigTable on GAE because of its suboptimal performance characteristics), nor is it cost effective for anything beyond free (if you've got $10/mo to host, you can do better than GAE), nor does GAE give you any real benefits, but imposes a lot of constraints (no new threads, no SQL, no data portability, etc.)

In terms of serializing Scala functions, it's not possible without implementing software that approaches JRebel in complexity.  There are constructs (e.g., closing over a 'var' across more than 1 function/serialization event) that will at the very best have really bad consequences (serialization appears to work, but silently fails because there are two different de-serialized references to what is conceptually a single heap-store var.)  Don't waste your time barking considering it.

It sounds to me like serializing the forms to JSON and then making an Ajax call on a stable URL will be the best bet for you.  On Monday, there will be support for Req.json such that you can more easily extract JSON from a REST call.  This combined with disabling the Lift heartbeat should give you stateless support for JSON.

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

Luke Nezda

unread,
Mar 13, 2010, 5:35:13 PM3/13/10
to Lift
On Mar 13, 3:37 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:

> A couple of quick things... I think I know why you're seeing phantom
> behavior with GAE... GAE does not preserve the same JVM across requests and
> all the stuff in the JVM that was not explicitly placed in a servlet session
> variable will be lost.  We do not store Lift sessions in servlet sessions,
> but in parallel to servlet sessions (some servlets barf if stuff in sessions
> is not serializable).  Thus, it appeared that the session was live, but the
> Lift stuff was lost.

Yow, this is deep. Am I right in surmising the design decision to not
persist state server side in the servlet sessions is strongly related
to the difficulty of serializing/deserializing first class functions
and singletons (i.e., objects)? Is this why /ajax_request/liftAjax.js
was occasionally not available in the GAE hosted version of my app?

> But, I have a simple cure for this... don't use GAE... it sucks.  It's
> neither scalable (contact me privately for information about Google
> engineers avoiding BigTable on GAE because of its suboptimal performance
> characteristics), nor is it cost effective for anything beyond free (if
> you've got $10/mo to host, you can do better than GAE), nor does GAE give
> you any real benefits, but imposes a lot of constraints (no new threads, no
> SQL, no data portability, etc.)

News to me. Heck, Lift seems to be promoting GAE at least a little,
by hosting a demo there (http://lift-example.appspot.com/) (or maybe
that's by some random Lift user like me?). I didn't have need of
persistence or scalability and was coping with the limitations fine,
especially for the price (free), but none-the-less, good to know. My
hosting provider doesn't do servlets (yet anyway), so I just went for
GAE, but certainly expected I'd find quality and cost would be there
if/when I needed it. Sounds like for the short term, if I make my app
stateless, I'll be OK with GAE at least a little longer.

> In terms of serializing Scala functions, it's not possible without
> implementing software that approaches JRebel in complexity.  There are
> constructs (e.g., closing over a 'var' across more than 1
> function/serialization event) that will at the very best have really bad
> consequences (serialization appears to work, but silently fails because
> there are two different de-serialized references to what is conceptually a
> single heap-store var.)  Don't waste your time barking considering it.

Yea, no plans to tackle this. Had run ins with Enums and readResolve
in the past... Will definitely only persist key pieces of data to
reproduce state when the need arises.

> It sounds to me like serializing the forms to JSON and then making an Ajax
> call on a stable URL will be the best bet for you.  On Monday, there will be
> support for Req.json such that you can more easily extract JSON from a REST
> call.  This combined with disabling the Lift heartbeat should give you
> stateless support for JSON.

Sweet -- I'd be happy to round out a little Wiki page example on this
if I could get a little initial guidance on using this new
functionality.

Thanks very much for your detailed response!

Kind regards,
- Luke

> On Sat, Mar 13, 2010 at 1:22 PM, Luke Nezda <lne...@gmail.com> wrote:


> > I think my expectations about form fields being "preserved" may be
> > naive.  Nevertheless, my observation is when a server session dies
> > (but client doesn't), and meanwhile, client side, form input is typed,
> > stored in the DOM tree, and finally posted to the server, it is
> > seemingly ignored in lieu of a full page refresh.  Maybe it would be
> > cool if the server created the fresh session, and then I guess
> > "replayed" the POST input it received.  This would only work if client
> > input beat the page-refreshing heartbeat indicating restored
> > connectivity.  That case raises other questions.  Is it possible for a
> > "page" to "get associated with" a new cookie without a refresh?  Is it
> > possible for a page / page component to "get associated with"
> > different GUIDs without a refresh?  Probably not, or I presume it
> > would just be implemented this way, effectively letting the browser
> > maintain "transient" page state like form fields.
>
> > I think David's suggestion of using stateless Ajax calls would suit my
> > application better, avoids heartbeat timeout flushing page content.
> > Do you have any pointers / Lift examples of this?
>
> > As to your question, no the page currently uses no Comet; that would
> > be cool (e.g., recent searches), but GAE doesn't currently let apps
> > spawn threads and its my understanding this means no Lift Comet.  For
> > reference, the current iteration of my app is hosted at

> >http://yawni-online.appspot.com/.  The source is all available (git://

> > > > > Please take a look athttp://foursquare.comThere's a lot of Ajax

> > liftweb+u...@googlegroups.com<liftweb%2Bunsu...@googlegroups.com>


> > .
> > For more options, visit this group at
> >http://groups.google.com/group/liftweb?hl=en.
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net

> Beginning Scalahttp://www.apress.com/book/view/1430219890

David Pollak

unread,
Mar 13, 2010, 8:04:33 PM3/13/10
to lif...@googlegroups.com
On Sat, Mar 13, 2010 at 2:35 PM, Luke Nezda <lne...@gmail.com> wrote:
On Mar 13, 3:37 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> A couple of quick things... I think I know why you're seeing phantom
> behavior with GAE... GAE does not preserve the same JVM across requests and
> all the stuff in the JVM that was not explicitly placed in a servlet session
> variable will be lost.  We do not store Lift sessions in servlet sessions,
> but in parallel to servlet sessions (some servlets barf if stuff in sessions
> is not serializable).  Thus, it appeared that the session was live, but the
> Lift stuff was lost.

Yow, this is deep.  Am I right in surmising the design decision to not
persist state server side in the servlet sessions is strongly related
to the difficulty of serializing/deserializing first class functions
and singletons (i.e., objects)?

No.  The way servlets deal with session expiration is haphazard and varies widely from container to container.  We had a lot of reports of stuff working in Jetty during dev, but failing in Tomcat, etc., so we wrote our own session management.
 
 Is this why /ajax_request/liftAjax.js
was occasionally not available in the GAE hosted version of my app?

It's a distinct possibility.
 

> But, I have a simple cure for this... don't use GAE... it sucks.  It's
> neither scalable (contact me privately for information about Google
> engineers avoiding BigTable on GAE because of its suboptimal performance
> characteristics), nor is it cost effective for anything beyond free (if
> you've got $10/mo to host, you can do better than GAE), nor does GAE give
> you any real benefits, but imposes a lot of constraints (no new threads, no
> SQL, no data portability, etc.)

News to me.  Heck, Lift seems to be promoting GAE at least a little,
by hosting a demo there (http://lift-example.appspot.com/) (or maybe
that's by some random Lift user like me?).

It went up to demonstrate that Lift ran on the GAE after the wicked bad communicators at Google listed Lift as not working on GAE without even talking to us first.  This was after we participated in the GAE beta but were not given a chance to run Lift on the final GAE nor were we given a heads up that GAE was launching.  I tried for a week to get the attention of the GAE folks (including asking Lex Spoon to talk to them.)  They pretty much ignored us until Yaminaka-san changed the few calls that were blocking Lift on GAE and we put up the demo.

But after spending some time actually working with GAE and exploring the limitations, I came to the conclusion that there's no good use for GAE.  In terms of speed and power, it slots in below the Xen slice-host players of the world.  If you want real speed and real growth, GAE is the last place to get it.  Plus, with all the limitations (no separate threads, no predictability as to when/where your JVM is running, etc.) it's impossible to build something real that might turn into something big on GAE.
 
 I didn't have need of
persistence or scalability and was coping with the limitations fine,
especially for the price (free), but none-the-less, good to know.  My
hosting provider doesn't do servlets (yet anyway), so I just went for
GAE, but certainly expected I'd find quality and cost would be there
if/when I needed it.  Sounds like for the short term, if I make my app
stateless, I'll be OK with GAE at least a little longer.

Take a look at slicehost.com or http://prgmr.com/xen/  In both cases, <= $20/mo buys you all you need to run a Lift app.
 

> In terms of serializing Scala functions, it's not possible without
> implementing software that approaches JRebel in complexity.  There are
> constructs (e.g., closing over a 'var' across more than 1
> function/serialization event) that will at the very best have really bad
> consequences (serialization appears to work, but silently fails because
> there are two different de-serialized references to what is conceptually a
> single heap-store var.)  Don't waste your time barking considering it.

Yea, no plans to tackle this.  Had run ins with Enums and readResolve
in the past...  Will definitely only persist key pieces of data to
reproduce state when the need arises.

> It sounds to me like serializing the forms to JSON and then making an Ajax
> call on a stable URL will be the best bet for you.  On Monday, there will be
> support for Req.json such that you can more easily extract JSON from a REST
> call.  This combined with disabling the Lift heartbeat should give you
> stateless support for JSON.

Sweet -- I'd be happy to round out a little Wiki page example on this
if I could get a little initial guidance on using this new
functionality.

Please open a ticket at http://ticket.liftweb.net asking for an example.  I'll work on it.
 
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

Luke Nezda

unread,
Mar 14, 2010, 6:42:54 PM3/14/10
to Lift
On Mar 13, 8:04 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:

> On Sat, Mar 13, 2010 at 2:35 PM, Luke Nezda <lne...@gmail.com> wrote:
> > On Mar 13, 3:37 pm, David Pollak <feeder.of.the.be...@gmail.com>
> > wrote:
> > > A couple of quick things... I think I know why you're seeing phantom
> > > behavior with GAE... GAE does not preserve the same JVM across requests
> > and
> > > all the stuff in the JVM that was not explicitly placed in a servlet
> > session
> > > variable will be lost.  We do not store Lift sessions in servlet
> > sessions,
> > > but in parallel to servlet sessions (some servlets barf if stuff in
> > sessions
> > > is not serializable).  Thus, it appeared that the session was live, but
> > the
> > > Lift stuff was lost.
>
> > Yow, this is deep.  Am I right in surmising the design decision to not
> > persist state server side in the servlet sessions is strongly related
> > to the difficulty of serializing/deserializing first class functions
> > and singletons (i.e., objects)?

If I'm understanding correctly, it is a known limitation that a
stateful Lift session can fail in undefined ways if requests of a
session are not serviced by the same JVM instance (e.g., those found
in clustered containers like GAE); i.e., stateful Lift requires sticky
sessions. I don't know how other frameworks behave with respect to
clustering - maybe this is a common simplifying assumption?

> Take a look at slicehost.com orhttp://prgmr.com/xen/ In both cases, <=


> $20/mo buys you all you need to run a Lift app.
>
>
>
>
>
> > > In terms of serializing Scala functions, it's not possible without
> > > implementing software that approaches JRebel in complexity.  There are
> > > constructs (e.g., closing over a 'var' across more than 1
> > > function/serialization event) that will at the very best have really bad
> > > consequences (serialization appears to work, but silently fails because
> > > there are two different de-serialized references to what is conceptually
> > a
> > > single heap-store var.)  Don't waste your time barking considering it.
>
> > Yea, no plans to tackle this.  Had run ins with Enums and readResolve
> > in the past...  Will definitely only persist key pieces of data to
> > reproduce state when the need arises.
>
> > > It sounds to me like serializing the forms to JSON and then making an
> > Ajax
> > > call on a stable URL will be the best bet for you.  On Monday, there will
> > be
> > > support for Req.json such that you can more easily extract JSON from a
> > REST
> > > call.  This combined with disabling the Lift heartbeat should give you
> > > stateless support for JSON.
>
> > Sweet -- I'd be happy to round out a little Wiki page example on this
> > if I could get a little initial guidance on using this new
> > functionality.
>

> Please open a ticket athttp://ticket.liftweb.netasking for an example.

> ...
>
> read more »

David Pollak

unread,
Mar 14, 2010, 9:38:55 PM3/14/10
to lif...@googlegroups.com
On Sun, Mar 14, 2010 at 3:42 PM, Luke Nezda <lne...@gmail.com> wrote:
On Mar 13, 8:04 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:

> > Yow, this is deep.  Am I right in surmising the design decision to not
> > persist state server side in the servlet sessions is strongly related
> > to the difficulty of serializing/deserializing first class functions
> > and singletons (i.e., objects)?

If I'm understanding correctly, it is a known limitation that a
stateful Lift session can fail in undefined ways if requests of a
session are not serviced by the same JVM instance (e.g., those found
in clustered containers like GAE);

The GAE imposes more limitations on sessions and the JVM in general than any other J/EE container that I know of.  Given the limitations of the GAE (no thread creation, undefined results when using statics, no inter-session communication mechanism, etc.), Lift does not do well in it.

If you have a site (like Four Square, for example and Novell Pulse for another example) that goes beyond needing a single JVM to support the traffic (e.g., Four Square did 375,000 checkins yesterday on their Lift-based application), then contact me off-list and I'll work with you to find a solution.

But, for the normal web site traffic, Lift's session mechanism is invisible to you.

So, if you've got a site that is growing faster than Twitter, Lift can scale along with it.  If you're everyone else, the mechanisms that Lift uses to manage state (except on GAE with imposes ridiculous limitations) is invisible to your application.

If you want a cloud-based solution, SliceHost and others provide one solution.  Stax.net provides another (and Stax is Lift-friendly.)
 
 
> ...
>
> read more »

--
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.

Luke Nezda

unread,
Mar 15, 2010, 12:15:07 AM3/15/10
to Lift
On Mar 13, 8:04 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> Take a look at slicehost.com orhttp://prgmr.com/xen/ In both cases, <=

> $20/mo buys you all you need to run a Lift app.
>
>
>
>
>
> > > In terms of serializing Scala functions, it's not possible without
> > > implementing software that approaches JRebel in complexity.  There are
> > > constructs (e.g., closing over a 'var' across more than 1
> > > function/serialization event) that will at the very best have really bad
> > > consequences (serialization appears to work, but silently fails because
> > > there are two different de-serialized references to what is conceptually
> > a
> > > single heap-store var.)  Don't waste your time barking considering it.
>
> > Yea, no plans to tackle this.  Had run ins with Enums and readResolve
> > in the past...  Will definitely only persist key pieces of data to
> > reproduce state when the need arises.
>
> > > It sounds to me like serializing the forms to JSON and then making an
> > Ajax
> > > call on a stable URL will be the best bet for you.  On Monday, there will
> > be
> > > support for Req.json such that you can more easily extract JSON from a
> > REST
> > > call.  This combined with disabling the Lift heartbeat should give you
> > > stateless support for JSON.
>
> > Sweet -- I'd be happy to round out a little Wiki page example on this
> > if I could get a little initial guidance on using this new
> > functionality.
>
> Please open a ticket athttp://ticket.liftweb.netasking for an example.

> I'll work on it.

I'm probably missing something really obvious here, but I can't figure
out how to file a ticket. I registered with assembla, verified my
email address, logged in/out/in, and even watched the introductory
ticket video, but https://www.assembla.com/spaces/liftweb/tickets /
http://ticket.liftweb.net has no big "New Ticket" button above the
"Active by Milestone" on the Tickets tab? (OS X 10.6.2, tried via
Firefox 3.6 and Safari 4.0.5) Please advise.

> ...
>
> read more »

Naftoli Gugenheim

unread,
Mar 15, 2010, 12:50:39 AM3/15/10
to lif...@googlegroups.com
You have to make yourself be a "watcher" of the lift space. I think there's a link near the top right corner of the page to watch the space.

-------------------------------------
Luke Nezda<lne...@gmail.com> wrote:

--

Luke Nezda

unread,
Mar 15, 2010, 9:18:16 PM3/15/10
to Lift
On Mar 13, 8:04 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> Take a look at slicehost.com orhttp://prgmr.com/xen/ In both cases, <=

Opened ticket http://www.assembla.com/spaces/liftweb/tickets/420-stateless-json
, though likely missed the Monday window.

Thanks,
- Luke

> ...
>
> read more »

David Pollak

unread,
Mar 16, 2010, 6:43:48 PM3/16/10
to lif...@googlegroups.com
On Mon, Mar 15, 2010 at 6:18 PM, Luke Nezda <lne...@gmail.com> wrote:

>
> Please open a ticket at http://ticket.liftweb.net asking for an example.
> I'll work on it.


Opened ticket http://www.assembla.com/spaces/liftweb/tickets/420-stateless-json
, though likely missed the Monday window.

You can see the code running at http://demo.liftweb.net/stateless_json

You register the stateless handler in Boot with:

    LiftRules.statelessDispatchTable.append {
      case r @ Req("stateless_json_call" :: Nil, _, PostRequest) => () => handleJson(r)
    }

And the actual handler is:

  def handleJson(req: Req): Box[LiftResponse] =
  for {
    json <- req.json // get the JSON
    JObject(List(JField("command", JString(cmd)), JField("params", JString(params)))) <- json // extract the command
  } yield JavaScriptResponse(SetHtml("json_result",cmd match { // build the response
        case "show" => Text(params)
        case "textile" =>  TextileParser.toHtml(params, Empty)
        case "count" => Text(params.length+" Characters")
        case x => <b>Problem... didn't handle JSON message {x}</b>
      }))

On the client side, the JavaScript looks like:

<button onclick="jQuery.ajax({ url : 'stateless_json_call', data : JSON.stringify({'command': document.getElementById('json_select').value, 'params':document.getElementById('json_question').value}), type : 'POST', dataType : 'script', contentType: 'text/json', timeout : 5000, cache : false});">Click Me</button>

You can use whatever Ajax call mechanism you want... just change the url and the data you send.

Thanks,

David

 

Thanks,
- Luke


Luke Nezda

unread,
Mar 18, 2010, 12:55:11 AM3/18/10
to Lift
On Mar 16, 5:43 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:

> On Mon, Mar 15, 2010 at 6:18 PM, Luke Nezda <lne...@gmail.com> wrote:
>
> > > Please open a ticket athttp://ticket.liftweb.netasking for an example.

> > > I'll work on it.
>
> > Opened ticket
> >http://www.assembla.com/spaces/liftweb/tickets/420-stateless-json
> > , though likely missed the Monday window.
>
> You can see the code running athttp://demo.liftweb.net/stateless_json

Very cool - Thank you David!

> > Thanks,
> > - Luke
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net

> Beginning Scalahttp://www.apress.com/book/view/1430219890

Dan Carleton

unread,
Feb 11, 2013, 6:54:03 PM2/11/13
to lif...@googlegroups.com
Can anyone speak to Luke's idea below?  I didn't see it addressed anywhere.

Context: The inability of a server node to go down without impacting users is an architectural sticking point for me.  In my usage of Lift, even after many AJAX transformations of the DOM, page state can be recreated using the URL and database queries alone.  I use the Lift session as a cache, and only keep transient data in the browser.  Therefore any failed requests could be safely replayed to a different back-end node in the event of renegotiation.  However the AJAX machinery as Luke describes, dropping any input the user has supplied on the floor.  Given my usage pattern, couldn't Lift instead retry with the new node?

Thanks,

- Dan

Antonio Salazar Cardozo

unread,
Feb 11, 2013, 11:29:30 PM2/11/13
to lif...@googlegroups.com
You've dug up a very old post; typically it would be considered more polite to initiate a new discussion around the matter, as it's been 2 years since this discussion took place.

That said, yes, a node going down will impact the users bound to that node if you've bound it using Lift's stateful features. You are of course free to avoid those stateful features in areas of your application where you want to add resilience to node failure (including every portion of the application if you so wish). However, you'll lose the security benefits (non-replayability, etc) that statefulness provides (or you'll have to reimplement them), amongst other things. There have been thoughts in my head about how to rehydrate field names on a given page via AJAX when a session is lost in the backend, but they're still vague ideas at the moment and I'm not at all certain that they don't break the security aspects that stateful field names provide.
Thanks,
Antonio

Dan Carleton

unread,
Feb 12, 2013, 1:50:35 AM2/12/13
to lif...@googlegroups.com
Antonio,

Thanks for your thoughts.  In the future I'll excerpt instead of reviving an old thread.

- Dan
Reply all
Reply to author
Forward
0 new messages