Thursday, June 30, 2011

Updated Node

As a result of a new version of Node being released, I've updated my Solaris 10 (x86) package, available here.

This updates Node to 0.4.9 and the bundled cURL to 7.21.7.

Building this was exactly the same as in my earlier posts. I've updated the patch, although the observant will notice that it's the same patch with the path updated; there's no functional change in the patch.

Tuesday, June 28, 2011

Node serving zipfiles

One problem I've been looking at recently is how to serve - efficiently - directories containing large numbers of tiny files to http clients. At the moment, we just create the files, put them on a filesystem, and let apache go figure it out.

The data is grouped, though, so that each directory is an easily identifiable and self-contained chunk of data. And, if a client accesses one file, chances are that they're going to access many of the other neighbouring files as well.

We're a tiny tiny fraction of the way into the project, and we're already up to 250 million files. Anyone who's suffered with traditional backup knows that you can't realistically back this data up, and we don't even try.

What I do, though, is generate a zip file of each directory. One file instead of 1000, many orders of magnitude less in your backup catalog (thinking about this sort of data, generating a backup index or catalog can be a killer), and you can stream large files instead of doing random I/O to tiny little files. We save the zip archives, not the original files.

So then, I've been thinking, why not serve content out of the zip files directly? We cut the number of files on the filesystem dramatically, improve performance, and make it much more manageable. And the access pattern is in our favour as well - once a client hits a file, they'll probably access many more files in the same zip archive, so we could prefetch the whole archive for even more efficiency.

A quick search turned up this article. It's not exactly what I wanted to do, but it's pretty close. So I was able to put together a very simple node script using the express framework that serves content out of a zip file.

// requires express and zipfile
// npm install express
// npm install zipfile
//

function isDir(pathname) {
if (!pathname) {
return false;
} else {
return pathname.slice(-1) == '/';
}
}

var zipfile = require('zipfile').ZipFile;
var myzip = new zipfile('/home/peter/test.zip');

// hash of filenames and content
var ziptree = {};
for (var i = 0; i < myzip.names.length ; i++) {
if (!isDir(myzip.names[i])) {
ziptree[myzip.names[i]] = myzip.readFileSync(myzip.names[i])
}
}

var app = require('express').createServer();

app.get('/zip/*', function(req, res){
if(ziptree[req.params[0]]) {
res.send(ziptree[req.params[0]], {'Content-Type': 'text/plain'});
} else {
res.send(404);
}
});

app.listen(3000);
console.log("zip server listening on port %d", app.address().port);

So, a quick explanation:

  • I open up a zipfile and, for each entry in it that isn't a directory, shove it into a hash with the name as the key and the data as the value.

  • I use express to route any request under /zip/, the filename is everything after the /zip/, and I just grab that path from the hash and return the data.


See how easy it is to generate something pretty sophisticated using Node? And even I can look at the above and understand what it's doing.

Now, the above isn't terribly complete.

For one thing, I ought to work out the correct content type for each request. This isn't hard but adds quite a lot of lines of code.

The other thing that I want to do is to have the application handle multiple zip files. So you get express to split up the request into a zipfile name and the name of a file within the zip archive. And then keep a cache of recently used zipfiles.

Which leaves a little work for version 2.

Saturday, June 18, 2011

Node and kstat goodness

Now I've got got node.js built on Solaris (see blog entry 1 and blog entry 2) I've been playing with using it as a server.

So the next thing I did was to augment node-kstat from Bryan Cantrill's original. There's the odd minor fix, I've essentially completed the list of kstats supported (including most of the raw kstats), and added methods that give the support needed by JKstat, and there's an example jkstat.js script that can run as a server under node that the JKstat client can connect to.

A collection of my Node stuff is available here, including a Solaris 10 package for those of you unable to build it yourselves.

Of course, in order to use the node-kstat server I've had to add RESTful http client support to JKstat, which has now been updated with the 0.51 release.

