Thursday, November 20, 2008

Another Flex Cross-Browser Incompatibility (OS X ProgressBar display)

So, Flash is supposed to solve all our cross-browser issues... only it doesn't.  My most recent discovery relates to the visual look of a ProgressBar between OS X and Windows when the mode = ProgressBarMode.MANUAL and indeterminate = true.

To illustrate this, we'll start with the following MXML component:

<mx:progressbar x="10" y="10" id="progress" labelplacement="bottom" indeterminate="true" enabled="true" minimum="0" maximum="100" label="" width="210"/>

With some styling applied, the result is this:


Now, let's say that we want to use the ProgressBar for some non-indeterminate (er, determinate) purpose, like loading some data from a server. So, let's do this:

progress.indeterminate = false;
progress.mode = ProgressBarMode.MANUAL;
progress.setProgress(0,100);

someLoader.addEventListener(ProgressEvent.PROGRESS,someProgressListener);

Great. Now, someLoader will load something, and someProgressListener will keep us up-to-date about the progress. So, say we've loaded all the data and now we're doing some processing, the progress of which we don't know since our processing function is synchronous and does not dispatch ProgressEvents. So, we want to set indeterminate back to true so that our users will know something is still happening but not show possibly incorrect progress with a determinate ProgressBar. So, let's do this:

progress.removeEventListener(ProgressEvent.PROGRESS,someProgressListener);
progress.indeterminate = true;

WTF! Now our ProgressBar looks like this:


In Firefox and IE on Windows, the ProgressBar looks identical to before, which is the expected behavior. But in Safari (at least) on OS X, we have this monstrosity. What happened? Shouldn't Flash look identical in any browser? Well, apparently it doesn't.

Fortunately, this isn't unfixable.  In fact, it's a very simple fix, though it makes zero sense to me why this changes the visual display.  Just set progress.mode = ProgressBarMode.EVENT. Turns out that this ugly displacement only occurs in OS X if (progress.mode == ProgressBarMode.MANUAL && progress.indeterminate). Oh well, something to chalk up on the list of cross-browser differences in Flash.

On another note, remember that progress.setProgress(num,total) will not do anything unless you have set progress.mode = ProgressBarMode.MANUAL. This is another thing I think is ridiculous, but the ProgressBar will ignore any calls to setProgress if mode != ProgressBarMode.MANUAL. I understand that the different ProgressBarMode settings will instruct the ProgressBar where to expect progress changes from, but it still seems odd to fully ignore calls to setProgress WITHOUT throwing any kind of error or warning when mode != ProgressBarMode.MANUAL.

Versions:  This was discovered in OS X 10.5, Flash MAC 10.0.12.36 (debug), Safari 3.2.1.

Monday, November 10, 2008

Flex / AS3 Tweening

I can't believe I haven't given a shout out to TweenLite before.  I love it.  Absolutely, love it.  For the only reason you should need to use TweenLite over Flash's built in Tween, Tweener, ZigoEngine (Fuse), Twease, HydroTween, or gTween, see this speed comparison.  

The second great part about TweenLite is that it is dead simple to use.

TweenLite.to(objectToTween, howLongToTween, { propsToTween ... callbacks });

Ridiculously simple to integrate into any project.

I only have one complaint:  When I haven't tweened in a few days, I always forget that the duration for a TweenLite is represented in secconds, not milliseconds.  Usually, it takes me about 5 minutes to remember, since my Tween didn't happen when I wanted and instead happened 300 seconds later as I was staring at my screen wondering what the hell I messed up.  I get that it's a convenience thing, but to me it's inconvenient.  If TweenLite durations were specified in milliseconds, I think I'd gain 1000* 60 * 5 * 7 back to my week.

Saturday, November 1, 2008

Monitoring AWS Ubuntu Instances using Munin

Another munin setup blog? Why?
There are way too many blog posts out there with incorrect info on how to setup munin. My process to get it setup infuriated me enough to break from work and write the post. If any blog tells you to run:
/usr/bin/munin-cron --force-root
or any other munin command using --force-root, you should instead run like hell in the opposite direction. Doing so will only cause you hours of hell figuring out why your graphs stopped updating. It turns out that by using --force-root you just hijacked ownership of the .rrd, .png, and .html files that munin uses; for more details, see here. Furthermore, anybody that wrote a blog telling you to do that clearly didn't wait to see whether their setup was successful before posting (or they made the unwise choice of running munin as root all the time).

There also is some bad information on the web about using the setting use_node_name when setting up groups and hosts in the munin master config file. Nearly every blog I found included enabling use_node_name despite the munin documentation saying very specifically,
"You will almost never want to set [use_node_name] to anything, as setting it to "yes" tends to break things." (source)
Doesn't that just scream "Don't do it!" And yet nearly everybody is blindly recommending it. The thing is that by enabling it, you can make a setup work where it otherwise wouldn't have if you screwed up the config by using inconsistent host names between the munin master and munin nodes. So, what should you do? Is it okay to enable use_host_name? My inclination is no, because it's just a naive hack around the fact that you screwed up the config. If the people that build munin say don't use it, then I say don't use it; just learn how to configure munin correctly.

