YAFLogo

tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
1.9.5Final came with the "timeago" module which is a really cool feature but can cause the browser to use 100% CPU (on one thread). This high CPU usage was reported here . I have spent some time tracking this bug down. It seems like after the browser was left idle for a while (1+ hour) and there are many "timeago" placeholder to update (i.e. in an active shoutbox with longer history data where people chat a lot and the chat history is kept about 2x longer than default values that came with YAF install), the browser will use 100% CPU and won't release the CPU unless the user killed the browser session.

To solve the above problem, I though of:

1. Increase update interval of "timeago" jquery from 60 seconds to 5 minutes

2. Keep shorter chat history

3. Stop the shoutbox after idling for X minutes and provide a button "I'm Back Now" for the user to click after coming back.

#1 might or might not fix the issue and make "time ago" not accurate anymore (i.e. showing "2 minutes ago" when could be "5 minutes ago").

#2 might or might not fix the problem and is not very nice for a bigger site with lot of chatters.

#3 wil fix the problem above and seems to be the best solution. This solution also keeps the Server from updating users that are no longer active. This saves bandwidth for both the Server and the Client.

I'm not familar with the shoutbox code. Any idea on how to detect the user idle for, say, 15 minutes?

Sponsor
Underground
13 years ago

3. Stop the shoutbox after idling for X minutes and provide a button "I'm Back Now" for the user to click after coming back.

#3 wil fix the problem above and seems to be the best solution. This solution also keeps the Server from updating users that are no longer active. This saves bandwidth for both the Server and the Client.

Originally Posted by: tommy382 

I agree with this.

People who leave the browser open and do nothing for a couple of minutes, the server (and data bandwidth usage) should be saved and the chat should stop updating. However, this should be notified to the user (The user should have visual feedback that the shoutbox has stopped refreshing).

- Robert


My site containing the YAF forum: Globalcaching.eu 
bbobb
  • bbobb
  • 100% (Exalted)
  • YAF Developer
13 years ago

I agree with this.

People who leave the browser open and do nothing for a couple of minutes, the server (and data bandwidth usage) should be saved and the chat should stop updating. However, this should be notified to the user (The user should have visual feedback that the shoutbox has stopped refreshing).

Originally Posted by: Underground 

It can't be done so, by current design. A complete replacement would help.

tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
Hmm, that is too bad. Maybe v2.0 :)

What does it take to add a new page into YAF? I want to add a page say "ShoutboxArchive" and show longer chat log than what the shoutbox is showing. This is for users who missed the chat and want to see longer history. I know how to retrieve the data from the DB and display but I'm not sure what it takes to add a new page and inherit all the settings (same skin as other pages, etc.) from an existing YaF page (like the default.aspx page).

BTW, after reducing the number of chat lines from 150 down to 50, it tend to "freeze" the browser less but the problem didn't completely go away. It would be great if someone can fix the lockup issue due to the "timeago" updating.

Underground
13 years ago
Look at http://www.globalcaching.eu/ 

It will use the stylesheet of the forum (board) and if signed in, it will use the by the user selected theme.

What you can do is (in the masterpage) check for the correct theme, check the theme folder and add theme.css to the header.

And depending on the selected theme you load your own theme dependend styslesheet.

I will show my code, but just for your information. You might have to need other source, but it will give you an idea.

