Tuesday, May 19, 2009

Review of new Seagate 500g 7200rpm laptop hard drive

When my 1 year old Hitachi laptop hard drive died in March, I started shopping for a bigger one. That's when I found the new Seagate Momentus 7200.4 model ST9500420AS with 500 gigs of space at 7200rpm.

At that time though, the drive was out of stock everywhere as Seagate seemed to have stopped production to fix some engineering problems. Since even now (May 2009) this is the biggest and fastest consumer 2.5 inch drive available, I decided to wait for it.

In the mean time, I read reviews on it, which were mixed. Some people who got their hands on one of the first run models reported them slow, noisy, buggy and hot. Other owners reported back that the drives were fine. Two more good reviews issued from barefeats and hardwarelogic.

After about 2 months of waiting, these drives were for sale once again, this time with updated firmware 2SDM1. This is the one I bought online from WiredZone for $133 with free shipping. It's just a brown box, egg foam and anti-static bag. No glossy fanfare, manuals or any of that. I don't know if that's typical.



Before evicting the old (warranty replacement) Hitachi 200g 7200rpm, I decided to take some crude benchmarks of it so I'd know whether the new Seagate was any better. Say goodbye to HTS722020K9SA00 Made in Thailand:



Hello Seagate Hecho in China:

Seagate ST9500420AS 7200rpm 500gig, firmware: 2SDM1

After copying a bootable backup onto an external firewire drive with the excellent SuperDuper! cloning software, I was ready to swap drives. Disassembly instructions for the model 3,1 MacBook Pro are at iFixit. If you have a Bugs Bunny video, then you won't have to unhook the delicate ribbon cable that connects the keyboard to the motherboard.



After the swap, I booted from the external firewire drive and used SuperDuper! to clone back onto the new empty internal hard drive. Then a reboot and everything is running on the new Seagate.

Now, for the comparisons, which may not be quite fair because the Hitachi was almost full for the benchmarks, while the Seagate was mostly empty.

Boot Time
HitachiSeagate
Apple Logo: 51 sec
Login Window: +33 sec
Total: 84 secs
Apple Logo: 16 sec
Login Window: +40 sec
Total: 56 secs


Xbench
HitachiSeagate
Results 43.50
System Info
Xbench Version 1.3
System Version 10.5.6 (9G55)
Physical RAM 4096 MB
Model MacBookPro3,1
Drive Type Hitachi HTS722020K9SA00
Disk Test 43.50
Sequential 79.03
Uncached Write 108.92 66.88 MB/sec [4K blocks]
Uncached Write 110.86 62.73 MB/sec [256K blocks]
Uncached Read 40.92 11.98 MB/sec [4K blocks]
Uncached Read 125.34 62.99 MB/sec [256K blocks]
Random 30.01
Uncached Write 9.76 1.03 MB/sec [4K blocks]
Uncached Write 97.19 31.11 MB/sec [256K blocks]
Uncached Read 77.83 0.55 MB/sec [4K blocks]
Uncached Read 130.73 24.26 MB/sec [256K blocks]
Results 52.73
System Info
Xbench Version 1.3
System Version 10.5.6 (9G55)
Physical RAM 4096 MB
Model MacBookPro3,1
Drive Type ST9500420AS
Disk Test 52.73
Sequential 119.09
Uncached Write 164.42 100.95 MB/sec [4K blocks]
Uncached Write 146.68 82.99 MB/sec [256K blocks]
Uncached Read 65.58 19.19 MB/sec [4K blocks]
Uncached Read 183.84 92.39 MB/sec [256K blocks]
Random 33.86
Uncached Write 10.73 1.14 MB/sec [4K blocks]
Uncached Write 171.66 54.96 MB/sec [256K blocks]
Uncached Read 80.56 0.57 MB/sec [4K blocks]
Uncached Read 148.96 27.64 MB/sec [256K blocks]


