SitePoint Sponsor

User Tag List

Results 1 to 21 of 21
  1. #1
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Session-Related DB Maintenance

    I store some visitor information in my database and it is indexed by the SessionID of the visitor. I regularly clean up this data in the Session_End event of the global.asax file, but I want to write a routine that will allow me to manually delete any artifacts that may exist (as a result of server reset, local testing, etc).

    So, what I've done is poll the database for visitor-specific records and from this I get a collection of SessionIDs that are theoretically still in play. What I need to do is spin through all current sessions and determine which of the DB SessionIDs no longer have a match in the application SessionIDs and subsequently delete those DB records.

    1)
    Is there any way ask the Session object if a particular SessionID exists? Is this a logical approach?

    2)
    I'd like to do this from a class in the app_code directory (where I don't seem to have access to the Session object). Would it make more sense to do this processing at the UI-tier?
    Last edited by pinch; May 12, 2008 at 10:46.

  2. #2
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,576
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    1) Hmm, good question. Never really tackled that one. I would start with the documentation on the SessionState class and go from there.

    2) That is because you are not asking the right thing. It is avaliable, just not pushed into your face like it is on a page. It can be grabbed from HttpContext.Current or, better yet, passed in as a parameter.

    The other realistic option is to hit this from a different angle and setup the cleanup to work on a time-based basis, where objects of a certain age are cleaned rather attempting to attach things to live sessions.

    Finally, you might want to do this in the Profile rather than the session. That is much more suitable for being a durable store for data, and it also works for anonomous users.

  3. #3
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wwb_99 View Post
    1) Hmm, good question. Never really tackled that one. I would start with the documentation on the SessionState class and go from there.
    I've run into a dead-end with the SessionState as it appears only the current Session is available to the user. What do you think of this approach in the global.acas file:

    Code ASP:
       private ArrayList activeSessionIds = null;
     
        void Session_Start(object sender, EventArgs e) 
        {
          // Code that runs when a new session is started
          activeSessionIds.Add(Session.SessionID);
        }
     
            void Session_End(object sender, EventArgs e) 
        {
          // remove the current session from the list of active sessions
          for(int i=0;i<activeSessionIds.Count;i++)  {
            if (activeSessionIds[i] == Session.SessionID)
            {
              activeSessionIds.RemoveAt(i);
              break;
            }          
          }
          // delete any DB records that the expired visitor has created
          DeleteExpiredVisitorRecords(Session.SessionID);
          // delete any DB records that no longer have a matching session in the application
          CleanupDBArtifactsMethod(activeSessionIds);
        }

    Ideally the variable activeSessionIds would be available from other places in the application so that I could initiate 'CleanupArtifactsMethod' from a page where I could monitor the number of artifacts (instead of at the end of each session, as is outlined above). Does this seem like a viable approach?

    I am anticipating lots of visitor cheat sheets which is why I'd rather use the database to store the data rather than in memory. Also, storing the visitor data in the database (and mostly in the same tables as the user cheat sheets) means I can treat them identically to user cheat sheets in code (I have lots of code which updates this DB data and it doesn't care of the sheet belongs to a user or visitor). Most importantly, converting a visitor cheat sheet to a user cheat sheet becomes trivial as I just remove a reference from a visitor DB table and add a reference to my user DB table (something we discussed quite while back).

  4. #4
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,576
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    That is going to get really ugly with lots of concurrent sessions.

    You really want to use the profile stuff, this sort of thing is exactly what it was meant for. Unlike sessions, storage is database-based. You could get the same effects, such as the "allow one to save stuff" advantage, without fighting with the session which was never meant to really be more than a user-specific singleton sans application control.

  5. #5
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wwb_99 View Post
    That is going to get really ugly with lots of concurrent sessions.

    You really want to use the profile stuff, this sort of thing is exactly what it was meant for. Unlike sessions, storage is database-based. You could get the same effects, such as the "allow one to save stuff" advantage, without fighting with the session which was never meant to really be more than a user-specific singleton sans application control.
    Hi wwb_99!

    Let me try to clarify a few points and you can tell me what you think.

    First, I should point out that I'm not storing hardly ANYTHING in the actual Session object itself. I store maybe 5 or 6 boolean values in each user's Session object, that's it. That main reason that I utilize the Session is for its SessionID, which I use to reference the visitor's cheat sheets in the DB. I also use Profile variables, but only for simple pieces of data.

    The problem I'm envisioning with using a Profile-based approach is that the cheat sheet itself is NOT a simple piece of data. I've attached a simplified diagram of the cheat sheet schema; you can see at the top how I differentiate between user and visitor sheets. I've also attached an image of the application interface where players are being reordered so that you get an idea of what I'm working with (each sheet has roughly 35-50 players but the user can add as many as they wish, up to 200 theoretically).

    While creating a visitor cheat sheet is not necessarily a common action, updating them will be EXTREMELY common (that is the whole purpose of the application, re-ordering players and tagging players). Using my custom DB approach means updating both visitor and user cheat sheet is extremely simple: update a single bit column in a single row of the 'CheatSheetItems' table. It's extremely fast and easy.

    If I use a Profile variable to store the visitor's cheat sheet, I'm thinking I will have to store a serialized cheat sheet object in the database. While this may not be a big deal when creating a sheet, I think it will become a big problem when hundreds or thousands of visitors are updating their sheets every couple of seconds. If I'm correct, even to update a single bit (say, designating a player as a 'sleeper') would require the application to de-serialize that user's cheat sheet, make the change to a single bit, then re-serialize the entire object and place it back in the DB.

    It seems much more simple and fast to issue a single UPDATE on a single record in a DB table?

    ....as I finish writing this its dawning on me that you may have meant to utilize the anonymous Profile variable to store the Session ID, not the entire Cheat Sheet object?
    Attached Images Attached Images

  6. #6
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,576
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    You are quite correct in not wanting to store a serialized cheat sheet in the DB.And you could use the profile to store the SessionID, But you could just skip the session stuff and store the cheat sheet ID. Or use a customized provider to actually store the cheat sheet natively, probably using your own business objects.

  7. #7
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    But you could just skip the session stuff and store the cheat sheet ID.

    So by using Profile variables instead of Session variables I can be sure that I won't lose track of visitors when the server restarts; this is because the visitor information is stored in the Membership database and not in a Session (memory which will be lost on reset).

    So, the question becomes: How do I clean up my DB information for inactive visitors whose information is stored in anonymous Profile variables?

    Previously I had an automatic cleanup mechanism because I could just use the Session_End event to delete any visitor sheets for a particular visitor when their session expired. With Profile variables it appears they use a persistent cookie to keep track of visitors and the timeout for these cookies is configurable. I then have to ask:

    1. How does the application inform me if it identifies a visitor whose cookie has expired? I would think that the only way this scenario would surface is if the user has an expired cookie in their browser and they revisit the application. I'm assuming that application will match that cookie to its Profile GUID, determine that the visitor's time has expired, and remove their Profile data from the database. But how do I slip into the process so that I can cleanup my DB information for that visitor? I'm assuming this no longer has anything to do with the Session so Session_End is out of the question.

    2. What happens if the visitor simply never returns to the application? Is there some built-in mechanism whereby the application will know that the Profile data for a particular anonymous user is stale?
    Last edited by pinch; May 13, 2008 at 09:47.

  8. #8
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Suggestion: You have your users in the database. Drop the Visitors table - you don't need it.

    You extend the CheatSheets table with a cleanup code. At regular intervals you 1) set all cleanup codes to indicate that they are candidates for cleanup. 2) remove the code for all users in the CheatSheetUsers table and for all CheatSheets which has been updated within the last 24H. 3) deletes all CheatSheets which still have the cleanup code set. Effectively an algorithm like garbage collection (although not generational).

    You can keep the CheatSheetId for "visitors" in the Session or in Profile. If you set Session to use a State Server even the Ids stored in the session will also survive a restart of the application (but not a reboot of the server which holds the state).

  9. #9
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You extend the CheatSheets table with a cleanup code. At regular intervals you 1) set all cleanup codes to indicate that they are candidates for cleanup. 2) remove the code for all users in the CheatSheetUsers table and for all CheatSheets which has been updated within the last 24H. 3) deletes all CheatSheets which still have the cleanup code set. Effectively an algorithm like garbage collection (although not generational).
    Technically couldn't I do this without adding anything to the CheatSheets table since it already has a LastUpdated column? I could just query the DB for all CheatSheets that have no match in the CheatSheetUsers table, then delete all of those records whose LastUpdated timestamp is older than 24 hours.

    I think the whole idea behind moving to Profile variables as opposed to Session variables (wwb_99 can weigh in on this) was that I'd always (theoretically) have a reference to each visitor who has created a cheat sheet and therefore would not have to worry about doing any garbage collection. But I'm still not sure if the Profile-based solution is fool-proof-enough to disregard garbage collection altogether (I'm still not sure how the application informs me when an anonymous Profile variable 'expires').

    If I still have to perform garbage-collection in a Profile-based system, then I think it defeats the purpose of using Profiles in the first place no? I mean if I'm doing garbage-collection to begin with, doesn't having things stored in the Profile mean I just have another place to delete things from?

    For instance, let's say I stored the visitors' CheatSheetIDs in Profile variables and also used some sort of garbage collection in the background. If I deleted old CheatSheets using garbage collection, wouldn't I also have to make sure that the Profile variables which match those cheat sheets are also removed so that I don't end up looking for cheat sheets that don't exist? That seems a bit counter-intuitive as the purpose of Profile variables was to avoid garbage collection in the first place?

    It seems to me that garbage collection would work well with a Session-based solution (since garbage collection makes the problem of artifact, visitor-based cheat sheets irrelevant), but only if everything cannot be handled automatically by utilizing cleanup code that executes when an anonymous Profile variable expires.

  10. #10
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    At regular intervals
    How would you suggest I perform something at regular intervals? I assume you're talking about in code and not in the database?

  11. #11
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If you are on your own server you can simply schedula a job for it. Maybe implement a special administrative URL (password protected) when requested kicks of the cleanup in a worker thread.

    On a shared host I would create a small http module (IHttpModule) which when loaded initializes a variable holding the "next" cleanup datetime. On each request it compares current datetime with this "next cleanup" datetime and when overdue kick off a worker thread to clean up old goo.

  12. #12
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    On a shared host I would create a small http module (IHttpModule) which when loaded initializes a variable holding the "next" cleanup datetime. On each request it compares current datetime with this "next cleanup" datetime and when overdue kick off a worker thread to clean up old goo.
    Yeah it'll be in shared hosting for sure so I'll have to research this. I imagine I could just have a 'Cleanup' button but it would be nice to have it done automatically for me.

  13. #13
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Some shared hosters allow you to schedule jobs as well. That might be another option.

  14. #14
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I was told by my hosting provider that I have a "Scheduled Task Tool" which can schedule an HTTP call to any page on my site at a given interval (they don't allow us to create server jobs)

    Should I just create a special page and from its Page_Init method call a stored procedure which would do my clean-up? Would that be sufficient?

  15. #15
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by pinch View Post
    I was told by my hosting provider that I have a "Scheduled Task Tool" ... Should I just create a special page and from its Page_Init method call a stored procedure which would do my clean-up? Would that be sufficient?
    The "Scheduled Task Tool" sounds perfect for the job. I would implement a simple handler (.ashx file) and spin off a workitem in order to avoid a timeout on the request:

    Code Csharp:
    <%@ WebHandler Language="C#" Class="Cleanup" %>
     
    using System;
    using System.Web;
     
    public class Cleanup : IHttpHandler {
     
        public void ProcessRequest (HttpContext context) {
            System.Web.Util.WorkItem.Post(PerformCleanup);
        }
     
        public bool IsReusable { get {return false; } }
     
        private void PerformCleanup()
        {
            // perform cleanup.
        }
     
    }

  16. #16
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I don't see that as one of my 'Add New Items..' options so I guess I just create it myself? Should I place this along with my other .aspx pages and just use it to call business objects in the PerformCleanup() method as if it was a page?

  17. #17
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It is called a "generic handler" in the add items dialog. An .ashx handler can be called like any page. Yes, you should just code the cleanup in the PerformCleanup() method. You may want to log the somewhere, as a workitem like this probably runs *after* the request has ended. It cannot write to the response as it is gone at that time. You will probably want to place this handler in a directory with restricted access. You may require that the request comes from the server itself. No harm will come from calling it as such, but if it is a longer running task it could be used to DoS your site.

  18. #18
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by honeymonster View Post
    It is called a "generic handler" in the add items dialog. An .ashx handler can be called like any page. Yes, you should just code the cleanup in the PerformCleanup() method. You may want to log the somewhere, as a workitem like this probably runs *after* the request has ended. It cannot write to the response as it is gone at that time. You will probably want to place this handler in a directory with restricted access. You may require that the request comes from the server itself. No harm will come from calling it as such, but if it is a longer running task it could be used to DoS your site.
    I'm not really sure how to do what you're suggesting (restricting directory access to the server). Current I just have the generic handler in the application root with other pages, and on my local machine it works property. But when I upload it to the server and navigate to that page I get an error:

    XML Parsing Error: no element found
    Location: http://www.mysite.com/Cleanup.ashx
    Line Number 1, Column 1:
    The code itself is simple:

    Code ASP:
    <%@ WebHandler Language="C#" Class="CheatSheetCleanup" %>
    using System;
    using System.Web;
    using BP.CheatSheetWarRoom.BLL.Sheets;
     
    public class CheatSheetCleanup : IHttpHandler {
     
        public void ProcessRequest (HttpContext context) {
            System.Web.Util.WorkItem.Post(PerformCleanup);
        }
     
        public bool IsReusable {
            get {
                return false;
            }
        }
     
        private void PerformCleanup()
        {
          CheatSheet.DeleteOldVisitorCheatSheets();
        }
    }

    Any ideas?

  19. #19
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It seems that somehow the browser have come to expect an xml document from the handler. The handler doesn't return anything.

    You could just write dummy single-element xml document to confirm the process has started.

  20. #20
    SitePoint Guru pinch's Avatar
    Join Date
    Mar 2005
    Posts
    686
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by honeymonster View Post
    It seems that somehow the browser have come to expect an xml document from the handler. The handler doesn't return anything.

    You could just write dummy single-element xml document to confirm the process has started.
    Would that essentially be the same thing as just putting the method call in the Page_Load event of a Web Form?

  21. #21
    SitePoint Wizard
    Join Date
    Feb 2007
    Posts
    1,274
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by pinch View Post
    Would that essentially be the same thing as just putting the method call in the Page_Load event of a Web Form?
    Yes, I suppose so.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •