Friday, May 29, 2009

Serious memory leak with TRUE in .Net

Greetings

At work, we’ve been running PSR tests against our primary application. Last night, the application (which runs as a windows service), was on an obvious spiral into the drain of despair. The memory was steadily increasing. When it reached 1.5 gigs, the application stopped responding altogether.

That’s not good.

We were going to just not say anything and hope for the best, but the more level headed of us thought that maybe we should take a look. I was completely against it and wanted to sacrifice a goat instead, but I was overruled. (I seem to get overruled every time I want to sacrifice animals.)

I started by reviewing the code. I found some connection objects that weren’t being properly cleaned up. I ordered 50 verbal lashes for the offending perps, but moved on. It wasn’t significant enough to be the memory leak, but did sprout an emotional leak in the bowels of my soul. (Please, just use the using clause. That’s all I ask.)

But I digress. Today, we ran ANTS against it to see where all that memory was all going. It didn’t help, which is unusual.

So, we did it the old fashion way. We started commenting things out of the main method to find which one was the problem. The object does:

  1. Receives an xml message
  2. Removes duplicate nodes
  3. Calls a stored procedure to get more information for each of the nodes
  4. Does an Xslt Transform
  5. Publishes the message via WCF

Of all the things there, I was most suspicious of the WCF client and least suspicious of XSLT. Imagine my chagrin when it turned out to be the XSLT method.

// XSLT

XslCompiledTransform transform = new XslCompiledTransform(true);

using (XmlReader xmlReader = new XmlTextReader(xsltFile))

{

transform.Load(xmlReader);

}

 

The problem is the TRUE parameter when the object is instantiated. If you remove it, or change it to false, then everything is stable.

This is a surprisingly large bug. How can something as fundamental as the boolean value TRUE take down an application like that? Shouldn’t someone have tested that the booleans work properly before shipping? The very foundations of computing are based on boolean values; bits are either on or off… yes or no… 1 or 0. This isn’t rocket science! How can Microsoft ship a product that doesn’t fully support the word true?

I haven’t been this bothered about the .Net framework since I learned that the number 0 is also broken. Every time I try to divide any number by it, it breaks with some illogical math error (can’t divide by zero?). I can’t say that I’ve tried to divide every number, but  I did get the vast majority. .. let’s just say that I’ve tested the theory enough times to be convinced that it is a problem.

I really love .NET and will continue to use it without reservation. But, the tough lesson is that you can’t take it for granted, especially if your logic is based on evaluations of some sort. Now that we’ve identified its limitations, we can use it more effectively.

If the problem was elsewhere… let’s say, if the problem were in the XslCompiledTransform object rather than the word true, that would be more understandable. Then we might be able to conclude that “XslCompiledTransform in debug mode leaks more than the Bush administration”. That’s something we could get our head around and come to terms with... But a broken true!? Dissapointing.

Monday, May 25, 2009

SpamArrest - You Got Me

I've been using SpamArrest.com for years now. I've been a big fan of the service, despite its flaws. But, as the years wane on and the flaws continue to persist uncontested, I have become less of a fan. In fact, I officially declare myself no longer a fan.

Here is a big related post from April 2008: http://hamletcode.blogspot.com/2008/04/email-decision.html

I was experiencing email turmoil at the time, and settled on SpamArrest. I would've liked to use Gmail, but could not (at least not the way intended).

Since then, 2 significant things have happened with spam arrest:

1 - I bought the lifetime subscription. At the time, I was still a fan. I've been using it for years and planned to use it for years more.

2 - They introduced a javascript bug. I reported the bug to them on October 27th 2008 after waiting serveral weeks to see if they would correct it themselves.


My Email to them

Greetings

This has been happening for quite a while now, but I waited incase it was going to be fixed, but it hasn't.

There's a bug on the login screen. It attempts to attach an event to the REMEMBER ME checkbox. But, if you're already logged in (because it remembered you), then the control doesn't exist, so it can't attach the event. This results in an error dialog:

A runtime error has occurred.
Do you wish to debug?
Line 1726
Error: 'addEventListener' is null or not an object.
Its not a big deal, but after a few weeks its getting a little annoying. (I'm a developer, so I can't disable the alerts.)

Jay



Rather than trying the steps listed, they instead suggested that my browser must be "acting up", and suggested that I install firefox or chrome. That's great... thanks. Great advice. I responded pointing out exactly where the javascript was failing and why, and showed that if fails in firefox 3 too. I listed 3 bulleted steps to reproduce. They responded saying "thank you, we'll forward that to our development team".

That was nearly 7 months ago. They still haven't fixed it. Its going to fail in every browser because it's just bad logic. When you're already logged in, there isn't a "REMEMBER ME" checkbox. The javascript is looking for it anyway, then fails when it can't find it.

Another of my favorite problems is REPLY ALL. If you click REPLY ALL, and forget to remove your own name from the address list, you get an onslaught of spam messages. Once you delete the messages, you don't get any more, so its not like it auto-approved them. Its just that you get a whole bunch that you have to cleanup before you're back to normal.

In my previous blog post, I mentioned the searches I conducted in their help system to find simple things like IMAP and DISK QUOTA. To find DISK QUOTA information, you have to search for "SPACE". To find IMAP information, you have to read an article entitled "WHAT IS A POP SERVER?" Well, I already know what a pop server is. If I'm looking for IMAP server information, why would I click that?

I strongly regret the life time subscription. I asked for a refund on it, and they responded saying no, but then asked me what the problems were. I didn't answer. How many times do I need to tell them what the problems are? At this point, I continue to use SPAM ARREST only out of laziness, despite the life time subscription. Maybe I can sell it on EBAY or something.

Now, the webmail client has a REFER FRIEND tab on which they ask me to refer business to them for savings. I already have a strongly regretted life time subscription... how does refering a friend to spam arrest help me? Furthermore, even if I was still a fan, then that tab would be useless anyway. I've been referring people to SpamArrest for years, not because I want some type of credit, but because it's a good service. Only a few of those referrals have actually started using it, but that wasn't due to a lack of effort on my part. (Most people find GMAIL junk filters to be sufficient.)

My official stance has changed. I will no longer recommend Spam Arrest. Infact, I will actively express dissapointment. Software is supposed to evolve; their web client continues to be stagnant (and broken) where it counts. The pages still say COPYRIGHT 2006!!! Has anyone looked at a calendar recently?

Its ok to have flaws. Its not ok to never fix them. I'm dissapointed in them for not evolving, and I'm really annoyed that I fell for the "lifetime subscription" scam.

Monday, May 11, 2009

Chewing

In order to chew, you need teeth.

Jack now has one that finally broke the gum line. This means that he can chew very small, very localized foods!

Tuesday, May 5, 2009

Linq, Attribute and Reflection, all in one

 

Greetings

I was goofing around with some reflection stuff, and wanted to use LINQ to build an xml document for me. It started off as three different steps, and whittled its way down to one.

  1. Gets a list of all of the properties in the current class that have the [ServiceProperty] attribute
  2. Creates an XDocument containing information about the property including the value, and all of the meta-data added by the [ServiceProperty] attribute

The only thing I don’t like is that it builds the entire wrapper, even if attribute is null. When the attribute is null, we don’t care about anything, and should just continue. But, as is, property.GetValue() executes even when we don’t need it.

   1: XDocument doc = new XDocument(



   2:     new XDeclaration("1.0", "utf-8", "yes"),



   3:     new XElement("properties",



   4:                  (



   5:                      from property in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)



   6:                      let wrapper = new



   7:                                        {



   8:                                            Property = property,



   9:                                            Attribute =Attribute.GetCustomAttribute(property, typeof(ServicePropertyAttribute)) as ServicePropertyAttribute,



  10:                                            Value = property.GetValue(this, BindingFlags.Public | BindingFlags.Instance, null, null,



  11:                                            CultureInfo.InvariantCulture)



  12:                                        }



  13:                      where wrapper.Attribute != null



  14:                      select



  15:                          new XElement("property",



  16:                                       new XAttribute("name", wrapper.Property.Name),



  17:                                       new XElement("display-name", wrapper.Attribute.DisplayName),



  18:                                       new XElement("default-value", wrapper.Attribute.DefaultValue),



  19:                                       new XElement("description", wrapper.Attribute.Description),



  20:                                       new XElement("required", wrapper.Attribute.Required),



  21:                                       new XElement("value", wrapper.Value),



  22:                                       new XElement("data-type", wrapper.Property.PropertyType.FullName)



  23:                          )



  24:                  )



  25:         )



  26:     );




image