SiteSetting class need redesign

This forum is only for questions or discussions about working with the mojoPortal source code in Visual Studio, obtaining the source code from the repository, developing custom features, etc. If your question is not along these lines this is not the right forum. Please try to post your question in the appropriate forum.

Please do not post questions about design, CSS, or skinning here. Use the Help With Skins Forum for those questions.

This forum is for discussing mojoPortal development

This forum is only for questions or discussions about working with the mojoPortal source code in Visual Studio, obtaining the source code from the repository, developing custom features, etc. If your question is not along these lines this is not the right forum. Please try to post your question in the appropriate forum.

You can monitor commits to the repository from this page. We also recommend developers to subscribe to email notifications in the developer forum as occasionally important things are announced.

Before posting questions here you might want to review the developer documentation.

Do not post questions about design, CSS, or skinning here. Use the Help With Skins Forum for those questions.
This thread is closed to new posts. You must sign in to post in the forums.
8/25/2006 10:49:10 AM
kwa
Gravatar
Total Posts 23

Re: SiteSetting class need redesign

I just discovered the error in the new page I created was because of SiteSetting is null. That is because SiteSetting was initialized in the Global.asax.cs then stored in the context. What the method SiteSetting.GetCurrent() does is nothing more than retrieve object from the context. My suggestion is let the method GetCurrent() responsible for the existence of the object, it take care everything including caching and storing object in context. This is where the caching implimentation takes place.

In the global page, instead of creating SiteSetting object, just call the GetCurrent().

 

8/25/2006 1:34:38 PM
Gravatar
Total Posts 18439

Re: SiteSetting class need redesign

Hi Bo,

I think you should subscribe to the svn commit notification so you can know about changes committed to svn
http://forge.novell.com/mailman/listinfo/mojoportal-svncommits

I've made some changes recently in relation to SiteSettings in svn /branches/joesandbox2

However I still think SiteSettings should be created in Application_BeginRequest and added the http context this way it is available to all users controls throughout the request duration.

I added the SiteSettings.GetCurrent a while back becuase I found myself writing the same code in many places
if(Context.Items["SiteSettings"] != null)
{
    siteSettings = (SiteSettings) Context.Items["SiteSettings"];
}

It doesn't seem unreasonable to me that SiteSettings.GetCurrent can return null
If it returns null in your page your should try and find out what happened and why it did not get added to the context at the beginning of the request.

I think we should first look into implementing page caching before we look at caching SiteSettings
8/25/2006 2:25:17 PM
Gravatar
Total Posts 18439

Re: SiteSetting class need redesign

Well, after thinking about it more. It might be better to not add SiteSettings to the context until the first time its needed.

I think in order to to that I will need to move SiteUtils from the Web project into the business project because we need some of the methods from there to get the host name and other things and we don't want a reference from the business project to the web project. I've been thinking this might be a good idea to move SiteUtils out of the web anyway so that external proejcts can also access SiteUtils and its not just avilable in the web project.

And probably I will remove SiteSettings.GetCurrent and create SiteUtils.GetCurrentSiteSettings because it would be better if we don't have dependencies on System.Web in a lot of business classes and only have it in SiteUtils. This way the business classes don't need to know about httpcontext and other System.Web things
8/25/2006 10:33:06 PM
kwa
Gravatar
Total Posts 23

Re: SiteSetting class need redesign

In the method GetCurrent()

if(HttpContext.Current.Items["SiteSettings"] != null)

{

         return (SiteSettings)HttpContext.Current.Items["SiteSetting"];

}

If HttpContext.Current.Items["SiteSettings"] is null , then do nothing. ----> that is why SiteSetting was null. The GetCurrent() method does not guarantee an instance of the object.

If you want the SiteSetting available to all  controls, I came up with a new way. SiteSetting should not be created based on pageId and pageIndex. The new scenario is to pull all data and store it in the cache. Create some methods to get setting for each page. Whenever the method GetCurrent() get called it try to get sitesetting from  the cache. If no cache, get it from database, store it in the cache and finally return instance of SiteSetting. This way, it is guaranteed that object is never null.

public static GetCurrent()

{

          SiteSetting setting = (SiteSettings)Context.Cache["SiteSettings"];

          //check if it is null which mean no cache

          if(setting == null)

          {

               //repopulate settings

               setting = new SiteSetting();

               //cache it

               Context.Cache.Insert("SiteSettings",setting,null, DateTime.Now.AddMinutes(60),System.Web.Caching.Cache.NoSlingExpiration);

           }

         return setting;

}

Modifying GetCurrent() required a new way of creating new instance of SiteSetting too. SiteSetting should be able to get the Hostname and fetch all data related to this Hostname.

Some additional methods are also needed such as method to get active page, method to get setting for a specific page ID etc.

That is why I'm thinking I need a serious modication to SiteSettings class. If you want, after I am done, I can send you a copy of what I do so you can see it.

 

 

(Bytheway, I also came up with a new and better way of caching that guarantee performance and flexibility, admin and user will see difference page cached, I will talk with you about this when I get the SiteSettings fixed).

 

8/26/2006 4:01:14 AM
Gravatar
Total Posts 18439

Re: SiteSetting class need redesign

Hi Bo,

I'll be glad to take a look when you are finished.
But, again I would urge you to track svn because I made a lot of changes last night.
SiteUtils is now in the business project
SiteSettings.CetGurrent no longer exists
SiteUtils.GetCurrentSiteSettings checks if siteSettings is in the context and if so returns it, otherwise it creates it adds it to the context and returns it

I am more interested in a page caching solution first before considering caching of siteSettings

Page caching must take into account:
the query string
the user roles
the user browser language
maybe other things I haven't thought of that testing will reveal
and must not cause excessive memory use, when load testing we must vary these things enough to see what will happen to memory in real environments
8/26/2006 3:46:13 PM
Gravatar
Total Posts 18439

Re: SiteSetting class need redesign

I have to admit you've made me think about SiteSettings and I see some good opportunities for refactoring. The changes to use the 2.0 .NET SiteMap make the current implementation less than ideal where it was a pretty good design for 1.1 .NET

Since the SiteMap is already cached it is unneccesary to retrieve the PageSettings collection on every request so I'm thinking (and this may be similar to what you are thinking), that the SiteSettings can be decoupled to some extent from the PageSettings collection (menu) and both SiteSettings and the PageSettings collection can be cached and only the  ActivePage needs to be maintained in the HttpContext and probably won't need to be cached if we implement page caching

Thanks for making me review this implementation. I will make these changes and probably commit to svn tomorrow.

Cheers,

Joe
8/26/2006 7:47:46 PM
kwa
Gravatar
Total Posts 23

Re: SiteSetting class need redesign

Yeah, now we are talking. ActivePage does not need to be cached or stored in the Context. SiteSettings encapsulates everything. SiteSettins works like a database in memory.  Whenever the activepage is needed, just pull it from the collections. That will be faster. That is what in my to-do list. Because it is not a fast jobs, I did not get to do it; instead I just made a quick change to GetCurrent() and added another helper class. Here is what I did:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace mojoPortal.Business
{
    public class SiteSettingsHelper
    {
        #region Added by Bo on 8/26/2006
        public static SiteSettings GetCurrent()
        {
            HttpContext context = HttpContext.Current;
            //try to get it from cache
            SiteSettings setting = (SiteSettings)context.Cache[GetCacheKey];
            //no cache ?
            if (setting == null)
            {
                //create new object
                setting = new SiteSettings(Hostname, PageIndex, PageID);
                //insert into cache
                context.Cache.Insert(GetCacheKey, setting, null, DateTime.Now.AddMinutes(3), System.Web.Caching.Cache.NoSlidingExpiration);
            }
            return setting;
        }
        private static string Hostname
        {
            get
            {
                HttpContext context = HttpContext.Current;
                return context.Request.ServerVariables["SERVER_NAME"].ToLower();
            }
        }
        private static int PageID
        {
            get
            {
                HttpContext context = HttpContext.Current;
                int pageid = 0;
                if (context.Request.QueryString["pageid"] != null && context.Request.QueryString["pageid"] != "")
                {
                    pageid = Convert.ToInt32(context.Request.QueryString["pageid"]);
                }
                return pageid;
            }
        }
        private static int PageIndex
        {
            get
            {
                HttpContext context = HttpContext.Current;
                int pageindex = 0;
                if (context.Request.QueryString["pageindex"] != null && context.Request.QueryString["pageindex"] != "")
                {
                    pageindex = Convert.ToInt32(context.Request.QueryString["pageindex"]);
                }
                return pageindex;
            }
        }
        private static string GetCacheKey
        {
            get
            {
                string key = "SiteSettings";
                if (PageID > 0)
                {
                    key = Hostname + "-SiteSettings-" + PageID;
                }
                else
                {
                    //PageID = 0 or no pageid
                    key = Hostname + "-SiteSettings-default";
                }
                return key;
            }
        }
        #endregion
    }
}

 

In the SiteSettings class I modified the GetCurrent() method to:

 

public static SiteSettings GetCurrent()

{

             return SiteSettingsHelper.GetCurrent();

}

 

Well, the goal of SiteSettingsHelper is to cache different versions of SiteSettings based on PageID and Hostname. And to not modify the structure of the portal.

This is not what I really planned to do and it is not the best way, but it is the fast and easy way to make things work and also a quick implementation of caching. Plus, I save alot of time.

 

8/27/2006 3:22:01 AM
Gravatar
Total Posts 18439