So, without further introduction, here's a more comprehensive guide to setting up a single munin master to monitor multiple munin nodes, and since I did this on AWS, there are some snippets about do's and don'ts on AWS.

On the Munin Master
Quite simple, run:
apt-get install -y munin apache2
There. Munin master is installed. Easy, no? That just setup the appropriate directories, cron jobs, and permissions, so all you really need to do next is edit /etc/munin/munin.conf but we'll wait to do that until after the nodes are ready. Additionally, it installed apache so that you can view munin reports on the web.

On all Munin Nodes
Again, simple, run:
apt-get install -y munin-node
Told you that would be easy. Great, now time to configure each node. There are only a few things we need to configure on the node, and they are all located in /etc/munin/munin-node.conf. The properties to set are:
host_name my.hostname.com
allow ^120\.0\.0\.1$
The host_name setting will be the name by which your munin-node will report itself to the munin master. This is very important, though I didn't find it mentioned in any other blog, for reasons I will explain below when I show the master munin.conf config file. The short version is that it doesn't really matter what you call the node, just choose something that makes sense to you and remember it. For a database layer, something rational might be mysql.yourdomain.com for the master, and slave0.yourdomain.com and slave1.yourdomain.com for the slaves.

The allow setting specifies what master host(s) are allowed to connect to this munin node. Due to some perl specifics, this value has to be a regex, though that can come in handy. You can repeat the allow line as much as necessary.

This is where the AWS issue comes in: what value do we put for allow? Do we use the external address, such as ec2-127-0-0-1.compute-1.amazonaws.com? Do we use the internal address, domU-00-00-00-00-00-00.compute-1.internal? Or, do we use the ip address directly, say 127.0.0.1? This will resurface later, too, in how the master will connect to the nodes.

Which one you choose does matter. For example, if you use the ip address or the external AWS address, your AWS account will be charged for all data transfer and you will have to authorize port 4949 to allow the munin master to get through the firewall. Ergo, it really makes sense to use the internal representation. Furthermore, this allows you to keep the firewall closed, in which case you can put a nice little regex in the allow line to allow any of your instances to connect, thus saving yourself headache if your master external address changes, if you decide to switch the master to a different address, or for some reason add another master. So, on AWS, the easiest allow line entry is:
allow ^.*$
If you're not familiar with regex, that literally allows any munin master to connect, which means that it would be ridiculously stupid to do in any situation where your machines are not all behind a common firewall. Make sure to restart each munin-node (/etc/init.d/munin-node restart) after editing the config file to ensure the changes are live. Okay, now repeat that on as many machines/instances as you want to monitor. For more options configuring a node, see the munin documentation. When finished, continue.

Configuring the Munin Master
The config file for the munin master is /etc/munin/munin.conf. The basic idea here is to define groups and hosts. A simple group looks like:

[db.yourdomain.com]
address domU-00-00-00-00-00-00.compute-1.internal
And here's what that all means: You defined a group "yourdomain.com" and then the host "db.yourdomain.com" under that group. Then, you said that the munin node located at that address is the host named db.yourdomain.com in the group yourdomain.com. So, what is all this group nonsense? The simple version is that it's just how hosts are organized, and that makes for a nice display on the web. If you want to get more complex, you can configure different behaviors for groups or show totals by group. For more info on this file, see the munin documentation.

So, on to things that matter. The name of the host (db.yourdomain.com) MUST match the value of host_name that the munin node at the address specified will report. If it doesn't, then you won't see any data collected for the node. The exception is if you enable use_node_name, then graphs will show up because that setting tells munin to fetch "all configured plugins associated with the given node, both plugins that report the nodes host_name and none at all," (source). But, since use_node_name is not recommended, just make sure that the values of host_name in munin-node.conf an the name of the host in munin.conf are the same.

Now Wait
You only have to wait 5 minutes at the most. After that, you will be able to see the data collected from the web. This is where a lot of blogs get impatient and tell you to run:
/usr/bin/munin-cron --force-root
which will update munin databases right away, but will also hijack the permissions from user "munin" to the root user, thereby denying the munin user access to those files at a later time, which will stop all updates because the cron job installed in /etc/cron.d/munin runs /usr/bin/munin-cron as munin, not root. You know, if I recommended doing that in my blog, I'd feel like a jerk for leading people astray.

Okay, patience is not one of your virtues and you don't want to wait 5 minutes? Fine. Just do this instead:

root@xxx:/root$ su munin --shell=/bin/bash
munin@xxx:/root$ /usr/bin/munin-cron
There, that will let you run munin-cron as "munin", which lets you see your update right away without hijacking file permissions. Just hit ctrl+d when you're done to go back to whatever user you previously were.

Alright, hope that saves somebody some frustration.