ASP.NET: Singletons



  • Lately, I have been throwing Singletons around like there is no tomorrow (hey why use all that memory?? Just make a singleton!). But I've come to see the downside of it. 2 Users could be using the same object. For some situations thats OK, but most of the times it is not.

    Now, I've had the idea of building a little chatroom app, just for fun, nothing serious. I was thinking of a singleton that could hold all the Chatroom objects in a Hashtable or something. Users can then select a chatroom, and get the object reference of the chatroom from the singleton. If 2 users join the same room, they would get the same chatroom reference, thus they would share the data of the chatroom (e.g Messages).

     It would mean that the IIS application pool has to be configured so that it would not recycle.

    In a real situation ofcourse, checks have to be builtin so that the memory won't get out of proportion.

     Plz, WTF-rate my idea. How would YOU do it?



  • @Ice^^Heat said:

     Plz, WTF-rate my idea. How would YOU do it?
     

    Why reinvent the wheel?



  • It is a programming experiment. Why learn algorithms when most of them are in a framework? Like I said, just for fun to try something out to see if it works.

    Back on topic.

    The list of chatrooms can be persisted to a database, and when nobody is in a chatroom, the object can be disposed. When someone joins an empty chatroom, the an object can be created which other users can then join.

    Does recycling remove everything in-memory? Or just unused stuff?

     



  •  Recycling removes everything.  The ASP.NET processes are shutdown and restarted.  I believe the only way to persist the data during recycling would be via a database or flat file; something external to the ASP process itself.



  • @Ice^^Heat said:

    Plz, WTF-rate my idea. How would YOU do it?

    On a scale of 1 to 10, 10 being the most WTF-y, I'd rate this as at least a 9, if only because of the remark of disabling app pool recycling. That's resource starvation waiting to happen. Beyond that, using true singletons is fairly stupid when asp.NET has object stores available that are shared over all incoming requests: the Items collection in HttpApplication and the Cache collection. It's possible to argue in favor of abstracting those collections with generic singleton implementations though; e.g. CacheSingleton<T> and HttpAppSingleton<T>.

    As for how I would do it:
    Rather than retain Chatroom and Message objects in memory I'd retain a collection of active user, chatroom and message data in a database, with the proper relations (m:1 - message : chatroom, m:1 - message : user, m:n user - chatroom) and additional indices on the proper columns. (Those being user name, room name and message timestamp, probably).

    I'd use ajax to pull/push messages, instead of using full postbacks. That way I can limit the amount of messages requested with each poll to only those the client browser hasn't downloaded yet. The ajax requests would be directed at a simple handler (.ashx) that could decompose the XML fragment, query the database for the relevant information and construct a response fragment. To optimize data throughput I'd use asp.net's cache object to keep track of the last 5 minutes or so worth of posted messages and look for messages there first, before resorting to hitting the database. Much the same for the users joining/leaving a room.



  • I don't see using a database as preferable, unless you are trying to do some kind of logging.

    But lets rewrite our (hypothetical) scenario:

    You are building a social website in the likes of Facebook. Users can have friends and they can see if their friends are online. If they are, a user can click on "Chat", then a chatbox will open where they can chat with eachother, when they leave, the box closes.

    Now, we do this by instancing a chatroom object, and passing the reference to the other user/session by using the CacheSingleton. When the users leave, the reference is set to Null and the chatroom object is disposed by the garbage collector.

    No need for a database, just In memory objects. Doesn't seem so bad to me. Or is it just so that Objects are not meant to be shared among requests?

    Worker process timeout can be set, and recycling can be enabled, as long that it doesn't interfere with existing chatrooms (which I don't know).



  • @Ice^^Heat said:

    You are building a social website in the likes of Facebook. Users can have friends and they can see if their friends are online. If they are, a user can click on "Chat", then a chatbox will open where they can chat with eachother, when they leave, the box closes.

    Now, we do this by instancing a chatroom object, and passing the reference to the other user/session by using the CacheSingleton. When the users leave, the reference is set to Null and the chatroom object is disposed by the garbage collector.

    No need for a database, just In memory objects. Doesn't seem so bad to me. Or is it just so that Objects are not meant to be shared among requests?

    Worker process timeout can be set, and recycling can be enabled, as long that it doesn't interfere with existing chatrooms (which I don't know).

    It likely will interfere, as all data inside the Cache and HttpApplication collections is volatile. It will be erased when the application domain is recycled. Data inside the Session collections will be erased as well, if it uses the default InProc (='in process') session state.

    This is why you want a database combined with a layer of in-application caching in the Cache collection. That way data will be persisted and your application's state will be preserved across a recycling operation. Again: for reasons mentioned in my previous post the disabling of application recycling should never be an option.

    If you want to implement this kind of direct messaging then the best method would probably be to retain a queue of 'received messages' for each person's account. Messages can then be popped from the queue when an account is polled for newly received messages and can be pushed onto the queue when some other person sends a message, ready to be sent back during the next polling event.

    The active contents of said queue can forcibly be persisted to the database in the Application_End event and can be reloaded during the Application_Start event to retain state across recycling of the application domain. In addition the working copies of the queues should be handled through the application cache, so that unused queues do not linger in memory but are written out to the database upon cache expiration and are read in from the database when a cache lookup fails.

    The messages could be queued along with some form of sender ID so that when a person receives his/her queued messages they can be grouped into chat boxes by sender. Such a set up would allow the sending of messages when a user is not online. In addition real time chatting can be allowed, given a suitable delay between polling events (say; 5 seconds or so) for a message view to keep itself updated.

    I'll hazard a guess and say you are stuck in thinking of chatrooms as an explicit object class. In the case of peer-to-peer chatting as in this proposed scenario, the chatrooms are only implicitly present as some form of 'relation' between sender-receiver. There is no reason to build them into the actual program logic.



  • @Ragnax said:

    I'd use ajax to pull/push messages, instead of using full postbacks.
     

    Developing a chatapp in html, or any variant thereof, is a giant WTF. For the love of god even a java applet is better, and that's saying alot.

    With regards to singletons, it is the most insuffereably overused pattern, since it's easy to understand by people starting out with patterns.

    Look into the factory pattern. Basically you want a class ChatRoomFactory to provide you with a ChatRoom, upon request. The factory is responsible for knowing if the ChatRoom already exists or not, so you don't have to. 



  • @chebrock said:

    Developing a chatapp in html, or any variant thereof, is a giant WTF. For the love of god even a java applet is better, and that's saying alot.

    Really?  How else do you propose to make a chat application that doesn't require the installation of additional software?  Were you one of those people who got their panties in a twist when the GUI came out because command line interfaces were "good enough"? 



  •  The purpose of singleton is usually not to save memory.  If it makes sense for something to be a singleton (because there should only be one of those things) then do that.  Otherwise, you'll be making 100 hacks to get around the problem that you singletonized something that shouldn't be a singleton.



  • @Ice^^Heat said:

    Lately, I have been throwing Singletons around like there is no tomorrow (hey why use all that memory?? Just make a singleton!). But I've come to see the downside of it. 2 Users could be using the same object. For some situations thats OK, but most of the times it is not.

    When you store data in a static variable, the data is available to all users on the entire AppDomain. If you need a single instance of some object per user, put it in the Session instead.

    If you like the convenience of using a singleton without actually making a piece of data globally accessible to all users, you can write something like this:

    public class MyClass
    {
        private MyClass() { }

        public MyClass Instance
        {
            get
            {
                string name = "MyClassInstance";

                if (HttpContext.Current.Session[name] == null)
                    HttpContext.Current.Session[name] = new MyClass();

                return (MyClass)HttpContext.Current.Session[name];
            }
        }
    }

    MyClass.Instance reads from the session now, so won't be forced to share the same class instance across the entire application context.

    Now, I've had the idea of building a little chatroom app, just for fun, nothing serious. I was thinking of a singleton that could hold all the Chatroom objects in a Hashtable or something. Users can then select a chatroom, and get the object reference of the chatroom from the singleton. If 2 users join the same room, they would get the same chatroom reference, thus they would share the data of the chatroom (e.g Messages).

     It would mean that the IIS application pool has to be configured so that it would not recycle.

    In a real situation ofcourse, checks have to be builtin so that the memory won't get out of proportion.

     Plz, WTF-rate my idea. How would YOU do it?

    Realistically, you should probably not try to reinvent the wheel.

    But since its just for fun, your suggestion isn't too terrible, although keeping your implementation thread-safe and low-memory can be a challenge. If you can keep your object model coherent, I'd say go ahead and try to write a little chat system like this.



  • Nod -- I think he needs a single object globally visible to all users, to store the messages and all that.  I think the question comes down to: keep it all in-memory, or persist it somewhere (i.e., a file or database).

    In a (very late) response to some other comments, I see no issues at all with doing a chat room using AJAX, that would work just fine and be very easy to do.  yahoo mail and gmail do this very nicely, and it certainly would be quite easy with ajax.net.

    The other pro of storing messages in-memory is: you don't need to store much.  Just the last x minutes of messages..  It's not a message board, just a chat app, right?




  • @Ice^^Heat said:

    Lately, I have been throwing Singletons around like there is no tomorrow (hey why use all that memory?? Just make a singleton!).

     

    Ouch. Singleton is the object oriented version of a global variable, and usually exactly as bad. There are very few problems for which Singleton is the correct solution.



  • @brazzy said:

    Ouch. Singleton is the object oriented version of a global variable, and usually exactly as bad. There are very few problems for which Singleton is the correct solution.

    Except it is much easier to add debuging and lazy instantiation to a singleton method.  Global variables and singletons are fine, the problem is with using them carelessly.  Seriously, how would you handle a globally-needed resource?



  • @morbiuswilters said:

    @brazzy said:

    Ouch. Singleton is the object oriented version of a global variable, and usually exactly as bad. There are very few problems for which Singleton is the correct solution.

    Except it is much easier to add debuging and lazy instantiation to a singleton method.  Global variables and singletons are fine, the problem is with using them carelessly.  Seriously, how would you handle a globally-needed resource?

     

    The problem is that many developers don't really consider whether a resource truely needs to be globally accessible (IMO very few), or could just as well be passed as a parameter or added to an object that is. Instead of taking the time to think it through and do it the right way (which may require some refactoring), they'll just create the 137th Singleton, until every other line contains at least one getInstance() call.

     "throwing Singletons around like there is no tomorrow", as the OP put it, sounds exactly like that case.



  •  @brazzy said:

    The problem is that many developers don't really consider whether a resource truely needs to be globally accessible (IMO very few), or could just as well be passed as a parameter or added to an object that is. Instead of taking the time to think it through and do it the right way (which may require some refactoring), they'll just create the 137th Singleton, until every other line contains at least one getInstance() call.

    What if you have 137 databases to connect to?



  • @Ice^^Heat said:

    But I've come to see the downside of it. 2 Users could be using the same object.
     

    Not to mention you're tying the objects that use the singleton into a context and increasing coupling. 



  • If you have that many resources, maybe you could pass some sort of aggregating data structure to your worker objects, a "context variable"? The problem with global state is, IMHO, that you never can predict further collaborations. Take the famous example of different conflicting log4j.properties. Building stuff which relies on global things also assumes one and only one instance, which can become a big PITA.



  • In the end it is all about separating logic from execution strategy. The same reasoning can be applied to the utilization of thread-local variables, system properies and alikes. I always strive to create a structure where the implementation is as agnostic as possible of its dependencies and their location, within reason of course. Inversion of control helps you realize this and really makes your code a lot cleaner and versatile.


Log in to reply