JKstat also loses the JavaFX demos. It doesn't appear to me that JavaFX is going to be terribly interesting for a while. The 2.0 beta isn't available for Solaris at all (1.x wasn't available for SPARC anyway), and appears to be essentially incompatible anyway.

Friday, June 17, 2011

Cache batteries on Sun 25xx arrays

I have a number of the old Sun 2530 disk arrays, and a batch that we bought 3 years ago have started to come up with the fault LED lit.

These arrays have cache batteries, and the batteries need replacing sometimes to ensure they're operating correctly. Originally, this was a simple 3 year timer. After 3 years, a timer expires and it generates a fault to let you know it's time to put in new batteries.

Current Oracle policy is different: rather than relying on a dumb timer and replacing batteries as a precaution, the systems are actually capable of monitoring the health of the batteries (they have built in SMART technology). As a result, they will only send out new batteries if there's actual evidence of a fault.

This is actually good, as it means we don't have to take unnecessary downtime to replace the batteries. (And it eliminates the risk of the battery replacement procedure accidentally causing more problems.)

Now, the management software version we have (6.xx) doesn't report the SMART status (but will report if a real failure occurs). So you can't see predictive failure, but if CAM just says "near expiration" then it's just the precautionary timer.

So, the solution is to check and reset the timer.

Go to CAM. (The exact location of the relevant menu item may vary depending on which version of CAM you've got, so you may have to go looking.)

Expand the Storage Systems tree

Select the array you want to fix

Click on service advisor

In the new window that pops up, verify that it's actually picked up the correct array. The name should be at the top of the expanded tree in the left-hand panel.

Under Array Troubleshooting and recovery, expand the Resetting the Controller Battery Age item.

Click on each battery in turn and follow the instructions.

This also applies to the 6x80 arrays as well, as I understand it, but I don't have any of those.

If you search for "2500 battery" on My Oracle Support, you'll find all this documented.

Wednesday, June 08, 2011

Node.js on Solaris, part 2

Here's a followup and slight correction to my notes on building node.js on Solaris 10.

If you read carefully, you'll notice that I specified --without-ssl to the configure command. This makes it build, as it looks like there's a dependency on a newer openssl than is shipped with Solaris 10. While this is good enough for some purposes, it turns out the the expresss module wants ssl (it's required, even if you don't use https, although all you have to do is delete the references).

So, a better way is to build a current openssl first and then get node to link against that. So for the openssl build:

gzcat openssl-1.0.0d.tar.gz | gtar xf -
cd openssl-1.0.0d
env CC=cc CXX=CC ./Configure --prefix=/opt/Node solaris-x86-cc shared
gmake -j 8
gmake install
I'm using the Studio compilers here, as I normally do with things like openssl that provide libraries that might be used by other tools, although gcc should work fine. The important parts here are that it matches the architecture of your other components (so 32-bit, ie x86) and you build shared libraries.

Then you can unpack and patch node as before, then configure with

env LDFLAGS=-R/opt/Node/lib CFLAGS=-std=gnu99 ./configure --prefix=/opt/Node --openssl-includes=/opt/Node/include --openssl-lib=/opt/Node/lib

Sunday, June 05, 2011

Building node.js on Solaris 10

Constantly on the search for new tools and technologies to play with, I wanted to actually try out Node.js on my own machine.

I'm running Solaris 10 (x86), and it didn't quite work out first time. But it was pretty trivial to fix. A couple of tweaks to the configure invocation and a simple patch to make things like isnan and signbit work with a vanilla Solaris 10 install.

So, to build Node on Solaris 10 x86:

First download node-0.4.8 and my node patch. (Yes, the patch to V8 is ugly, and it's likely to be specific to the V8 version and the Solaris rev and gcc version. And don't expect Node or V8 to work on sparc at all.)

Unpack node and apply the patch:

gzcat node-v0.4.8.tar.gz | gtar xf -
gpatch -p0 -i node-v0.4.8.soldiff

Then configure and build:

env CFLAGS=-std=gnu99 ./configure --prefix=/opt/Node --without-ssl
gmake -j 8
gmake install

Replacing /opt/Node with wherever you want to install it. (Somewhere you have permission to write to, obviously.)

You then want to install npm. You will need to have curl for this, although I recommend downloading the install.sh and running it by hand, like so:

env PATH=/opt/Node/bin:$PATH TAR=gtar bash install.sh

This way, it uses the correct version of tar and uses a compatible shell. (It actually invokes curl in the script, so you still need curl installed. That's not hard, or you can find one on the Companion CD.)

To actually use npm you need to continue with the tweaks. For example:

env PATH=/opt/Node/bin:$PATH TAR=gtar npm install express
or, if you want it to install express into the Node tree rather than "." you'll need something like:

env PATH=/opt/Node/bin:$PATH TAR=gtar npm install -g express