Comet Freezing (Lift 2.4 M1, Jetty 6)

109 views
Skip to first unread message

Dan G.

unread,
Jun 17, 2011, 3:47:04 PM6/17/11
to Lift
I recently started playing with Lift and I have been very impressed so
far. However, I am having a problem that I cannot seem to figure out.

As a toy project, I am making a very simple collaborative text editor
using differential synchronization (http://neil.fraser.name/writing/
sync/ if you are curious). I wanted to use comet so I am having the
server push the text of the shared document to all the clients (I saw
in Simply Lift that Lift does some cool stuff to only send the changes
to the client). When I open up around 6 editors, however, lift freezes
but there is no stack trace in the output. I am sending a lot of ajax
requests (2/sec per client editor), but changing the frequency of
these updates did not affect how many documents I could open. Further,
when I turned off the comet, I was able to open many, many documents
sending 2 req/s and lift did not have a problem handling them. Since I
am brand new to lift, I figure I am doing something wrong in my comet
code, so I have included it below:

// EditorServer.scala
import name.fraser.neil.plaintext._
import diff_match_patch._
import java.util.List
import java.util.LinkedList
import net.liftweb._
import http._
import actor._

object EditorServer extends LiftActor with ListenerManager {
private var text: String = ""
private val dmp = new diff_match_patch()
private val lock = new Object();

def createUpdate = text

override def lowPriority = {
case s: String => {
val patches: List[Patch] = dmp.patch_fromText(s)
lock.synchronized {
text =
dmp.patch_apply(patches.asInstanceOf[LinkedList[Patch]], text)
(0).asInstanceOf[String]
}
updateListeners()
}
}
}

// Editor.scala
package code.comet

import net.liftweb._
import http._
import util._
import Helpers._
import js.JE
import net.liftweb.http.js.JsCmds
import scala.xml.Text
import net.liftweb.http.js.JE.JsRaw
import net.liftweb.http.JsContext
import net.liftweb.common.Empty
import net.liftweb.http.js.JsCmd

class Editor extends CometActor with CometListener {
private var shadow: String = ""

def registerWith = EditorServer

override def lowPriority = {
case v: String =>
shadow = v
reRender()
}

def render = "#serverText *" #> shadow
}

// end code

Essentially, client diffs are sent as ajax requests (in a separate
snippet) and are passed to the EditorServer where they are applied to
the text. The EditorServer then sends the document to all the Editors
who update the value of the document in the client by changing the
child of the textarea with id "serverText". I apologize in advance if
I am doing something blatantly wrong.

Also, I have attached my sbt.bat for good measure:
set SCRIPT_DIR=%~dp0
java -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=512m -Xmx712M -
Xss2M -jar "%SCRIPT_DIR%\sbt-launcher.jar" %*

Using Jetty 6, Lift 2.4, 64bit jvm
Thanks a lot!

David Pollak

unread,
Jun 17, 2011, 4:04:45 PM6/17/11
to lif...@googlegroups.com
Sounds to me like a connection starvation issue... basically more connections to the server than are allowed by the client.

Please post a full runnable example (see Posting example code | Lift Space | Assembla ) so we can test it out.

Also, try using a different browser (e.g., Chrome vs. Firefox vs. Safari).

Finally, doing a synchronization within an Actor is super ultra mega bad.  If you're using Actor-based concurrency, you should never lock or block in the Actor handler.


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

Antonio Salazar Cardozo

unread,
Jun 17, 2011, 4:05:23 PM6/17/11
to lif...@googlegroups.com
I'm not directly sure what's causing the behavior you're talking about, but drop the synchronized block around the patch_apply -- EditorServer is an Actor, meaning you'll only ever be processing one message a time. It's designed precisely so you can avoid locks in use cases like this :)
Thanks,
Antonio

Dan G.

unread,
Jun 17, 2011, 5:27:35 PM6/17/11
to Lift
Thank you both for the quick responses. The locking issue should teach
me to understand the tools I use before I start hacking...but it
probably won't :)

Anyway, I got rid of the synchronization block and tested in Chrome
and IE 9. I had only tested in Chrome before and it still doesn't work
there. I can open about 6 editor tabs before it freezes. However, it
DOES seem to work in IE 9. I opened about 50 tabs in IE 9 each with
the editor and it never froze. I will test in Firefox if I get a
chance to install it later, and I didn't test it with having editors
open in multiple browsers.

