Welcome to CrankyGoblin.Com Sign in | Join | Help

Public Class GeoffAppleby

Inherits Microsoft.VisualBasic.MVP : Implements IBrainFart
How many ways are there to skin a cat? (Part 1)

A question came up on the aus-dotnet mailing list last night that got my juices going. William Bartholomew was looking at getting a call to a web method to try again a few times if it had trouble making the call. All he (innocently) asked for was if his general way of doing it was considered 'ok'. Here's his sample code:

    Dim lastException As Exception
    For retry As Integer = 1 To MAX_RETRY_COUNT
      Dim ws As TheWebService
      Try
        ws = New TheWebService
        'Use the web-service
        lastException = Nothing


      Catch ex As Exception
        lastException = ex

      Finally
        ws.Dispose()

      End Try
    Next

    If Not lastException Is Nothing Then
      'We got through all of the retries without succeeding
    End If

There's two problems with his request. He was bitten (mostly) with the first type: people gave a bit of advice on specifics to do with the code he showed, but I don't think any of it was really answering his primary question. I think (and I'm putting words in his mouth, for all I know) he was after a good mechanism to use to attempt retries on a failed web method call. I was guilty of this myself in responding, but I took  a more esoteric view of it at the same.

Esoteric. What a nice way to describe things. Sounds good right? What really happened is that at first I looked commenting on general things to do to the sample to make it more reliable, more robust, and more likely to achieve his outcome, but without really modifying the way he was trying to get it to happen.

And then the geek in me started to shine. In my defense, I was a caffiene rush last night, which generally is when I start typing and just don't stop, my brain churning away on a few sidetracks all at once, and I just start to drivel. I ended up going on a little bit about different loop mechanisms, ways to control them, and what would be considered 'nice'.

To put it bluntly, I crossed to the intellectual side. It's that place where getting the code working isn't important, it's getting that code...right. Justifiably right. The most elegant, the most clear, the damn fastest you can get it. To the point where you'll never need to modify ever again (for a while). Day to day, having all my code 'right' is certianly my aim, but I take it witha dash of realism - there just isn't enough time to churn out the needed stuff and make it right. But I refuse to stop short of at least half way to two thirds. That's the spot where you have it stable, and pretty nice. It's a hard line to walk, actually, and a skill that takes a long time to develop - something I think very few of us have perfected.

But in the dark hours of the night (or my lunch break, or a smoke break, or...well, any time I'm not actually trying to get things done) I always enjoy falling back to the intellectual side.

Tonight I'm not on a coffee high anymore. But I'm back there again.

Let's look at William's code again. First up, well done him on using VB! (I really had to say it).

Next, general optimisations. The first thing that was brought up was the recreation of the web service client proxy on every interation. William justified it by saying that since it had a Dispose method, he really thought he should be disposing it. And this is true to some extent, but not really what the Disposable pattern is all about.

The Disposable pattern gives the programmer early access to free up some resources within the object that might otherwise take a while to be garbage collected. It doesn't garantee that those resources themselves will get garbage collected immediately, but those things inside it might become available for collection earlier than if you were to just set the object to Nothing and forget about it. But the point is that it should only be called once you're ready to destroy it anyway - while there's likely some object around that you can call the Dispose method on multiple times, generally speaking once you Dispose of an object, the only thing it's good for is the trash heap.

I also have a sneaking suspicion that the other point of the Dispose method was that it was supposed to only free up unmanaged or expensive resources - this is a gut feel that I think I heard somewhere round the traps that I haven't actually confirmed (I'm sure someone who knows will leave a comment letting me know, so I choose the lazy option :)

However, just because an object has a Dispose method, doesn't mean you can't reuse it.

So there's a little optimisation. Move the declaration and instantiation of the web service client proxy to outside of the loop, and we've saved multiple hits on the constructor. Aside: I had a look in reflector at what the dispose method on a client proxy does, and it ends up that it's only there because it inherits (a few steps back in the chain) from Component, and the disposing of it makes sure that it's removed from it's container - which it will only be if you dropped it on a form in the first place. Calling new on it means it's not held in a container :)

Next up there's wasted loop cycles if everything worked. This one I called William on, and he confirmed that he forgot to include a pretty important line in the sample code. Having successfully called the web method, you really want to bail out of your loop immediately with an Exit For statement. By doing this, we don't call the web method again even though it worked.

Another important one is putting a delay in before a retry attempt is made. If the service is temporarily down, hitting it 5 times in 5 seconds is not likely to get you the result you wanted. But waiting a while (say, 15 seconds? who knows? It depends on what sort of problem you're targetting) gives the server to finish that reboot that accidentally got scheduled.

Delaying between calls like this needs to carefully investigated before you do it, however. If you're in a winforms application and doing this on the main GUI thread, do you really want to lock up your form for a minute or two? If you're in a web site, your browsers is likely to get a HTTP timeout.

And what sort of exception occurred? There's a few reasons why exceptions get raised when calling a web service, but not all of them are something that warrants a retry. If the data you passed to the web method was invalid, passing it a second time won't help. If the web service or method you're calling doesn't exist, calling again is futile. If credentials were needed and you got them wrong, will passing them again get you any further? Nope.

So that's some things that could be done to make this peice of code do what it really needs to do.

The more interesting part (to my inner geek) is not what it should do. It's how. We've looked at some simple optimisations that might make it a little less intensive on the machine, and can re-jigger it into enough of a shape to make it do the right sort of thing. And if this was code I was writing for my work that I didn't have any more time to work on, I'd happily leave it at that. (OK, almost happily. There's still one or two little tweaks and nudges that I wouldn't be able to help but do, but it's a good enough shape to end on for this post).

But there's a little more in there that I'd like to poke a stick back into to make it feel a little nicer. And I'll talk about it in Part 2. I'll also be talking more about that cat I mentioned in the title. The beauty of programming in any language is that there's almost always several different ways a task can be performed. Finding the best way is the fun part! Sometimes there's a right way, sometimes there's a wrong way, sometimes the difference between them is blurred, and sometimes there's just no right way to do it, and you're left with personal preference. I can't give any advice on how to find the right or best way, but I think it's a fascinating subject to discuss.

Until Part 2...

Listening to: i don't wanna stop - good charlotte - (2:40)
Posted: Tuesday, 12 July 2005 7:40 AM by Geoff Appleby
Filed under: , ,

Comments

Joshua Flanagan said:

Regarding your sneaking suspicion (and invitation for a confirmation)...
The way I understand it:
- You should only use a "finalizer" when you have unmanaged resources.
- If you use a finalizer, you should always implement IDisposable (so that you can deterministically free the resources and suppress the finalization).

That does not mean you should only use IDisposable when you have unmanaged resources.
You should use IDisposable any time you want to run some sort of deterministic cleanup.
# July 12, 2005 1:19 PM

Public Class GeoffAppleby said:

Last night I started what ended up being a pretty long discussion on a pretty small piece of code. I...
# July 13, 2005 7:25 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

To submit your comment, click on these pictures:
  • Hairy Geoff
  • Teenage Mutant Ninja Geoffy!
  • Shocked Geoff
Gaptcha Image - No Peeking! Gaptcha Image - No Peeking! Gaptcha Image - No Peeking!
Gaptcha Image - No Peeking! Gaptcha Image - No Peeking! Gaptcha Image - No Peeking!
Gaptcha Image - No Peeking! Gaptcha Image - No Peeking! Gaptcha Image - No Peeking!
Can't recognise the people in these pictures? Look here for a quick introduction.
There's a time limit for you to get your comment submitted before this set of pictures expires. If you think it's been longer than 10 minutes, get some new pictures first (you won't lose what you've typed so far).
Get some new pictures 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS