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
-------------------------------------
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.
> 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...
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
-------------------------------------
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).
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."
>
> -------------------------------------
>
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.
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
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.
> -------------------------------------
>
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
BTW do you have any comet in your page?
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 »
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
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
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 phantomYow, this is deep. Am I right in surmising the design decision to not
> 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.
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?
News to me. Heck, Lift seems to be promoting GAE at least a little,
> 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.)
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.
Yea, no plans to tackle this. Had run ins with Enums and readResolve
> 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.
in the past... Will definitely only persist key pieces of data to
reproduce state when the need arises.
Sweet -- I'd be happy to round out a little Wiki page example on this
> 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.
if I could get a little initial guidance on using this new
functionality.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
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 »
On Mar 13, 8:04 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
If I'm understanding correctly, it is a known limitation that a
> > 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)?
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);
> ...
>
> 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.
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 »
-------------------------------------
Luke Nezda<lne...@gmail.com> wrote:
--
Opened ticket http://www.assembla.com/spaces/liftweb/tickets/420-stateless-json
, though likely missed the Monday window.
Thanks,
- Luke
> ...
>
> read more »
Opened ticket http://www.assembla.com/spaces/liftweb/tickets/420-stateless-json
, though likely missed the Monday window.
Thanks,
- Luke
Very cool - Thank you David!
> > Thanks,
> > - Luke
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net
> Beginning Scalahttp://www.apress.com/book/view/1430219890