Bonnie++
Hitachi:
Version 1.93c       ------Sequential Output------ --Sequential Input- --Random-
Concurrency 1 -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP
arf.local 16G 297 97 52023 18 24538 9 332 95 55015 11 105.9 10
Latency 80374us 687ms 610ms 121ms 213ms 4029ms
Version 1.93c ------Sequential Create------ --------Random Create--------
arf.local -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
files /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP
16 6111 55 +++++ +++ 9257 61 367 8 +++++ +++ 151 4
Latency 76653us 920us 79744us 333ms 1399us 414ms

Seagate:
Version 1.93c       ------Sequential Output------ --Sequential Input- --Random-
Concurrency 1 -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP
arf.local 16G 293 96 90295 32 36049 14 343 98 91548 20 164.4 13
Latency 134ms 388ms 217ms 85474us 132ms 1942ms
Version 1.93c ------Sequential Create------ --------Random Create--------
arf.local -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
files /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP
16 5313 56 +++++ +++ 5567 41 320 8 +++++ +++ 149 5
Latency 106ms 200us 151ms 497ms 380us 322ms


dd
I ran the write in root "/" directory (and therefore had to use sudo) to avoid writing in my home directory, which is Filevault and would probably skew performance downward. The 1st dd in each section below is writing, the 2nd is reading.

HitachiSeagate
$ sudo time dd if=/dev/zero of=/Volumes/Macintosh\ HD/test bs=1024k count=16384;
17179869184 bytes transferred in 319.400409 secs (53787875 bytes/sec)

$ time dd of=/dev/null if=/Volumes/Macintosh\ HD/test bs=1024k
17179869184 bytes transferred in 305.974147 secs (56148107 bytes/sec)
$ sudo time dd if=/dev/zero of=/Volumes/Macintosh\ HD/test bs=1024k
17179869184 bytes transferred in 188.208635 secs (91280983 bytes/sec)

$ time dd of=/dev/null if=/Volumes/Macintosh\ HD/test bs=1024k
17179869184 bytes transferred in 180.531769 secs (95162581 bytes/sec)


The result of the tests showed that in addition to more than doubling my disk space with the new drive, it is also objectively faster than the old one, even at the same spindle speeds. However, this is probably just because the new one is mostly empty and the old one was mostly full. Performance will always be a lot better when data is on the beginning instead of the end of a mechanical drive.

The last bit of info to share is from SMART, accessed with smartctl from smartmontools. The Hitachi was in pretty good shape, aside from the strange value for Power-Off_Retract_Count. Don;t have a clue what that one means:

Hitachi SMART
Model Family:     Hitachi Travelstar 7K200
Device Model: Hitachi HTS722020K9SA00
Serial Number: 080830DP0470DTGP3MMC
Firmware Version: DC4AC77A
User Capacity: 200,049,647,616 bytes
Device is: In smartctl database [for details use: -P show]
ATA Version is: 8
ATA Standard is: ATA-8-ACS revision 3f
Local Time is: Sun May 17 23:31:07 2009 PDT
SMART support is: Available - device has SMART capability.
SMART support is: Enabled


SMART Attributes Data Structure revision number: 16
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate 0x000b 100 100 062 Pre-fail Always - 0
2 Throughput_Performance 0x0005 100 100 040 Pre-fail Offline - 0
3 Spin_Up_Time 0x0007 176 176 033 Pre-fail Always - 1
4 Start_Stop_Count 0x0012 100 100 000 Old_age Always - 200
5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail Always - 0
7 Seek_Error_Rate 0x000b 100 100 067 Pre-fail Always - 0
8 Seek_Time_Performance 0x0005 100 100 040 Pre-fail Offline - 0
9 Power_On_Hours 0x0012 100 100 000 Old_age Always - 342
10 Spin_Retry_Count 0x0013 100 100 060 Pre-fail Always - 0
12 Power_Cycle_Count 0x0032 100 100 000 Old_age Always - 192
191 G-Sense_Error_Rate 0x000a 100 100 000 Old_age Always - 0
192 Power-Off_Retract_Count 0x0032 100 100 000 Old_age Always - 42954326020
193 Load_Cycle_Count 0x0012 100 100 000 Old_age Always - 8991
194 Temperature_Celsius 0x0002 130 130 000 Old_age Always - 42 (Lifetime Min/Max 14/47)
195 Hardware_ECC_Recovered 0x000a 100 100 000 Old_age Always - 0
196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 0
197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 0
198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 0
199 UDMA_CRC_Error_Count 0x000a 200 200 000 Old_age Always - 0
223 Load_Retry_Count 0x000a 100 100 000 Old_age Always - 0


