Migrated 2.6 -> 3.2... duplicate renders

108 views
Skip to first unread message

Dan Gravell

unread,
Apr 5, 2018, 12:23:29 PM4/5/18
to Lift
Just moved from 2.6 to 3.2 and also Scala 2.11 to Scala 2.12. Easier than I thought, so thanks for that.

One issue I have is comet re-rendering itself, seemingly without being asked (by me).

The page with a comet section is correctly rendered first time.

The comet long poll starts as normal. The page appears ready at this point. But then there's a reply to the second long poll:

try{jQuery('#'+"F9661741687974DSMQJ_outer").html("<div id=\"F9661741687974DSMQJ\" style=\"display: inline\"><div class=\"album-detail\">\u000a\u0009\u0009\u0009\u000a<title>Abbey-Road - bliss</title>\u000a\u0009\u0009\u00[...]

My entire comet component is re-rendered with no changes. This is obviously not great from the performance view, but also it also breaks something in the UI because I rely on not doing re-renders (naughty naughty maybe) because I apply a JS library to a div when the page is loaded. All comet work is done in partialUpdate to other components.

Note this is not deterministic - sometimes it works and no update like the above is sent, 80% of the time it doesn't.

I tried breakpointing partialUpdate. I see lots of "registerComets" calls being sent to the browser (oftentimes with duplicate function IDs and some other numeric ID) but nothing like the "html" above. Any other places I can look? I noticed "AnswerRender" might return already generated output, but couldn't see where that might be sent from.

Dan

Dan Gravell

unread,
Apr 6, 2018, 11:55:05 AM4/6/18
to Lift
I'm sure this is something I'm doing wrong.

I think I've found the next level of "why". For my actor, it evaluates a Listen message inside BaseCometActor's message handler after the page has already been delivered to the client.

The Listen.when is less than lastRenderTime, and so it performs a rerender, overwriting the previous HTML. If the when is not less than the lastRenderTime, it sends deltas, which I think is what should happen. 

I guess this is to send all the info so far when the source of the Listen started listening before the actor performed its first render?

So my next task is to work out why Listen.when is less than lastRenderTime ...

Dan

Antonio Salazar Cardozo

unread,
Apr 6, 2018, 3:00:11 PM4/6/18
to Lift
Hey Dan,
One big change that happened in 3.0 was that we discovered some issues with how deltas
were computed and sent down in cases where a comet wasn't used to render a chunk of the
page but rather to continually send down push updates. We shifted to allow toggling between
the old and the new behavior, but the old behavior should be the default.

The toggle is overriding partialUpdateStream_? to return true; if you haven't overridden it, it
should be false. This probably isn't the issue for you, but figured it couldn't hurt to mention it.

Regarding your deeper tracking: a Listen message is triggered when the client issues a long-
poll request. The comet then decides whether it has new stuff to send down, or if not it adds the
listener function to notify it of updates if they come in later. Listen.when should be the version
marker that the client sends to the server indicating the last update that it received. This is a
monotonically increasing number that isn't directly tied to a time (unfortunately; this is old code,
which is why the naming is a little fuzzy).

Part of the setup process of a new comet actor should be, on page load, setting the last render
number on the client to the server's version (this happens in the lift.registerComets JS).

Hope that helps you investigate further!
Thanks,
Antonio

Dan Gravell

unread,
Apr 9, 2018, 10:28:14 AM4/9/18
to Lift
Thanks Antonio. Yes I noticed partialUpdateStream_? too, but as you said I'm not overriding it so discounted it.

I remembered this comet actor is instantiated by a snippet which renders the the correct data-lift incantation which is then evaluated and the actor created as a result. Also, an eager_eval=true is passed... for what reason I cannot remember, but it makes me wonder if this control over the ordering might be having an effect.

I looked at my pages, and got the problem to reproduce (as I said, it's not deterministic). On a case it reproduces, we get:

_lastRenderTime is instantiated (I can't check the value of this because if I spend too long in the debugger something returns me to the home page of the app)

Then a performReRender occurs and the value of _lastRenderTime is set to 234617271589

The page is delivered, and the values are as so:

<div data-lift-comet-version="234617271585" id="F234617271584ABKI0Y_outer" [...]

ift.registerComets({"F234617271584ABKI0Y": 234617271585}

Then when the Listen gets triggered, the when is 234617271585

But _lastRenderTime is 234617271589 (this is the value after performReRender), so the identical nodeseq is sent all over again.

_lastRenderTime is only updated when
- BaseCometActor is instantiated
- BaseCometActor is re-rendered
- A Listen message is received which has a when after the _lastRenderTime (and any deltas are sent)

Should performReRender be called as part of initial page load? If so, and assuming the value of the rendering is used, why isn't the updated _lastRenderTime used on the initial page load and message exchange? i.e. I would expect the following to be delivered:

<div data-lift-comet-version="234617271589" id="F234617271584ABKI0Y_outer" [...]

ift.registerComets({"F234617271584ABKI0Y": 234617271589}

Dan

Dan Gravell

unread,
Apr 9, 2018, 10:37:47 AM4/9/18
to Lift
Yes, looks like re-rendering is done as part of PerformSetupComet2 which I guess is normal setup.

So why is the page delivered with the original value for _lastRenderTime, rather than that after performReRender is called...?

Dan Gravell

unread,
Apr 10, 2018, 9:34:42 AM4/10/18
to Lift
I now realise this is occurring consistently across all my pages (with the caveat that for each page it's non-deterministic... so the _possibility_ is there).

In one example which renders thirty-five comet actors for tracking progress (they don't do anything until an associated button is clicked) the whole lot get destroyed then regenerated on the second callback (the first one being the listening setup).

It looks like the Listen message originates from the ContinuationActor - so I was wondering if my use of Jetty 7.6.1 is problematic, which is very old now. Unfortunately upgrading to 9.4.9 has made no difference. I also tried downgrading to Lift 3.1.1 but that made no difference.

I have a build I need to make today, so I'm going to put a hack in there to cope with the duplicate rendering. But we target embedded devices with constrained memory and this is a bit worrying, so if anyone has any further feedback on things to try/debug that would be great.

Dan

Antonio Salazar Cardozo

unread,
Apr 13, 2018, 2:40:56 PM4/13/18
to Lift
Sorry for not getting to circle back here until today but is there any chance you're able
to reproduce this with a small example? That may help narrow things down.
Thanks,
Antonio

Dan Gravell

unread,
Apr 16, 2018, 11:31:36 AM4/16/18
to Lift
Thanks - I'll try to get back to this soon.

Dan Gravell

unread,
Jul 13, 2018, 4:39:00 AM7/13/18
to Lift
I haven't managed to find anything more on this. It just bit me again on another page, so I'm implementing a similar kludge - use the JQuery Initialize plugin to re-initialise HTML once it gets dragged back a second time.

It's odd this seems to happen on every comet page.

Matt Farmer

unread,
Jul 15, 2018, 9:14:58 AM7/15/18
to lif...@googlegroups.com
Sorry to hear you’re still struggling with this. If you get an opportunity to produce an example project that demonstrates the issue I promise one of us will try to look into the matter further. :/
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dan Gravell

unread,
Jul 16, 2018, 4:37:26 AM7/16/18
to Lift
Sure, and to be clear, I didn't mean that to be a nag, just happened to come across it again and the group was already open in a tab...

My thoughts are:

- See what happens if I create a new bare bones comet component
- Try commenting out bits of Boot.scala - who knows what crazy setting might be influencing it
- Try some different Jetty configs

Dan

Matt Farmer

unread,
Jul 29, 2018, 11:10:34 AM7/29/18
to lif...@googlegroups.com
Sorry for the delayed response, but this all sounds good to me. :)

Dan Gravell

unread,
May 24, 2019, 12:15:22 PM5/24/19
to Lift
This continues to trip me up on a monthly or so basis.

I just did some experimenting and noticed it _doesn't_ happen when I ship a HTML file with no <body> tags. Not very useful!

Without body tags, the following script is inserted:

<script type="text/javascript" src="/lift/page/F4609426409321CZLTQ.js"></script>

However, with the body tag, this script is also inserted (as well as the above script):

<script src="/classpath/lift.js" type="text/javascript"></script>

I'm guessing both are required.

BTW, the comet component's output is rendered in both cases.

I think all that is happening is that the F4609426409321CZLTQ.js is failing to call lift.js and so the duplicate render can not occur because the call to registerComets never occurs.

Dan
To unsubscribe from this group and stop receiving emails from it, send an email to lif...@googlegroups.com.

Antonio Salazar Cardozo

unread,
May 24, 2019, 1:50:35 PM5/24/19
to Lift
It occurs to me that a workaround here could be to include a call in your `render` function that will reattach the JS library.

Fwiw, the missing `/classpath/lift.js` will probably also break all other comet functionality---it's what defines the actual JS library Lift uses under the covers. The F...js file is the page-specific JS, which kicks off the comet requests that are used to communicate events, updates, etc.
Thanks,
Antonio

Dan Gravell

unread,
Jun 27, 2019, 7:41:30 AM6/27/19
to Lift
I'm looking at this again. I used the Giter8 approach to create a new project, imported my bare-minimum comet setup in, and exactly the same thing happens - the initial render state is sent in the page load, then a duplicate "html()" call with the same content.

What is the best way to share this?

Dan

Dan Gravell

unread,
Jul 10, 2019, 10:26:08 AM7/10/19
to Lift
FWIW, I noticed some strange behaviour using Wiring inside a Comet actor that suffers from this.

Following the shopping cart example at https://simply.liftweb.net/index-6.3.html if I include a "total" field as so:

WiringUI.asText(total)

... the page infinite loops, continually calling back to the server, and each time rendering:

jQuery(document).ready(function() {});

(batching them into many per call).

So it appears Wiring isn't usable when suffering from this problem.

Just documenting that for the search engine in case anyone else has this.

Dan

Antonio Salazar Cardozo

unread,
Jul 10, 2019, 4:32:46 PM7/10/19
to Lift
Sorry, regarding the best way to share, see https://app.assembla.com/wiki/show/liftweb/Posting_example_code -- basically, a GitHub repo.
Thanks,
Antonio

Dan Gravell

unread,
Jul 11, 2019, 7:13:28 AM7/11/19
to Lift

Matt Farmer

unread,
Jul 12, 2019, 8:31:33 PM7/12/19
to Lift
Hey Dan,

I checked out your PR and I'm not seeing duplicate renders as you described. The only comet request I see is a long poll. Is there any chance this is browser specific? What browser are you using?

Matt Farmer

unread,
Jul 13, 2019, 12:40:04 PM7/13/19
to Lift
Hey all,

Re-reading the original posts and I see that (1) it's not happening every time and (2) it seems to matter that the body tag exists in some of your templates.

That would explain why I haven't seen it. I tend to ship individual pages that aren't full a HTML document in and of themselves. I wonder if surround could have gotten borked at some point.....

Dan Gravell

unread,
Jul 15, 2019, 6:24:23 AM7/15/19
to Lift
Hi Matt. I see this on FF 67.0.1 and Chrome 67.0.3396.99 .
To unsubscribe from this group and stop receiving emails from it, send an email to lif...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages