Friday, November 11, 2011 11:07:42 PM UTC #

Every post I write should come with this standard disclaimer. If I ever re-do my blog, I’ll link to this standard disclaimer from the top of every blog post.

This Stuff Just Doesn’t Matter

This stuff, this software development stuff in and of itself, just doesn’t matter. It isn’t the end goal. There are bigger things in life.

All things being equal, it is better to be a competent software developer than an incompetent software developer. This is why I write posts about how to invest my limited time.

All things being equal, it is better to be learned rather than ignorant about software development practices. This is why from time to time I feel the urge to linkblog posts I find on twitter that I believe my blog audience (i.e. you) haven’t seen, and may benefit from. My linkblog posts are gold, I tell you, gold.

All things being equal, it is better to be intentional about your career path and career goals, especially when it comes to dealing with Microsoft’s endless framework lahar. I see a lot of time wasted on studying for exams, and attention given to half-baked frameworks that subsequently under deliver. And I don’t know why, but I have the urge to fix this problem. For those of you who could not care less about helping others make wiser choices with their learning investments, sorry, but it’s who I am, and it bothers me enough to blog about the topic…frequently.

All things being equal, it is better to go to work and experience less unnecessary pain. This is where a lot of my “written for the search engine” and “suriving TFS” posts come from, and where I hope most people find value. I write many of my blog posts with the singular goal of reducing pain. Pain isn’t the ultimate evil. (There’s a great discussion about pain in A Canticle For Leibowitz, which by the way is the first post-apocalyptic book, but I’m too lazy to find the exact quote. PS—dork alert)

All things being equal, it’s more productive for me to blog here than to sit on the couch on a Saturday and take a nap while watching college football. Though there’s nothing wrong with any combination of naps and college football. It’s also better for me to blog than to play video games; or browse the gaming subreddits; or watch someone on twitch.tv live streaming while they play video games; or best of all watch someone on twitch.tv live streaming while they browse the gaming subreddits, which frees you from the chore of browsing the internet yourself. You should probably visit that hyperlink, because it’s just perfect. It’s like watching Inception if Inception featured laziness as its major theme. It just makes sense. Go watch Inception, and go click that link.

I’m not a super expert genius ninja samurai ZeroCool hacker

If it appears that I’m presenting myself as an authority on any topic, make sure I back it up with personal experience. If I don’t have the personal experience to back up my claims, take my argument for what it is: an unsupported opinion. I know that I’m not an expert, and when writing blog posts my self-image doesn’t change—but maybe here on the internet, where they don’t know you’re not a dog, you don’t read my posts the way I intend for you to.

I’m not an expert, but if it so happens I am, I’ll tell you why.

This is a good rule in general. Given blog posts aren’t built off of months of investigative journalism or academic research, the best blog posts are harvested from personal experience (as opposed to blog posts written by pundits with no experience). And let me draw one more point from this: a lot of .NET experts aren’t experts either on the subjects they write about. They are no more an expert, no more experienced, no more capable and have no better software development experience than you or me. They’re just people like you or me with better communication skills. With that said, some of them are true experts. The difference between a good blog post and a great blog post is, in my opinion, the great blog posts are harvested from years of painful experience. Compare this great blog post to my post on the same subject, but clearly written from a newbie’s perspective for an example of this in action.

One additional point I’d like to make is that I feel like I’ve crested the hill and I get it now. Software development is a known problem for me. I’m comfortable with the things I know, and I’m comfortable not knowing the things I’m fuzzy about and still working on (see: estimation; finding out what the customer wants), and I’m comfortable with the fact that I may never learn Haskell, or SmallTalk, or BizTalk, or Joomla. This greater sense of perspective wasn’t always how I was, and I get the idea that most of the working world is full of people who don’t get it yet. So yet another of my part-time crusades is to get everyone up to speed, at least to the point where they get it. I’ve met people who (without some help) will remain forever behind, forever…for lack of a better word: incompetent. And I don’t see my “getting-it-ness” as unique expertise but simply what all software developers should have. I look around and I don’t see that…getting-it-ness. Find me a better word. I can’t write more explanatory text right now without repeating myself.

I will make every effort not to blog work arguments, or be passive-aggressive in general

My theory is most blog posts spring forth from blog arguments or work frustrations, as I feel this urge to blog work arguments from time to time. If I won’t say it in person, I shouldn’t say it on the blog. And even if I say it in person, some work arguments should be kept in the family.

Every now and then I step out of bounds.

And finally, this stuff just doesn’t matter

Software development is not important in the grand scheme of things. Being a bad software developer in and of itself does not make you morally inferior. To pick on something specifically: software craftsmanship is not a new morality, whereby you are righteous (professional?) if you write clean code and unrighteous (unprofessional?) if you don’t. Depending on the bigger picture, and I place emphasis on the phrase bigger picture, you may be doing serious harm by e.g. overdosing radiation therapy patients via your software, or more likely, putting your company out of business because of your incompetence—but in and of itself, being a bad (worse than average?) software developer isn’t evil.

This stuff just doesn’t matter.

Every post I write, no matter how passionate I may sound, no matter if in truth I get carried away and lose perspective and start believing it, this stuff just doesn’t matter.

Friday, November 11, 2011 11:07:42 PM UTC  #     |  Comments [1]  |  Trackback
Tuesday, October 27, 2009 3:59:44 AM UTC #

This is a two-parter. The first part is to say, hey, look at this sweet hack I've discovered in the Oxite source*! The second part is to ask, hey, is this a good idea?
* the refactored Oxite source, that is

Background services

First, let's give a little detail here—background services are long-running tasks that Oxite needs to run periodically. These are things like sending emails and sending trackbacks—necessary, certainly. But, they shouldn't be running while some chump stares at his Netscape window waiting for the site to finish sending 1000 spam trackbacks. He should be able to post to his blog, receive an immediate response indicating the post is now available, and the trackback spamming can commence later. Background services are the things you can put off, the things that don't have to finish before sending a response to your website patrons.

These background services are called by many names—I've heard cron jobs, timer jobs, background jobs, jobs, "the heartbeat," services, and tasks.  In Oxite they're called background services.

Look at this sweet hack!

The full source is below, but I'll attempt a walkthrough of the solution here. First, to explain the problem: we must achieve the impossible—we must somehow emulate a continuously-running Windows service inside an IIS worker process. This means we must periodically trigger jobs to run, but we can't monopolize valuable worker threads. And we certainly can't delay responses to send 30000 spam trackbacks. We've got to run, but we can't run anywhere in the ASP.NET page/request lifecycle! It's a conundrum.

What the Oxite team has done to achieve the impossible is, plainly, to cheat—they use a System.Threading.Timer.

