Monthly Archive for November, 2007

My Blog, My Rules

Charlie Arehart send me an email the other day  about my Flex charting control and the way it's presented on the site (which is poorly).  I ended up writing a fairly long response that was a bit tangential, but unreasonably so.  The basic premise was that it's my site, and I don't like maintaining web sites, so I do it as little as possible.  For example, I'd never bothered to actually put an email address on my About page.

But that expands out to cover some other bases as well.  For example, I actually release a fair amount of code, and I have a enormous amount of non-released code that I could release.  But you'll notice that I don't have a single software product that I maintain.  Contrast this with Ray Camden.  I don't claim that I produce the quantity of code that he does, but he produces products, not just code.

Productizing a software product is a huge amount of work, and maintaining that product only adds more.  I have little to no interest in dedicating my personal time to that.  Even just prepping code to release it takes a lot of work, in my view.  If I'm excited about something, I want to share it and get other people excited about it too, just like anyone else.  But the ROI is minimal, since I'm not trying to build any sort of brand or create an income stream.  Which isn't to say I don't have personal brand, just I don't cultivate it the way a business(wo)?man or consultant would.

So I'm curious.  Do people use the software I release?  Comment if you do, and list what you use.  I'm just wondering, because the code is rarely polished, it's not formally versioned, rarely has standalone docs (and often very minimal inline docs), and there is no promise of backwards compatibility or long term support.   Is that a deterrent sufficient to turn away people who's problems my code might directly address?  Should it be?

I know I'm apt to discount software packages that aren't actively maintained, but I'm far less likely to discount something if it's source.  Sure, I like to have someone else do the work, but if I find something that addresses a need I have, I'm going to use it if at all possible.  I'm quite lazy that way.

Then there are the blog posts themselves.  I go very back-and-forth on blogging.  I enjoy writing, but the blog is a weird medium.  In one sense, you're writing to nobody, but you're also writing to the readership you're aware of.  But that's never your complete readership.  And unlike most other forms of communication, you have rather little feedback on a blog.  Sure, people comment, but what percentage?  Contrast that with a document you write for responding to an RFP, an email you write to someone, or even an IM conversation.

As such, I dislike writing posts that don't have a clear point (like this one).  It leaves me feeling like I'm not done yet, and that I wasted every reader's time.  But at the same time, any sort of pointed discourse leaves me this wondering how I did.  Did I get my point across?  Was I confusing?  Without a specific audience and direct feedback, those questions are very hard to answer.

So why do I write on my blog?  Usually to share something I'm excited (or frustrated) with.  Hoping that someone else will read it and be excited too.  But it's always technical, because I know that most of the readers are techies, and have this weird feeling like I have to appease my perceived audience or a Bad Thing will happen.   But I want more than that.  I want to have a conversation.  To yell.  To ramble on.  To share the love of something.  I want to turn comments off so I don't have to listen to people.  I want to force every reader to comment.  I want to start an interesting philosophical discussion that persists forever.

I've considered dedicating a period each day to writing.  About whatever.  But I know that would leave me less satisfied, because it'd become a chore, and I wouldn't end up writing about the stuff that really blows my skirt up (no, I don't typically wear a skirt - makes for cold … legs … on the motorcycle).

What's the solution?  Someone knows.  Tell me.  Please.

New FlexChart Demo

I've updated the FlexChart demo to include display of the descriptor XML that is loaded into the chart, as well as providing a way to edit the XML inline and load your modified XML into the chart client-side.  In addition to being far easier to experiment with, it also showcases the the client-side redrawing of the chart and gives a hit of how powerful the engine can be within a JS UI.

Doing it this way means I lost the demo of having the chart request a new descriptor from the server-side on it's own, but the client-side descriptor injection is a "neater" capability, I think.   I haven't posted an updated zip of the source (including the demo app), but it's available from Subversion at the URL listed at the bottom of the demo.

A Very Wonky Request/CFTHREAD Bug

Found some really interesting behaviour with CFTHREAD over the past couple days of testing.  Put this code into three browser tabs and run them concurrently (so you get three simultaneous requests which you can view the output of).  And ensure you have the CF Server Monitor open in a different window with monitoring enabled and the Active ColdFusion Threads report showing.

<cfoutput>
<cfapplication name="#createUUID()#" />
<cfset request.threadName = createUUID() />

<h1>Request: #timeFormat(now(), "HH:mm:ss")#</h1>
<cfflush />
<cfdump var="#cfthread#" label="cfthread is empty" />

<cfthread action="run"
  name="#request.threadName#">
  <cfthread action="sleep" duration="10000" />
</cfthread>

<cfdump var="#cfthread#" label="one thread running" />
<h1>Thread: #timeFormat(cfthread[request.threadName].startTime, "HH:mm:ss")#</h1>
<cfflush />

<cfif structKeyExists(url, "join")>
  <cfthread action="join" name="#request.threadName#" />
</cfif>

<h1>Complete: #timeFormat(now(), "HH:mm:ss")#</h1>
<cfdump var="#cfthread#" label="thread shows completed" />
</cfoutput>

What happens?  As expected, all three requests return immediately, each having spawned a thread.  The server monitor shows that the threads all go away after ten seconds.  Most importantly, all three requests started, launched their thread, and completed at the same time (+/- a few tens of milliseconds).

Now go to the first tab and add "?join" to the URL, which will trigger the joining of the thread back to the request thread.  Refresh all three tabs again.  This time, the tabs without the join command behave exactly the same, but the tab with the join waits for the thread to complete before returning, as evidenced by the "Completed" timestamp.  This is all correct behaviour, so lets get down to it.

Add "?join" to the second tab (so now two tabs will join), and refresh all three tabs a third time.  Notice that the second tab that joins doesn't even start until the first joining request completes.  The non-joining request completes immediately, as always, initiating at the same time as the first joining request.

I don't even know how to characterize this issue.  It's certainly a bug, but I'm not sure what.  Even more baffling, I can't even come up with a theoretical scenario that would cause these symptoms.  Anyone?

Clickable FlexCharts

I just updated FlexChart with a 'click' callback.  You can now specify the name of a JavaScript function to be invoked when a data point on the chart is clicked.  The function is passed the ID of the chart, the series label, and the x and y values for the point, in that order.  Check the demo (which just pops an alert) to see it in action.

What is this good for, you ask?  Everything that CFCHART's 'url' attribute was theoretically good for, except it's actually useful.  If you wan CFCHART's behaviour, just have your callback do a window.location.  For those of us that don't, you can use the parameters to AJAX in some related content to another section of the page, compute and inject a new descriptor do do "drill-down" within the same chart instance, or any number of other things.  Lots of possibilities.

CFTHREAD/Server Monitor Issue

I was working with CFTHREAD today, and found an interesting bug in ColdFusion.  I'd wager that it's within the monitoring stuff new in CF8, but I can't say that for sure.  Here's the problem:

Creating threads with CFTHREAD makes them appear in the server monitor, but they aren't removed from the server monitor when the terminate unless the monitoring/profiling options are enabled.

To put that another way, threads give the appearance of sticking around forever unless you launched them with the monitoring/profiling options enabled.  I have no idea why this is the case, but it is.  The inaccurate state persists across reloads of the monitor, so the bug is somewhere inside CF, not the Flex app.  Threads that are listed but are actually dead don't have a value in the "Java Thread Name" column, you can identify (and ignore) them in the datagrid view.  The chart view, however, stays polluted until you restart CF.

Me.

For most of the US, as is common knowledge, this weekend was Thanksgiving weekend.  For most of the "white collar" workforce, that means a four day weekend.  Which brings up an interesting question.  What color is my collar?  It certainly not a laborer, which seems to mean it's white by default, but that seems … weird.  Lawyers wear white collars.  Who knows.

So what did I do with my weekend?  I (along with Heather and the kids) went to my folks house on the coast, ate copiously, stayed up WAY too late writing code by choice, watched some football, came back home Saturday, and went to the zoo for a picnic on an appropriately sunny, albeit cold, Sunday.

Work has been very frustrating since starting at Mentor, and it's only gotten more so within the past few months.  Suffice to say, not having to think about anything work related for four days was a blessed relief.   I don't want to imply that I'm one of those people who dreads going to work every day.  In the general sense, I very much like my career, just the specifics of the past "while" have me to the point of dread.

What struck me this evening, as I was opening my laptop to check my email one last time, was that I didn't want to just check my email.  I wanted to go get a glass of water and spend until the early hours of tomorrow morning banging out some more code (and Flex-based code to boot!).  That was strangely reassuring to me, because it meant that I'm still doing the right thing for my job, it's just the specifics that suck.

Work is something that I've always tried to cleanly delineate from my personal life.  The lens was certainly focused spending last year working from home, but it existed before and has persisted since.  One aspect that certainly contributes is that Heather doesn't really understand software development beyond a superficial level (the "yeah, so I cast the foo char to the boo-leen [sic] array, and pop the kernel stack" level), so the triumphs and defeats at work are hard to bring home with any sort of depth.

My evenings are, ironically, usually spent on the computers working on "stuff", where stuff is any of myriad personal application I've built and use, some public, some not.   I dislike watching TV aside from a handful of shows, but Heather follows a number of series, so she's often watching those or at practice for the community choir she sings in.  A significant portion of the R&D work I do is off the clock.  Which I don't really mind, because I'm going to write the code whether it's paid for or not, but it seems a bit odd from the 25,000 foot view.

Before leaving AudienceCentral at the end of last year (amicably, I might add), I'd been pretty much heads-down on a complete rewrite of the company's software platform.  A significant undertaking, as you might imagine.  Aside from that project, most of the "demanding" coding that I've done since the turn of the century is of a personal nature.  And not even "on personal time to be released open source to the benefit of all", but totally personal.  I put "demanding" in quotes above, because I have two very specific definitions in mind:

  1.  requiring significant effort to …

end of story, at least for tonight.  Heather just came and kissed me, and it was wonderful……..

One More Reason to Love GMail