The Seagate, on the otherhand, looked alarming when I first checked it out. I thought the drive was defective and was ready to send it back for RMA:

Seagate SMART
$ sudo smartctl -s on /dev/disk0
SMART Enabled.

$ sudo smartctl -a /dev/disk0
Device Model: ST9500420AS
Serial Number: 5VJ079ZE
Firmware Version: 0002SDM1
User Capacity: 500,107,862,016 bytes
Device is: Not in smartctl database [for details use: -P showall]
ATA Version is: 8
ATA Standard is: ATA-8-ACS revision 4
Local Time is: Wed May 20 00:42:19 2009 PDT
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
...
SMART Attributes Data Structure revision number: 10
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate 0x000f 118 100 006 Pre-fail Always - 184273167
3 Spin_Up_Time 0x0003 100 100 085 Pre-fail Always - 0
4 Start_Stop_Count 0x0032 100 100 020 Old_age Always - 2
5 Reallocated_Sector_Ct 0x0033 100 100 036 Pre-fail Always - 0
7 Seek_Error_Rate 0x000f 100 253 030 Pre-fail Always - 139058
9 Power_On_Hours 0x0032 100 100 000 Old_age Always - 24
10 Spin_Retry_Count 0x0013 100 100 097 Pre-fail Always - 0
12 Power_Cycle_Count 0x0032 100 037 020 Old_age Always - 5
184 Unknown_Attribute 0x0032 100 100 099 Old_age Always - 0
187 Reported_Uncorrect 0x0032 100 100 000 Old_age Always - 0
188 Unknown_Attribute 0x0032 100 100 000 Old_age Always - 0
189 High_Fly_Writes 0x003a 100 100 000 Old_age Always - 0
190 Airflow_Temperature_Cel 0x0022 062 052 045 Old_age Always - 38 (Lifetime Min/Max 28/41)
191 G-Sense_Error_Rate 0x0032 100 100 000 Old_age Always - 0
192 Power-Off_Retract_Count 0x0032 100 100 000 Old_age Always - 0
193 Load_Cycle_Count 0x0032 099 099 000 Old_age Always - 3798
194 Temperature_Celsius 0x0022 038 048 000 Old_age Always - 38 (0 22 0 0)
195 Hardware_ECC_Recovered 0x001a 045 045 000 Old_age Always - 184273167
197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always - 0
198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 0
199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age Always - 0
240 Head_Flying_Hours 0x0000 100 253 000 Old_age Offline - 70690866724884
241 Unknown_Attribute 0x0000 100 253 000 Old_age Offline - 2076453070
242 Unknown_Attribute 0x0000 100 253 000 Old_age Offline - 2307582568
254 Unknown_Attribute 0x0032 100 100 000 Old_age Always - 0

SMART Error Log Version: 1
No Errors Logged

SMART Self-test log structure revision number 1
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Short offline Completed without error 00% 21 -
# 2 Extended offline Aborted by host 90% 21 -
# 3 Extended offline Aborted by host 60% 9 -
# 4 Extended offline Aborted by host 50% 2 -


Raw_Read_Error_Rate, Seek_Error_Rate and Hardware_ECC_Recovered make it look like the disk is dying. Attributes 240, 241, 242 are nonsensical. After investigating though, it seems that these type of values on those attributes are just normal for a Seagate.