I uploaded the project to GitHub: https://github.com/danrg/diffsync_lift

David Pollak

unread,
Jun 17, 2011, 6:21:22 PM6/17/11
to lif...@googlegroups.com
On Fri, Jun 17, 2011 at 2:27 PM, Dan G. <dan...@gmail.com> wrote:
Thank you both for the quick responses. The locking issue should teach
me to understand the tools I use before I start hacking...but it
probably won't :)

Anyway, I got rid of the synchronization block and tested in Chrome
and IE 9. I had only tested in Chrome before and it still doesn't work
there. I can open about 6 editor tabs before it freezes. However, it
DOES seem to work in IE 9. I opened about 50 tabs in IE 9 each with
the editor and it never froze. I will test in Firefox if I get a
chance to install it later, and I didn't test it with having editors
open in multiple browsers.

It's a connection starvation issue.  Basically, all of the Ajax connections are trying to hit the server at the same time and they are competing with the Comet long poll connection for the limited client-server connections.  Lift does a reasonable job of avoiding connection starvation by reducing the number of comet long polls if the aggregate comet long poll count is N -1 and an Ajax request comes in (where N is the maximum number of connections the browser is allowed to make to the server.)  Unfortunately, with the long polling plus 12 Ajax requests per second (all from different tabs so they are not serialized by the browser as they are in each tab), the Ajax connections and Comet connections saturated the available connection pool for the browser (this is all browser related so if you have 6 different browsers all pointing at the app, the app work work the way you expect it to).

Anyway, I fixed your app by removing the Ajax polling (see https://github.com/dpp/diffsync_lift/commit/5d2b3ae24da810d28f6eb1721bf43a02ebdc90e4 ) and I opened a ticket for Chrome... I'm going to bump the default number of long poll connections for chrome down by 1 so that this issue would generally be avoided, see https://www.assembla.com/spaces/liftweb/tickets/1049-connection-starvation-on-chrome
 

I uploaded the project to GitHub: https://github.com/danrg/diffsync_lift
--
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.

Dan G.

unread,
Jun 20, 2011, 8:57:19 PM6/20/11
to Lift
Thanks for the fix and very helpful response.

On Jun 17, 3:21 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> On Fri, Jun 17, 2011 at 2:27 PM, Dan G. <dan...@gmail.com> wrote:
> > Thank you both for the quick responses. The locking issue should teach
> > me to understand the tools I use before I start hacking...but it
> > probably won't :)
>
> > Anyway, I got rid of the synchronization block and tested in Chrome
> > and IE 9. I had only tested in Chrome before and it still doesn't work
> > there. I can open about 6 editor tabs before it freezes. However, it
> > DOES seem to work in IE 9. I opened about 50 tabs in IE 9 each with
> > the editor and it never froze. I will test in Firefox if I get a
> > chance to install it later, and I didn't test it with having editors
> > open in multiple browsers.
>
> It's a connection starvation issue.  Basically, all of the Ajax connections
> are trying to hit the server at the same time and they are competing with
> the Comet long poll connection for the limited client-server connections.
> Lift does a reasonable job of avoiding connection starvation by reducing the
> number of comet long polls if the aggregate comet long poll count is N -1
> and an Ajax request comes in (where N is the maximum number of connections
> the browser is allowed to make to the server.)  Unfortunately, with the long
> polling plus 12 Ajax requests per second (all from different tabs so they
> are not serialized by the browser as they are in each tab), the Ajax
> connections and Comet connections saturated the available connection pool
> for the browser (this is all browser related so if you have 6 different
> browsers all pointing at the app, the app work work the way you expect it
> to).
>
> Anyway, I fixed your app by removing the Ajax polling (seehttps://github.com/dpp/diffsync_lift/commit/5d2b3ae24da810d28f6eb1721...)
> and I opened a ticket for Chrome... I'm going to bump the default
> number
> of long poll connections for chrome down by 1 so that this issue would
> generally be avoided, seehttps://www.assembla.com/spaces/liftweb/tickets/1049-connection-starv...
>
>
>
> > I uploaded the project to GitHub:https://github.com/danrg/diffsync_lift
>
> > --
> > 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 frameworkhttp://liftweb.net
> Simply Lifthttp://simply.liftweb.net
Reply all
Reply to author
Forward
0 new messages