Friday, July 24, 2009

PostgreSQL and capitalization

The terminal-based PostgreSQL front-end psql, which ships standard, does not like capitalized table names.

ri=> \d KDplot_plotpoint
Did not find any relation named "KDplot_plotpoint"
ri=> \d "KDplot_plotpoint"
Table "public.KDplot_plotpoint"
Column | Type | Modifier
...

Double-quotes are your friends.

Tuesday, July 21, 2009

On ZFS and terror

I've had my main backup / nameless cruft repository on 3 x 1TB drives in a ZFS RAID-Z array for a while now, and finally found out what happens ZFS (or Linux ZFS-FUSE, at least) encounters bad blocks.

It loses it, completely.

I suspect this is largely a Linux problem: once a bad block is encountered, the Linux kernel (?) seems to go into an infinite-reread tailspin, flooding syslog with errors and generally making the SATA bus (and the machine at large) unusable. So it might not be ZFS' fault.

And a quick aside to the many posts claiming that "the OS should never see a bad block; the drive should silently remap the block to a spare and the OS will only be aware of it when you've run out of spares!": bullshit. I've had half a dozen drives turn up w/ bad blocks, and the SMART stats on each reported plenty of spares, and each drive was fine after a forced overwrite. (IE, I zero'ed out the drive.)

So I repeated my zero-out procedure on this drive, and the sequence of events gets fuzzy, but at some point I backed up /etc and at some later point I ran zpool export on the DEGRADED array. When the time came to put the newly-zero'ed drive back in the array, zpool wouldn't bring it back up: zpool import reported that "The pool can be imported despite missing or damaged devices," but zpool import tank ("tank" being the customary name for ZFS pools, I think) complained that it "cannot import 'tank': one or more devices is currently unavailable." zpool import -f tank yielded the same thing.

This when when I began to panic.

Despite having 2 fully functional drives, all my data would have been lost. But remember that backup of /etc, notably including /etc/zfs/zpool.cache? That was my salvation: restoring that brought me back to my pre-export, degraded state, which in turn let me replace the faulty drive.

My data is safe for now, but my confidence in ZFS is shaken. Why did exporting a degraded array make it un-importable? If I lose my zpool.cache, is all lost?

Wednesday, June 3, 2009

Developer preferences

Ubuntu still ships with GHC 6.8, released 12 December 2007. Macports offers 6.10.3, which has been around for about 3 weeks.

I might be reading into this too much, but methinks there's something there.

Friday, May 15, 2009

On the prescience of Infinite Jest

It's not a central plot point, but DFW's near-future Infinite Jest (1996) mentions Microsoft Pink, the operating system du jour.

Perhaps he has a fan in Redmond?

Wednesday, April 22, 2009

Are you sure you want to navigate away from this page?

Scenario: you've been tasked with creating a simple web-based time-sheet application, so employees can log what percentage of their time is devoted to which project. Now, this hardly qualifies as "Bioinformatics," a domain claimed by both your job title and personal aspirations, but it's a small company and they pay your rent.

So you build the little beasty, and your cube-neighbor sits down to fill out a time-sheet. She spends 7 minutes selecting options and punching in percentages, and then realizes she wants to trim one field from 15% to 10%, clicks her mouse 4 pixels too far to the right putting the page, and not the text input, into focus, and hits Backspace -- equivalent to the back button.

There go her 7 tedious minutes of form-filling -- this app isn't sophisticated enough to autosave a draft in real-time, after all.

Enter the onbeforeunload function.

I've borrowed from this guy a bit, but here's the code:
var confirm_exit = true;

window.onbeforeunload = function() {
if( confirm_exit )
return "";
return; };

$(document).ready( function() {
$("#id_theform").submit( function() { confirm_exit = false; } );
} );
Originally, I'd planned to skip the ugly global variable and simply re-bind the onbeforeunload function, but apparently you can't rebind like that.

Friday, April 10, 2009

A Beginner's Guide to Hating iMovie '08

Thank you, Apple. This is pretty much my first experience editing footage into a "movie," and with iMovie '08, you've made the process slow, painful, and constrained. What's more, I was lucky enough to get my computer weeks before the release of iMovie '09, so any of your stunning omissions fixed in the subsequent version will cost me $100. No thanks.

First off, titles. Adding chapter titles is a pretty common use for iMovie, I would think, but clips cut from one Project and pasted in another are stripped of their title. Thanks.

Oh, but wait: "chapter titles?" What's a chapter? iMovie isn't really meant to create movies for later burning onto a DVD -- who would ever want to distribute a DVD? So very 2003! The only way I've found to separate footage into chapters for iDVD is to put each chapter into its own Project. Annoying.

Thursday, April 9, 2009

The perils of laziness

I've begun cargo culting my way into Haskell, in the hopes that I'll eventually grasp such mathematical mysteries as anamorphisms. In the mean time, I'd like to write some code that actually works.

To this end, I've written a program to digest the C. elegans transcriptome in silico, then check the resulting fragments against data from a mass spectroscopy experiment (Multidimensional Protein Identification Technology --"MudPIT"--if you're curious). I use regular expressions to break up the protein sequences, store the fragments in a big Data.Map, and then test my MudPIT fragments against the Map.

I've greatly simplified my function to be a simple list-to-map conversion -- which would be stupid, since Data.Mat.fromList does this quite well -- but the real function involves plenty of off-topic complexity, so here's the svelt form:
  add_frags (i:l) m =
let m' = M.insert i () m in
add_frags l $! m'
add_frags [] m = m
The important part is the $!, in bold. Given some function f and parameter x, f $! x is equivalent to x `seq` f x; the seq function, meanwhile, forces the evaluation of the first thunk. So f $! x applies f to x AFTER x HAS BEEN EVALUATED.

Without that forced evaluation, ghc will simply add more thunks to the stack until the stack is blown ... hours after the run has begun. You can pretty easily imagine this as a series of substitutions, which is exactly what we're talking about:
  import qualified Data.Map as M
m = M.empty
add_frags ["ACDEF","GHI","KLMNO","PQRST",...] m
-> M.insert "ABCDEF" () (add_frags ["GHI","KLMNO","PQRST",...] m)
-> M.insert "ABCDEF" () (M.insert "GHI" () (add_frags ["KLMNO","PQRST",...] m))
And off the stack grows, until pop.