A few searches on these attributes will find many discussions where people are concluding that these odd values don't indicate a problem. Seagate also has a KB article basically warning users not to pay attention to those SMART values. Apparently they stuff their own proprietary values into the SMART circuits which only becomes meaningful when pulled through their "Seatools" disk analyzer software. Regular SMART software tools that follow the published SMART protocols won't be able to make any use of Seagate's stored raw values for those attributes. Incidentally, there is no Mac version of Seatools.

I've had my new Momentus drive running now for about 24 hours. Over the last 4 hours while I've been using the machine, I have heard a pretty loud "CLUNK" twice. Probably the heads parking or unparking for powersaving mode. The Hitachi never did that, but it's only happened twice and other than that, I can't tell the difference between this drive and the old one by noise, vibration or temperature. Tests indicate that there are no errors that other owners were complaining about this past winter with the older firmware rev, and it is a bit faster than Hecho in Thailand.

I guess it's a good one but I'll still keep up the SuperDuper! onsite and Crashplan offsite regimen.

Monday, December 08, 2008

use jQuery AJAX to create options in a dropdown menu

If you love to hate Javascript, that half-breed, amateur-magnet language, and every onload() and eval() you ever saw, then jQuery just rained on your parade. Now, with jQuery, so many things are elegant and easy - the opposite of everything you've ever known about Javascript.

Here's how to populate a select menu's option list (values and labels) with data retrieved from an AJAX request. The impatient may jump to a working demo to see if this is even what you want. Maybe you were searching for some sink cleaning product.

Materials:
  • 1 webpage that loads jQuery and makes an AJAX request (page.html)
  • 1 script that receives the AJAX request and answers it (script.php)
Unlike so many pages written in plain Javascript, which don't function or are missing content in browsers that do not execute Javascript (Lynx, Googlebot, people who dont want your crappy code heating up their CPU and turned it off in Preferences), jQuery's philosophy is that if your browser will execute Javascript, then the page will be better, but if not, then the page should "degrade gracefully" and still at least mostly work. So in this tutorial, the demo page is served with an initial select menu that should be good enough, in case the user doesn't run the script.

Here's the markup that we start with (page.html): A form with an input field, select menu and a button that will trigger our script. In this example, a user enters a zip code in the input box and clicks the button. That will trigger an AJAX request to another resource, sending the zip code and retrieving a bunch of shipping options, which will then magically fill the dropdown menu.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>jQuery ajax demo populating select dropdown menu</title>
</head>
<body>
<form>
Zip: <input name="zip" type="text" size="5" maxlength="10" id="zip" value="" /><br />
<select name='shipping_method' id="shipping_method">
<option value="0" selected="selected">Select Shipping Method</option>
<option value='FEDEX_2_DAY' >FedEx 2 Day</option>
<option value='FEDEX_EXPRESS_SAVER' >FedEx 3 Day</option>
<option value='INTERNATIONAL_PRIORITY' >FedEx International</option>
<option value='PRIORITY_OVERNIGHT' >FedEx Priority Overnight</option>
<option value='STANDARD_OVERNIGHT' >FedEx Standard Overnight</option>
</select>
<input id="getrates" type="button" value="Lookup Shipping Rates" /><br />
</form>
</body>
</html>


Notice that there are no Javascript functions strewn into the markup as tag attributes (no onclick(), no onmouseover()). That's because jQuery separates code for behavior from code for presentation. All the jQuery code will go in the "head." From high up there, it can hook into the DOM using only its patent pending "selectors."

So now let me show you what to add inside the "head" tag in order to load jQuery on the page, and then jQuery code to write that will do all the work.

First, a tangent: Normally, you would download the jQuery.js libraries from jQuery.com onto your own webserver, and serve them to your visitors from there with a "script src" tag. That's fine if you want to do it that way, but there's another option to direct visitors get those libs from Google instead. There are a lot of good reasons for offloading this job, so I leave it to you to read about it. In this example, that's what we're doing.