(LabelStyleSheet is a Literal control in the header)


                    if (!Page.IsPostBack)
                    {
                        //get selected theme
                        string selTheme = "BlueGrey.xml";
                        object o = null;

                        if (UserAccountInfo.IsAuthenticated)
                        {
                            o = dbcon.ExecuteScalar(string.Format("select ThemeFile from yaf_user where UserID={0}", UserAccountInfo.ID));
                            if (o != null && o.GetType() != typeof(DBNull))
                            {
                                selTheme = (string)o;
                            }
                            else
                            {
                                o = null;
                            }
                        }

                        if (o == null)
                        {
                            o = dbcon.ExecuteScalar("select Value from yaf_Registry where BoardID=1 and Name='theme'");
                            if (o != null && o.GetType() != typeof(DBNull))
                            {
                                selTheme = (string)o;
                            }
                        }

                        if (HttpContext.Current.Session["SELECTED_THEME"] == null || HttpContext.Current.Session["SELECTED_THEME_HEADER"] == null || HttpContext.Current.Session["SELECTED_THEME"].ToString().CompareTo(selTheme)!=0)
                        {
                            HttpContext.Current.Session["SELECTED_THEME"] = selTheme;

                            StringBuilder sb = new StringBuilder();

                            XmlDocument xmlDoc = new XmlDocument();
                            xmlDoc.Load(System.IO.Path.Combine(HttpContext.Current.Server.MapPath("/forum/themes/"), selTheme));
                            XmlElement root = xmlDoc.DocumentElement;
                            string forumThemeFolder = root.Attributes["dir"].Value;

                            sb.AppendLine(string.Format("<link rel=\"stylesheet\" href=\"/themes/{0}/style.Css\" type=\"text/Css\" />", forumThemeFolder));
                            sb.AppendLine(string.Format("<!--[if IE 6]><link rel=\"stylesheet\" href=\"/themes/{0}/style.ie6.css\" type=\"text/Css\" media=\"screen\" /><![endif]-->", forumThemeFolder));
                            sb.AppendLine(string.Format("<!--[if IE 7]><link rel=\"stylesheet\" href=\"/themes/{0}/style.ie7.css\" type=\"text/Css\" media=\"screen\" /><![endif]-->", forumThemeFolder));

                            //sb.AppendLine("<link href=\"/forum/resources/css/jquery.yafmodaldialog.css\" rel=\"stylesheet\" type=\"text/css\" />");
                            //sb.AppendLine("<script type=\"text/javascript\" src=\"/forum/resources/js/jquery-1.4.4.min.js\"></script>");
                            sb.AppendLine("<link href=\"/forum/resources/css/forum.css\" rel=\"stylesheet\" type=\"text/css\" />");

                            sb.AppendLine(string.Format("<link href=\"/forum/Themes/{0}/theme.css\" rel=\"stylesheet\" type=\"text/css\" />", forumThemeFolder));

                            XmlNodeList pages = root.SelectNodes("page");
                            if (pages != null)
                            {
                                foreach (XmlNode page in pages)
                                {
                                    if (page.Attributes["name"] != null && page.Attributes["name"].Value == "THEME")
                                    {
                                        XmlNodeList rsrcs = page.SelectNodes("Resource");
                                        if (rsrcs != null)
                                        {
                                            foreach (XmlNode rcs in rsrcs)
                                            {
                                                if (rcs.Attributes["tag"] != null && rcs.Attributes["tag"].Value == "HEADER")
                                                {
                                                    //sb.AppendLine(rcs.InnerText.Replace("~", string.Format("/forum/themes/{0}", forumThemeFolder)));
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                }
                            }
                            HttpContext.Current.Session["SELECTED_THEME_HEADER"] = sb.ToString();
                        }
                        LabelStyleSheet.Text = HttpContext.Current.Session["SELECTED_THEME_HEADER"].ToString();
                    }


My site containing the YAF forum: Globalcaching.eu 
thethanghn
13 years ago
I used jQuery.nap plugin to turn on a flag that tell page to stop polling whenerver user become idle
tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
Can you elaborate on this? How do you turn on that flag?
tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
I spent couple hours today trying to come up with a way to prevent the forum from locking up the browser after several hours of idle. As I dig into it, it turned out this is more than just the TimeAgo module. The root issue here is the Shoutbox have an UpdatePanel that does the update every 3 seconds (I changed to 10 seconds for mine). So ever 3 seconds, there will be a Partial Update to the page. This partial update, however, does post a Postback and it seems the logic in the rest of the forum code doesn't know that. Hence, there are a bunch of processing going on on every Partial Update including starting the "Time Ago" script to update all timed elements in the page.

Every time the "time ago" script is started, it will refresh itself in 60 seconds. So we basically have a nested loop of updates here:

For every 3 seconds, start a new timeago instant. For every timeago instant, refresh every 60 seconds

After a while, the above will create so many "time ago" updates that it would freeze the browser and cause 100% cpu usage.

The following is the code that kick off the timeago update. I added a check that if the call is part of a partial update, don't kick off another timeago udpate. Note that the Vars "RegisteredTimeago" is always undefined per partial update. Hence the check "!this.PageContext.Vars.ContainsKey("RegisteredTimeago"" didn't work.

private void PageContext_PageLoad([NotNull] object sender, [NotNull] EventArgs e)
    {
      //Tommy: don't process on partial update (i.e. shoutbox).
      System.Web.UI.ScriptManager sm = 
        System.Web.UI.ScriptManager.GetCurrent(PageContext.CurrentForumPage.Page);
      if (sm != null && sm.IsInAsyncPostBack)
        return;

      if (PageContext.BoardSettings.ShowRelativeTime &&
          !this.PageContext.Vars.ContainsKey("RegisteredTimeago"))
      {
        YafContext.Current.PageElements.RegisterJsResourceInclude("timeagojs", "js/jquery.timeago.js");
        YafContext.Current.PageElements.RegisterJsBlockStartup("timeagoloadjs", JavaScriptBlocks.TimeagoLoadJs);
        this.PageContext.Vars["RegisteredTimeago"] = true;
      }
    }

I think the above check should be place even higher in the processing stack (i.e. at private void Forum_Load(object sender, EventArgs e)) to prevent further unneeded processing on every shoutbox partial update. I placed the check above kinda deep down on the call stack (works for my purpose of preventing the browser from freezing up) and so I still see a lot of unneeded processing like "generating page tile" still being ran on every partial update. It would be good if someone more familiar w/ the forum code place that check as high up in the Postback processing stack as possible.

tha_watcha
  • tha_watcha
  • 100% (Exalted)
  • YAF.NET Project Lead 🤴 YAF Version: 4.0.0 rc 2
13 years ago

Increase update interval of "timeago" jquery from 60 seconds to 5 minutes

I added a Setting for that, it is now possible to set ist to any time you want (In Milliseconds)

tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
I have successfully implemented #3 without using any jquery plugin. Due to the way UpdatePanel works, I doubt that it would fix the high cpu usage issue by stopping the "time ago" update via AJAX (i.e. as long as the UpdatePanel is still there, the PostBacks will be executed).

I have tried many approaches to solve this high cpu usage problem and none really solved the problem completely. I ended up with #3 as the solution and it did solved the problem. My solution is not ideal but it is a must for me since many users are complaining the forum locks up overnight and caused their PC to consume more power.

I hope someone more familiar with the code can come up with a real solution in the next release. The info below will help with coming up with the real solution to this problem.

1. The shoutbox UpdatePanel will cause a PostBack every 3 seconds. This is a full-brown PostBack that will cause Forum_Load method to run. Avoid doing expensive work in this method by detecting that it's a async-postback and perform minimal amount of work needed to update the shoutbox panel. Detection code:

      System.Web.UI.ScriptManager sm =
        System.Web.UI.ScriptManager.GetCurrent(this.Page);
      if (sm != null && sm.IsInAsyncPostBack)
      {
        //This is a post back caused by UpdatePanel.
      }
2. During shoutbox UpdatePanel postback, don't need to start another "timeago" javascript instant.

    private void PageContext_PageLoad([NotNull] object sender, [NotNull] EventArgs e)
    {
      //Tommy: don't process on partial update (i.e. shoutbox).
      System.Web.UI.ScriptManager sm = 
        System.Web.UI.ScriptManager.GetCurrent(PageContext.CurrentForumPage.Page);
      if (sm != null && sm.IsInAsyncPostBack)
        return;
       
      if (PageContext.BoardSettings.ShowRelativeTime &&
          !this.PageContext.Vars.ContainsKey("RegisteredTimeago"))
      {
        YafContext.Current.PageElements.RegisterJsResourceInclude("timeagojs", "js/jquery.timeago.js");
        YafContext.Current.PageElements.RegisterJsBlockStartup("timeagoloadjs", JavaScriptBlocks.TimeagoLoadJs);
        this.PageContext.Vars["RegisteredTimeago"] = true;
      }
    }

So why is my current solution not ideal? Well, I basically make the Shoutbox "disappear" after 15 minute of non-chat activity (i.e. not sending any message to the shoutbox). Using a jquery plugin to detect idle is better. Also, instead of making the Shoutbox totally disappear, it might be better to leave it but put up a message that said "Click here when you are back to see the shoutbox again". This requires some jquery/javascript coding to do.

Here are the screenshots showing my solution working. Sorry I'm reusing the images I made for other purposes to save time so please ignore the bubbles.

Shoutbox in Operational State (visible):

UserPostedImage

Shoutbox after 15 minutes of non-chat activity (completely gone from view):

UserPostedImage

tha_watcha
  • tha_watcha
  • 100% (Exalted)
  • YAF.NET Project Lead 🤴 YAF Version: 4.0.0 rc 2
13 years ago

I hope someone more familiar with the code can come up with a real solution in the next release

Jaben already made a solution which will be included in 1.9.5.5. Shoutbox will be only automatically updated when there is a update.

tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
Oh, that is nice! Is the shoutbox still poling for update every 3 seconds or the server is somehow able to push down the update when there is one (client-pull vs server-push approach)? I will try it when it's ready.
Underground
13 years ago

Oh, that is nice! Is the shoutbox still poling for update every 3 seconds or the server is somehow able to push down the update when there is one (client-pull vs server-push approach)? I will try it when it's ready.

Originally Posted by: tommy382 

I doubt it. Still uses Ajax and the client needs to check.

The change is, (as I asume) is that when nothing is added, no new messages, shoutbox panel is not updated.

So, polling is still done.


My site containing the YAF forum: Globalcaching.eu 
Jaben
  • Jaben
  • 100% (Exalted)
  • YAF Developer
13 years ago

Oh, that is nice! Is the shoutbox still poling for update every 3 seconds or the server is somehow able to push down the update when there is one (client-pull vs server-push approach)? I will try it when it's ready.

Originally Posted by: tommy382 

10 bytes of data is polled every 2-3 seconds to see if the chat has new messages. If it does, it refreshed the UpdatePanel.

tommy382
  • tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
13 years ago
Awesome! I'll keep an eye out for v1.9.5.5 non-beta. Any known date for this yet?

I like that solution better than my current hack. I modified the shoutbox to be in 2 modes: Chat and Chat+VoiceChat. In the 2nd mode, 15 minutes non-chat is too little since the singer might be busy singing and not chatting text or she might be chatting via the voice (still treated as "idle") so I had to make it 30 minutes. On weak machines, 30 minutes would lock up the forum already 🙂