I had a race condition eat my week. Stick around, I’ll tell you what that’s about.
Here’s some IRC, with some comments.
10:29 < mhoye> Oh, man. I think I've finally fixed this bug.
10:30 < mjschranz> mhoye: What was the bug?
10:30 < mhoye> Multi-user, multi-stack web services are the most horrible thing.
10:30 < mjschranz> mhoye: Sounds like a nightmare.
10:32 < mhoye> mjschranz: When we built this Firefox customizing thing, we built a pretty clever way to scan a directory tree and construct an .MSI file out of the result.
10:33 < mhoye> The software stack is a trail of tears. web -> php -> python -> wine -> mono (the .net emulator) -> emulated DOSSHELL commands and then all the way back.
I promise you it’s necessary. I’ve stared at this stuff for a long time, and there’s no other way. I overlooked bash and MySQL in that list, too. Go, me.
10:34 < mhoye> And at one point, we decided to hardcode in some work directory names.
10:36 < mhoye> the MSI-building tools have this awful bug in them such that the absolute path of a Linux filename, as munged through that stack and turned into a dosshell filename, has to be less than 96 characters.
10:37 < mhoye> So we put everything in /tmp/m - Nice and short, and Firefox's tree is never so deep that we'll get close, so we should be OK.
10:37 < mhoye> But it's a multiuser system.
10:37 < mhoye> What happens if two people happen to try building stuff in /tmp/m at the same time?
10:38 < mhoye> I was staring at that code the other day, on the verge of giving accounts to clients when it dawned on me.
If there’s one thing I’m proud of here, it’s that I saw this in the code before a customer saw it on their screen.
Imagine you’re wrapping gifts for a departments store. The process is simple: you take the gift, you get a box, you put it in the box. You wrap the box, you hand it to the client. Nothing to it.
But imagine if two people are using the space to do that, at the same time; you’re both racing to see who gets to which step first, to see who gets to use that space. And because these two people aren’t people at all, but processes on a computer, they don’t just nod, wait and take turns of their own initiative; they just follow the process. And whoever “wins” the race, getting there first, gets their workspace flattened as the other process moves their own work into it.
You take a gift. The other guy takes a gift. Both of you find a box that fits your gift, and one of you put it on the table first. And then the second guy puts theirs on the table.
What happens then? Does the second box get put in the larger, or on the smaller, first one? Does it just get brushed aside?
And then it gets worse, because then the gifts get dropped into the mix. Maybe a too-large gift smushes the smaller box, maybe both get put in the same box, maybe the small one gets put in the larger box before being crushed under the larger gift. Maybe one of them got wrapped already, if one of the processes thinks it’s that far ahead, and then gets a new, unwrapped gift dropped on top of it.
Shortly whatever mess is left on that table gets handed by one of the processes to one of the clients. You can never be sure which one, or what state that package is in, but you’re virtually guaranteed that it’s not something the client wanted. And better yet, the other robot can then turn around and maybe wrap up a bunch of empty space where the gift used to be, and hand that empty wad of wrapper off to their customer. Assuming some third client hasn’t come along to use that space at the same time. Or maybe forty more clients.
That class of problem is what’s called a “race condition“, and they’re some of the most subtle, pernicious and difficult problems in any kind of process management, software or otherwise.
10:39 < mhoye> the _best case_ scenario there is that it all just goes to hell.
10:39 < mhoye> Everything breaks for no obvious reason.
10:39 < mhoye> That's the best case.
10:39 < mhoye> The worst case is that one of my clients is given _somebody else's_ .MSI.
10:43 < mhoye> The real problem turned out to be "finding everywhere we've hardcoded that directory name, and figuring out how to pass that variable around".
10:44 < mhoye> And it was hidden in some places that, in retrospect, seem obviously nuts.
I’m sure all of this would have been easier if I was smarter.
One of the worst was finding that we’d put “-om” in the arguments we pass to a decompression tool, which actually mean “output to directory m”, not “using optional feature om”. Instead of taking a few extra minutes to define a variable somwhere and build the command string. Stupid, stupid. We did it months ago, too; I looked right at it when we did, with both eyes, I know better than to do that, and I did it anyway. My eyes skittered over that line for hours without latching onto that, either; embarrassing.
10:47 < mhoye> Anyway, rookie mistake solved.
10:47 < mhoye> But the lesson here is that knowing whether or not 2+ people will be using a program at the same time is a profoundly important design decision.
10:48 <@humph> so true
10:50 < jbuck> mhoye: 0, 1 or infinity users :)
10:51 < mhoye> And nobody gets paid for zero, and it's rare to get paid for one, yeah.
10:58 < mhoye> Man.
10:59 < mhoye> Git commit and git push have never felt this good.
One more reminder that “works for me” isn’t worth all that much. I’m going to chalk this one up to experience, and man, I’m glad this week is nearly done.