So, first, add this inside your head tag (before or after title tag) to get your visitor to load the jQuery libraries:

<script src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("jquery", "1");
google.load("jqueryui", "1.5.2");
</script>


Now the browser understands jQuery. All the rest of your jQuery code can come next, in separate script tags, still in head:

<script type="text/javascript">
// When the DOM is ready to have events hook into it...
$(document).ready(function() {

// when the DOM element with id="getrates" is clicked....
$("#getrates").click(function() {

// make that button you clicked disappear...
$(this).hide(); // opposite of show() // See jQueryUI for more info

// and issue an AJAX request to a PHP script in the same directory
// getJSON is a method that expects a JSON-encoded data structure to be returned
// there are other AJAX methods too. See http://docs.jquery.com/Ajax
$.getJSON("script.php", // 1st arg to getJSON is the URI of the script
{
zipcode: $("#zip").val(),
random: "noise"
},
// 2nd arg to getJSON is an array of key-value pairs to send
// to the script. As many as you want.
// left-side is the GET variable name as seen by the target
// script, right-side is the value that will be sent.
// the above 2 args will cause the AJAX script to be hit with
// the query string: ?zipcode=90019&random=noise
// assuming that the user typed "90019" into the
// element on this page with the id="zip"


// 3rd arg is the callback function for the AJAX response
// The script.php responds in a JSON format so jQuery can
// understand the data structure natively, without you
// writing awful parsing of your own:
function(j) {
// erase all OPTIONs from existing select menu on the page
$('#shipping_method options').remove();

// You will rebuild new options based on the JSON response...
var options = '<option value="">Choose Shipping Method</option>';
// "j" is the json object that was output by your PHP script
// it is the array of key-value pairs to turn
// into option value/labels...
for (var i = 0; i < j.length; i++)
{
options += '<option value="' +
j[i].optionValue + '">' +
j[i].optionDisplay +
'</option>';
}
// stick these new options in the existing select menu
$("#shipping_method").html(options);
// now your select menu is rebuilt with dynamic info
}
); // end getJSON
}); // end clicked button to trigger AJAX
}); // end document ready
</script>


That's it. When the user clicks the button, an AJAX request is sent to script.php, and the response is used to rebuild the shipping_method dropdown menu. Prices appear inside the options list before your very eyes. If you want to see a working demo, check here

You may also want to copy this, name it "script.php" and save it on your server in the same directory as the html page above. It outputs a canned JSON answer that the jQuery code will use to make the select options.

<?php
# script.php

$pretend_results = array('PRIORITY_OVERNIGHT' => 39.69,
'STANDARD_OVERNIGHT' => 48.45,
'FEDEX_2_DAY' => 19.75,
'FEDEX_EXPRESS_SAVER' => 15.75);

$haOptions = array();
foreach($pretend_results as $method => $cost)
{
$haOptions[] = array('optionValue' => $method, 'optionDisplay' => "$method $$cost");
}

# make JSON object that will populate select dropdown menu options

if(function_exists('json_encode'))
{
echo json_encode($haOptions); # this puts the php array in the funny javascript array/object
# format so you don't have to know how to translate manually
}

else
{
# some lame web hosts dont have a new version of PHP (5.2+) that includes json functions in core
# so here, I fake it for you so you will have a working demo:
echo '[{"optionValue":"PRIORITY_OVERNIGHT","optionDisplay":"PRIORITY_OVERNIGHT $39.69"},{"optionValue":"STANDARD_OVERNIGHT","optionDisplay":"STANDARD_OVERNIGHT $48.45"},{"optionValue":"FEDEX_2_DAY","optionDisplay":"FEDEX_2_DAY $19.75"},{"optionValue":"FEDEX_EXPRESS_SAVER","optionDisplay":"FEDEX_EXPRESS_SAVER $15.75"}]';
}

# this output is what the jQuery ajax request will receive and parse in its ajax callback function
exit;
?>


When pasting the above PHP script into your editor, if your server does not have the json_encode() function (PHP version < 5.2) then be careful to NOT let your editor (like pico) wrap the long line dummy JSON string with hard line breaks. Hard returns in that data structure will break the fragile thing and your AJAX callback function will not execute.

It is still Javascript, after all, what did you expect?

Sunday, March 02, 2008

Reasonable Backups of Filevault

It doesn't take much web searching to come to the conclusion that the new Time Machine in MacOS 10.5 does not work well with Filevault.

The problem is that to Time Machine, a home directory protected with Filevault is just one big "sparse image" encrypted file. Although it will happily backup this file, doing that defeats one of the purposes of TM, which is to give you snapshots of every individual file from different times, so that you can go back through them and preview them easily before restoring.

If TM is backing up this giant disk image each time, then it is spending all your disk space on your backup drive on the whole disk image for every snapshot. This is a total waste of space. Without Filevault, the behavior would be to take a snapshot only of the changed files, so that your backup drive was only using space to store 1 copy of your files, plus the changes for each snapshot. Another problem with the interaction between FV and TM out of the box is that it's not very convenient in the "Cover Flow" interface to browse through the encrypted images, nor to have to provide a passphrase for each and mount each in order to look at the files inside.

Therefore, I decided to use "rsync" (from Terminal) to backup my home directory to an external drive while I am logged in. In order to keep my files secure on the backup drive, I decided to encrypt that whole device with Truecrypt, which just recently added support for MacOSX.

First I downloaded the Truecrypt .dmg file, mounted that by doubleclicking it, then ran the Truecrypt installer inside there. Once Truecrypt was installed on the Mac, I ran it and told it to encrypt the whole external USB backup drive.

After that was finished, I mounted the new volume according to the "Beginner Tutorial" in the TC documentation. At this time, TC could only create the volume as a FAT filesystem. Because I've been burned before by FAT's maximum filesize of 4G (tarring some stuff directly to the backup drive and having my tarball silently truncated at 4g), I wanted a real filesystem for my backups.

To change the filesystem of the mounted Truecrypt volume, I opened Disk Utilities from the Applications, Utilities menu in the Finder and, while Truecrypt volume is still mounted (so you see it without the encryption), told it to partition the new volume 200G HFS+ and 50G FAT. I left a FAT partition on it so that I could still use the drive on other non-Mac computers.

After the TC volume was re-partitioned and reformated, I was ready to run rsync to copy my home directory in there:
rsync --archive --progress --verbose \
--exclude '.Spotlight-V100' --exclude '.fseventsd' \
--exclude 'Desktop ' --exclude 'Library/*' \
--exclude 'Downloads/*' --exclude 'Music/*'
--exclude 'Public/*' --exclude 'Sites/*'
~me /Volumes/MacBackup/backup
Where my username on the Mac is "me" and the HFS partition inside the Truecrypt volume is "MacBackup" and the directory inside there where I want all my backup stuff is "backup." The result of the command is that everything in my home directory, including hidden files that begin with a '.' like .bashrc, will be copied to the backup directory -- except for a few subdirs of the home that I don't care about and have excluded.

While figuring out which rsync command will work for you, add the "--dry-run " option in until you get it right. I will be saving that command in a shell script that I will periodically execute after connecting the USB drive and running Truecrypt to unlock and mount it.

The reason that I am involving Truecrypt at all is just so that I could use the backup drive on other non-Mac machines, since it is cross platform Windows/Linux/Mac encryption. If I didn't care about the cross platform stuff, I would just have used Apple's Disk Utilities to create an encrypted disk image on the USB drive, and stored backups in there. I sort of defeated some of that purpose by using an Apple-only HFS partition, but maybe in the future there will be a better cross platform filesystem to select from the Disk Utility menu that will also support files larger than 4G.