Re: SiteSetting class need redesign

I think it will be better to de-couple PageSettingsCollection and ActivePage from SiteSettings we don't need an in memory database and we don't need a different copy of siteSettings per page.

If I de-couple them then we only need 1 copy of siteSettings per site in the cache
We don't need to do anything about caching the PageSettings collection for the menu because the SiteMap is already cached,  so no need for this chunk of data to live inside siteSettings at all just need to make SiteMapProvider get its data directly from the PageSettings class with a new method and once it has it SiteMap is cached and it won't call this method again until the cache expires

Getting the Active page is not just a matter of finding it in the PageSettings collection using pageindex, currently inside siteSettings we are loading all the modules for the active page but not for the other pages and I don't want to change to load the modules for all pages. My plan is to move this outside of siteSettings so that we only need one instance of siteSettings in the cache per site. I will add a new method SiteUtils.GetCurrentPage which will check the context for the CurrentPage object and if its not there create it and add it to the context then return it. CurrentPage will be equivalent to what siteSettings.ActivePage was and will have the modules collection for the current page.

PageIndex is a legacy thing that is only used by the skmMenu that we were using before 2.0 .NET. Having pageindex=somenumber in the query string was only so skmMenu whould know how to highlight the current page in the menu
PageID in the query string does indicate the active page id

We could cache a PageSettings instance per page (aka Current/Active page) but I think that caching the page itself will be better and make this un-needed so for now I'm going to continue putting CurrentPage/ActivePage in the httpcontext

I will commit these changes later today.
8/27/2006 4:48:43 AM
Gravatar
Total Posts 148

Re: SiteSetting class need redesign

We could cache a PageSettings instance per page (aka Current/Active page) but I think that caching the page itself will be better and make this un-needed so for now I'm going to continue putting CurrentPage/ActivePage in the httpcontext

If PageSettings contains everything needed to render the page, I recommend measuring the difference between caching the PageSettings and caching the rendered page.  Caching the PageSettings would be a lot less effort than caching the rendered page because you wouldn't have to deal with the security and language issues.  Also since caching the PageSettings would require less memory it might actually turn out to provide better performance due to locality of reference (e.g. the info needed to render the home page might fit entirely in the processor's cache, but the different versions of the rendered page might not).

Bottom line: "Measure twice, cut once."

--Dean

P.S. The one place where caching the rendered page would definitely provide at least some benefit is if you let the browser cache the page.  That would obviously reduce network bandwidth.  I'd recommend considering that as a separate optimization, which could be considered in addition to either caching the PageSettings or caching the rendered page.



8/27/2006 4:57:30 AM
Gravatar
Total Posts 18439

Re: SiteSetting class need redesign

Good point Dean.

I'll definitely measure the performance gain from caching pageSettings per page after I get siteSettings cached and decoupled and before doing anything with page output caching

PageSettings contains a list of modules for the page. Rendering still requires loading those modules which in turn call the db for their content so my guess is page output cache would offer much better performance though as you note it will be more difficult to implement

Good idea on trying to let the browser cache as well. If I do that is there a way to make the browser reload the page when the content changes or if the user logs in and has edit permission?
8/27/2006 5:45:22 AM
Gravatar
Total Posts 148

Re: SiteSetting class need redesign

I agree that output caching will probably provide better performance if caching PageSettings doesn't eliminate all the DB calls.  An intermediate option would be caching in the business object layer so that you wouldn't need to load the modules and content from the DB each time.  That would allow you to avoid the security issues but you'd still need to implement logic to remove objects from the cache when they are updated.  You'll need that logic to do output caching anyway so it wouldn't be wasted effort.

Proposed plan:

  1. Measure current performance.
  2. Measure performance with "dumb" output caching (i.e. neglecting security and cache invalidation issues).  This would give you an upper-bound on the effect of output caching.
  3. Measure performance of "dumb" business object caching (i.e. neglecting cache invalidation issues).  This would give you an upper-bound on the effect of business object caching.
  4. Measure performance of both types of caching using simplest possible cache invalidation logic (ie. any change to anything on the site invalidates the entire cache.).
  5. If it is worth the extra effort, implement more sophisticated invalidation logic and/or handle the security issues associated with output caching.
  6. If it is worth the extra effort, implement browser caching.  If you handled the output caching security issues in step 5, this is a no-brainer.  If not, you need to decide whether the reduced network bandwidth and server load is worth the effort to do that.

is there a way to make the browser reload the page when the content changes or if the user logs in and has edit permission?

I believe so.  I think there is a property you can set to force the browser to always revalidate it's cache entry with a HEAD request.  So, you'd just need to implement Global.GetVaryByCustomString() to generate a different strings for users with different permissions and for changed content.

--Dean
You must sign in to post in the forums. This thread is closed to new posts.