How they manage the impossible is a lot like juggling—magic juggling. Enter stage left: Oxite, the juggler. Oxite takes a background task and throws it in the air. He takes hold of the next background task (let's start calling these things bowling pins) and throws it into the air, and moves on down the line. Before anyone knows what's happened, Oxite has gathered up all the bowling pins, thrown them all into the air, and made his getaway. Unlike most jugglers, Oxite makes no attempt to catch bowling pins once thrown! And this is why it's magic.

Let's try to break this back down into code. When Start() first executes [line 28], the Timer object sets a callback without halting progress [line 43]. This is the juggler throwing a pin in the air.

The callback method is eventually invoked. A thread is spun up* and runs the designated timerCallback() function [line 56]—and, let's make this clear—timerCallback() doesn't block the original Oxite web request; it lives in a new thread. And this new thread does its first dose of work, as shown on line 68 (SPOILER ALERT: it calls Run()).  We're not interested in what Run() does exactly—for today it must remain a spooky mystery, go look it up yourself.
* precisely how the thread is spun up is in fact, real magic, or might as well be to my superstitious caveman brain

Ok. Here's where the "magic" part of magic juggling comes in. Because any dunce can throw bowling pins, and any dunce can catch them, and any dunce, with practice, can juggle. The magic here is inside the timerCallback() method, where the Timer once again sets a callback. Each time a background service awakens, it does its work and, before going back to sleep, sets up the next callback with another call to timer.Change() [line 75]. That is to say, each time the bowling pin makes as if to land, it spins back upward into the air!

So there you have it. Oxite takes a bunch of bowling pins, throws them all into the air, and leaves. As the pins drop down to the ground, the "mystical Timer callback juggling force" propels them back into the air.

And we're running background threads in the web process. Sweet.

Now the question is: is this a good idea?

Now you understand how background tasks work in Oxite—or can now juggle. I get confused sometimes. In any case, congratulations!

Assuming I'm not misrepresenting anything, this is how background tasks work in Oxite. So, now for the question. Is this a reasonably acceptable way to set up background tasks for a site? I've discussed it some on twitter, but is there anything particularly nasty I've missed? Will it kill the process? Will it hang all 25 threads? Or some large portion of them?

I'm curious to hear if anyone has taken this approach, and what their experiences were.

Full source

From http://oxite.codeplex.com/SourceControl/changeset/view/45053#438025:

    1 //  ————————————————

    2 //  Copyright (c) Microsoft Corporation. All rights reserved.

    3 //  This source code is made available under the terms of the Microsoft Public License (Ms-PL)

    4 //  http://www.codeplex.com/oxite/license

    5 //  ————————————————-

    6 using System;

    7 using System.Threading;

    8 using Microsoft.Practices.Unity;

    9 using Oxite.Services;

   10 

   11 namespace Oxite.Infrastructure

   12 {

   13     public class BackgroundServiceExecutor

   14     {

   15         private readonly Timer timer;

   16         private readonly IUnityContainer container;

   17         private readonly Guid pluginID;

   18         private readonly Type type;

   19 

   20         public BackgroundServiceExecutor(IUnityContainer container, Guid pluginID, Type type)

   21         {

   22             this.timer = new Timer(timerCallback);

   23             this.container = container;

   24             this.pluginID = pluginID;

   25             this.type = type;

   26         }

   27 

   28         public void Start()

   29         {

   30             IBackgroundService backgroundService = (IBackgroundService)container.Resolve(type);

   31             IPlugin plugin = getPlugin();

   32             TimeSpan interval = getInterval(plugin);

   33 

   34             if (interval.TotalSeconds > 10)

   35             {

   36 #if DEBUG

   37                 if (plugin.Enabled)

   38                 {

   39                     backgroundService.Run(plugin.Settings);

   40                 }

   41 #endif

   42 

   43                 timer.Change(interval, new TimeSpan(0, 0, 0, 0, -1));

   44             }

   45         }

   46 

   47         public void Stop()

   48         {

   49             lock (timer)

   50             {

   51                 timer.Change(Timeout.Infinite, Timeout.Infinite);

   52                 timer.Dispose();

   53             }

   54         }

   55 

   56         private void timerCallback(object state)

   57         {

   58             lock (timer)

   59             {

   60                 IBackgroundService backgroundService = (IBackgroundService)container.Resolve(type);

   61                 IPlugin plugin = getPlugin();

   62                 TimeSpan interval = getInterval(plugin);

   63 

   64                 if (plugin.Enabled)

   65                 {

   66                     try

   67                     {

   68                         backgroundService.Run(plugin.Settings);

   69                     }

   70                     catch

   71                     {

   72                     }

   73                 }

   74 

   75                 timer.Change(interval, new TimeSpan(0, 0, 0, 0, -1));

   76             }

   77 

   78             //TODO: (erikpo) Once background services have a cancel state and timeout interval, check their state and cancel if appropriate

   79         }

   80 

   81         private IPlugin getPlugin()

   82         {

   83             IPluginService pluginService = container.Resolve<IPluginService>();

   84             IPlugin plugin = pluginService.GetPlugin(pluginID);

   85 

   86             return plugin;

   87         }

   88 

   89         private TimeSpan getInterval(IPlugin plugin)

   90         {

   91             return TimeSpan.FromTicks(long.Parse(plugin.Settings["Interval"]));

   92         }

   93     }

   94 }

Categories: .NET | ASP.NET MVC
Technorati:  | 
Tuesday, October 27, 2009 3:59:44 AM UTC  #     |  Comments [3]  |  Trackback
Tuesday, October 21, 2008 11:58:07 AM UTC #

HOWDY!

This is a quick announcement to let you know that my site now runs on ASP.NET MVC. A few things have been updated:

(strike that) dasBlog 2.1 running on ASP.NET MVC Preview 5

dasBlog 2.2 running on ASP.NET MVC Beta

First thing I should point out is that I'm running under IIS7 Integrated mode on shared hosting. If you're attempting this, be sure you're running on IIS 7 in Integrated mode. If you're trying to test this out on your own machine, this means you must be running Vista or Server 2008, must create a fresh web site in IIS and make sure the app pool is running on Integrated mode. Let me be clear: you can't properly test on the Cassini web server running your Visual Studio project.

ALSO: Now that ASP.NET MVC Beta adds itself to the GAC, for a while (until you host loads the dll's to its server's GAC), you'll have to make local copies of each ASP.NET MVC dll. "Copy Local" a property under each assembly Reference—set it to True for each one of them.

Ok. There are three things you have to do to get dasBlog working underneath an ASP.NET MVC app.

First, in your MVC app, set Routing to ignore your blog's folder. Mine is called "/blog". Here's what it looks like (I think I stole this from a Phil Haack blog post, so if it looks familiar, it is):

code setting routes to ignore blog dir 

Second, and this won't be an option for all of you—I removed all the System.Web.Extensions (AKA ASP.NET AJAX, AKA "Atlas") from my root MVC app. This fixed the problem I was experiencing with my ASMX-powered RSS feed, which Atlas usurps by default (thanks Ben for the tip, that did the trick).

Third, we need to do some heavy work on the dasBlog web.config. First I'll say, thanks to Paulb on the dasBlog team for providing the starter IIS7 web.config. Big ups to changeset 14700.

Instead of attempting to explain in detail any of the nasty things I've done to make the dasBlog 2.2 web.config work under the most recent MVC drop, I'll just post my web.config directly for viewing. I don't recommend what I've done for others; instead I'll say that I got my web.config minimally working underneath a small MVC-based site.

If you're reading this blog post because no one else has provided a better explanation, then maybe perusing my web.config will help.

Without further ado, I present you: web.config of my dasBlog application running underneath an ASP.NET MVC site.

KNOWN ISSUE: http://www.pseale.com/blog (without the trailing slash) bombs out with an error. http://www.pseale.com/blog/ works fine. I assume it has something to do with the blowery.web component, something that I have no desire to fix; I'll work around the problem with a Routing fix/hack. Anyway, lesson learned: BEWARE TRAILING SLASHES!

UPDATE: apparently the blowery.web compression is somehow interfering with delivery of my CSS files. I say apparently because I didn't attempt to troubleshoot this, I just disabled the HttpModule…with extreme prejudice! As much extreme prejudice as one can muster against an HttpModule, anyway.
Categories: ASP.NET MVC | Awesomeness
Technorati:  | 
Tuesday, October 21, 2008 11:58:07 AM UTC  #     |  Comments [0]  |  Trackback
Syndication

Search
Posts on this page
Categories
Sites I visit regularly
About

Powered by: newtelligence dasBlog 2.2.8279.16125

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2013, Peter Seale

Send mail to the author(s) E-mail



Sign In