Google released a new version of the GMail UI a couple weeks ago.  Not a whole lot different on the surface, but it's noticeably snappier overall, and has a few little improvements throughout (mute from the actions menu, contact detail popups).

I had two main gripes: it doesn't work with the [unsupported] BetterGMail Firefox plugin and the space bar didn't scroll up/down the page anymore (presumably from different key listening stuff).  The UI upgrade is backwards compatible, so you can, at your discretion, flip-flop between the old and new versions.  I debated about continuing on the old version, but I'd only just picked up BetterGMail and so wouldn' t miss it that much, and I figured I could learn to use the scroll wheel like a "normal" person not concerned with shaving literally hundreds of milliseconds of their email reading experience.

I finally got around to complaining about the space bar issue either Friday or Saturday morning.  When I logged on today the space bar scrolled correctly.  This isn't the first time I've gotten rapid gratification for my complaints/suggestions, though it's been a while, mostly because they've fixed all the things that bothered me.  ;)  I'm certainly not going to take sole credit for prompting this patch (or any other), but it's nice to know that as well as producing an email application that is actually user friendly (don't get me started on Outlook which is required at the office), they listen.

Critical Bug: Schema Tool

I discovered a critical bug in my schema tool this evening. During the refactoring I did last week to allow multiple persistence mechanisms (so it doesn't require a DB) I reintroduced a bug that I had fixed in the DB-only version.

If the first minor version of a migration (major version) throws an error, it updates the major version counter in the persistent store without resetting the minor version to zero. As such, when you resolve the error and rerun the script, the tool believes that you've already run N minor versions of the migration, where N is the number of minor versions in the previous migration script.

If you've downloaded the tool since the 18th of November, you've got the buggy code, and need to download a freshly patched copy. Subversion is also updated, if that's your preferred source.

As a freebie, there's a new enhancement in the SQL-based migration script for supplying a custom statement delimiter.  Previously you had to use a semicolon, but now you can specify your own.  Check the comment at the top sqlbasedmigration.cfc for more details.

Just Say No to Materialized MySQL Views

I'm a big fan of MySQL, but I ran into an interesting performance issue this weekend regarding views. MySQL added view support in 5.0, and I've used it to great effect, but it's not all roses.

Within MySQL, there are several ways to categorize views, but the one I care about is materialized vs. rewritten/merged. Materialized views are views that, as you might guess, are materialized when they're needed. In simpler terms, the view definition is used to create a temporary table and then that temporary table is used for the actual query. Rewritten or merged views are combined with the actual query by the SQL engine and executed as a single query.

The performance problem I ran across was with the former view type. It seems that the internals of MySQL manage materializing in a very inefficient way. I can't provide any sort of hard data to support that, but what I experienced was definitely points to the conclusion.

I had a view that had some fairly intensive stuff in it (both structurally and computationally). I knew it was materialized and was fine with a little bit of a slowdown, but I was seeing times in the 15 second range for certain queries that used it on my personal server (which, admittedly, is a only 3GHz Celeron). Manually materializing the view (i.e. running the view statement to create my own table) and then running the queries in question took a few hundred milliseconds tops, counting both the generation of the temp table and the actual query time (with most of it being the former).

The only explanation I can come up with was that the materializing of the view was happening repeatedly. Very repeatedly. Recasting the "view" as a normal table that I repopulate as needed has eliminated all the performance issues, and the queries are now typically running in tens of milliseconds.

I should mention that I've never had any performance issues with rewritten/merged views. MySQL seems to do a fantastic job efficiency-wise with them, and I continue to use them.

Interestingly, I had a very similar problem with Oracle a few months ago. The main difference was that I wasn't using views, but rather subqueries in the FROM clause (i.e., inline views). In that app, creating separate views was both impossible (we didn't own the database), and unnecessary (only one query used the view), and we were able to get around it with "factored subqueries" (the WITH clause).

More Flex Chart Goodness

This evening, I extended my Flex charting widget further.  The demo is still available, and I've posted a new ZIP archive of the source.   Here's a quick rundown of what's changed:

  • The custom tag is now called xmlchart.cfm, instead of just chart.cfm.  That's to make way for a different chart.cfm that provides an XML-less interface (like CFCHART does) that will internally build the XML and then hand it off to xmlchart.cfm.
  • Category and DateTime axes are now supported.  The only gotcha is that you must supply a milliseconds-since-epoch value for your timestamps.  It's XML, not real objects, and the serialization/deserialization doesn't work right with native dates.
  • Pie charts are now available.
  • Legend placement is now customizable (top, bottom, left, right, none).
  • Axes can have a title set for them, and they can be positioned on either edge of the chart (top/bottom for x, left/right for y).
  • The descriptor language has changed significantly:
    • The x- and y-axis now have their own element under the chart element.
    • All series and sets are now contained with a new data element under the chart element.
    • There are only series and set elements now, and both take a type attribute for determining what type of series/set they are.  Series implicitly inherit their type from the containing set, if present, otherwise their type must be specified.

At this point, I'm pretty much done developing, and I'm on to using it.  I did one app as a proof of concept and it works great.  I'll be doing some more shortly.

And Happy Thanksgiving, at least for all you US folks.