Specialized and my famous water bottle

June 8th, 2012

I’ve loved riding bikes as long as I can remember. I would ride for hours. In the early 70s I had a bike that I converted to an offroad bike, but I preferred the road. Eventually I moved on to a Sears steel-framed 10 speed bicycle. I rode that bike everywhere. I rode to school, to the bank on weekends to deposit lawn mowing money, often times trips that were probably twenty or thirty miles. It was a heavy bike, but, I took care of it and met a guy named Don who worked for Prince Georges County and had a bike sales and repair business that he ran out of his house.

Where I grew up, there weren’t many bike shops – in fact, Calvert County, Maryland had 38000 residents at the time and three stoplights – one of which blinked. University of Maryland College Park had more students attending than we had in our county. Being too young for a drivers license, his house became a parts shop and tune-up shop for my bike. I would voraciously read every Bicycling magazine the day it came in the mail – eyeing frames, cranks, sprockets, etc. I followed the careers of Eddy Merckx, Hinault, LeMond, this Texan rider named Lance Armstrong, the USPS and 7-11 teams and many more. Later I would see the USPS riders around Baltimore and Annapolis and ride with them for two evenings which was a highlight.

Don had purchased and refurbished a used bike but couldn’t find anyone that could ride it. I was relatively tall and the bike was a perfect fit. I paid $150 for it, $30 for a bike computer and he threw in a Specialized water bottle. With a few words of wisdom, trading in my steel bike and cash, I rode the ten or so miles home. After a bit of research recently, I realize now that he probably lost money on that sale. Don, Thank you.

I purchased my Concord road-racing bike with Centerpull drop-forged brakes, aluminum wheels, a light frame, 14 speeds with the shifters on the downtube. A quick education on the bike and I was off. I was 15 so I didn’t really listen, I wanted to ride my new bike.

And oh was that bike different. I suspect it seriously outgeared my old ten speed and it was light, nimble and very responsive. I couldn’t ride with no hands for weeks and even the slightest shifting of my weight meant I had to react very quickly to grab the handlebars. I rode everywhere. With a rolling start, I would beat the school bus to school on its 4.7 mile journey and its few stops to pick up students – and it was a slight uphill grade with some rolling hills for the first 1.5 miles. Wearing a backback became a problem during hill climbs, so, I bought an aluminum bike rack and would strap my backpack on after splitting the books and putting them in the panniers. My ride home consisted of one of two loops – either 21 or 38 miles – in the later years after running Cross Country practice.

But, that wasn’t enough. On weekends I would ride hundreds of miles and Don would ask why I was wearing out tires so quickly or chains or whatever part was being replaced. After a year or so I replaced my computer with a Cateye, replaced with another Cateye which I just replaced with a Sigma 1609 since my old Cateye sensor wire appeared to have a short. I do remember the old Cateye registering 50k miles. I rode in a lot of Criterions, Centuries and Double Centuries. I ran four Triathlons, finishing 8th overall as my best finish. Once, I rode 30+ miles, competed in a Century, finished in the top ten, and then rode home.

Then life happened. I got really busy and rode the bike on weekends. After a while, it sat in the basement because I would just go riding next week. Weeks turned into months and after a few years I was back on the bike. Mostly weekend tours around the Potomac River, out to West Virginia and Western Maryland a few times. Zoomed past Mount Weather on a few trips.

Work started to pick up, I moved 1000 miles to Florida and removed a lot of the time that I would have ridden. I figured the ‘endless summer’ would let me ride more. Once in a while I’d hop on the bike and do a ten or twenty mile ride, but, it was infrequent. Then things took a turn for the worse and my workload increased significantly and I didn’t ride the bike at all for five or six years. Every year or so I would look at it and think about pulling it down to ride again, but, never had the time.

Fast forward to a few weeks ago and I decided to get some exercise as working behind a desk for so many years hasn’t been particularly good for my physique. I spent a day cleaning my bike, adjusting and lubricating parts and pieces. I took it for a few quick half mile rides around the neighborhood and recognized the clunk, clunk, clunk of improperly adjusted cones and the tang, tang, tang, whump of a loose spoke as I spun the wheels and let my fingernail hit the spokes. The derailleur seemed straight even though it had been through a few moves and with some adjustment, got it to go into everything but the lowest gear. Later tinkering showed that the bolt that held the bike rack on was preventing the chain from always making it to the smallest sprocket. Turning that screw around fixed that, though, the nut was pretty chewed up from having been mangled many times getting into low gear. That explains the intermittent problems I had getting into low that thankfully never plagued me while racing – since the bike rack wasn’t on the bike. :)

I replaced the handlebar tape with new microfoam tape and short of a minor goof from not having wrapped handlebars in 20+ years, they look pretty good. Replaced the brake hoods which were falling apart and had gummed up the brake exterior and gave it a good tuneup. One wheel was a little out of round but a few turns of the spoke wrench and it is now fairly true.

A few days later I learned that the Motivation Man Triathlon was riding on the road not too far from my house. I did some quick calculations of 1/2 Ironman and Olympic swim times, relative distance and calculated when I would need to be at the Olympic distance turnaround. The 1/2 Ironman riders proceeded past that point to a turnaround in the Everglades and a return trip. I ended up working until 3am and had an alarm scheduled for 6:45am.

And that’s when it happened.

While removing the top from the bottle to wash it, I noticed a small crack. I gingerly took the top off, washed and rinsed it really well, refilled it and tossed it in the fridge for the night. The next morning I grabbed it, two bottles of water and two ‘snack packs’ of Chips Ahoy cookies to toss in the Vetta box along with a rain jacket. The crack had grown slightly overnight, but, I figured it would be fine for the trip. I ended up taking 356 pictures using SnapReplay which were uploaded to the Motivation Man Triathlon, Royal Palm Beach, Florida event. My phone’s battery died about the time the last rider turned at the Olympic turnaround. A quick ride home to get three more batteries for my phone and the crack was growing.

After taking the rest of the pictures from the 1/2 Ironman distance return trip, I rode home, unpacked and put my water bottle in the fridge. When I went to clean it that evening, I tried to pull the cap off without using the thumbhold, but, it was fruitless.


Note: upon looking at the bottle more closely, it must have been a replacement as it bears a manufacture stamp of 7/92. Unless Specialized uses the Mayan calendar, the bottle is 20 years old, not 29 as originally calculated.

I thought back about the water bottle and tried to do some quick math and the numbers just didn’t seem right. Every time I tried to figure out when I had purchased it, I realized it was the one I got when I purchased the bike. In looking at the bottle when figuring out whether it was recyclable, I found a manufacture date stamp of 7/92, so, the bottle was not the one originally purchased with the bike, but, probably a direct replacement for the original bottle. It seemed impossible that it lasted that long and went through so much. It’s got scratches and scars on it from tumbles in the road, worn paint from the holder, but, I thought, I’m really happy that it lasted this long and Specialized made a great product, maybe they’ll get a kick out of it. I sent this tweet. @IamSpecialized retweeted and posted it on Facebook.

Within a few minutes of the retweet, KM @ Specialized asked if I was going to replace it with the same color and I said yes. Minutes later was a request for my mailing address so they could send me a free bottle — roughly six minutes before someone posted on Facebook that they should send me a free one.

So my water bottle received its fifteen minutes of fame.

Today, I received a package from Specialized including a Purist Hydroflo bottle. I spent a few minutes washing it, filled it with water and of course, tried it out. As many people mentioned, make sure it is BPA Free. And indeed, it appears all of Specialized’s water bottles are BPA free – and this one mentions it on the cardboard tag that came with it.

I didn’t write the original tweet expecting anything, I was just happy that the product had outlived its normal lifespan and thought they might find it interesting. As it turns out, they did as did a number of other people.

While my bike was perfect for the type of riding I did 22+ years ago, elbow to elbow crits, centuries, etc., today’s technology just puts that bike to shame. 7-cog clusters on the rear were rare back then and are just as rare today. The current gearing is 39/52 and a 13-15-17-19-21-25-30 rear. Somewhere in the boxes that have been sitting for twelve years is my 11-28 rear. I remember seeing it when moving and thinking, I need to remember that box.

I’m not going to race crits or triathlons anymore. I always wanted to race RAAM, but, that may have to be an unfulfilled dream. While researching my bike, I learned a lot about its history and learned that I probably underpaid for it when I purchased it. As near as I can tell it is a Concord RS1400(?) made by Kuwahara in Osaka Japan and retailed at $350 with the equipment it had. With as many parts as I’ve replaced over the years, there isn’t much left that is original.

Looking at today’s bikes, it really looks like starting anew is probably a much better choice. Specialized is at the top of a very short list.

My goal for 2012 after 22 years of casual riding is a solo century. And no, not one of those newfangled metric centuries, a real century. That means parts, water, food, snacks all need to be with me. I’m somewhat lucky in the area where I live since we do have long flat stretches of roads with bike lanes, few cars and a decent 65 mile loop to the west.

Since getting back on my bike, I’ve seen more of the city than I’ve seen in the twelve years I’ve lived here. It feels good to be back on the bike.

Can’t wait to try out the new bottle this evening.

Thank you again Specialized!

Ext4, XFS and Btrfs benchmark redux

May 22nd, 2012

As Linux 3.4 was just released and it includes a number of btrfs filesystem changes, I felt it was worth retesting to see if btrfs had better performance.

$ /usr/sbin/bonnie++ -s 8g -n 512

ext4

mkfs -t ext4 /dev/sda9
mount -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   582  98 59268   6 30754   3  3515  99 104817   4 306.1   1
Latency             15867us    1456ms     340ms    8997us   50112us     323ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 35092  55 520637  91  1054   1 35182  54 791080 100  1664   2
Latency              1232ms     541us   14112ms    1189ms      41us   11701ms
1.96,1.96,colo7,1,1337657098,8G,,582,98,59268,6,30754,3,3515,99,104817,4,306.1,1,512,,,,,35092,55,520637,91,1054,1,35182,54,791080,100,1664,2,15867us,1456ms,340ms,8997us,50112us,323ms,1232ms,541us,14112ms,1189ms,41us,11701ms

ext4 with tuning and mount options

mkfs -t ext4 /dev/sda9
tune2fs -o journal_data_writeback /dev/sda9
mount -o rw,noatime,data=writeback,barrier=0,nobh,commit=60 /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   587  97 64875   6 34046   4  3149  96 105157   4 317.2   4
Latency             13877us     562ms    1351ms   18692us   54835us     287ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 38127  59 525459  92  2118   2 37746  58 792967  99  1433   2
Latency               980ms     525us   14018ms    1056ms      46us   12355ms
1.96,1.96,colo7,1,1337661756,8G,,587,97,64875,6,34046,4,3149,96,105157,4,317.2,4,512,,,,,38127,59,525459,92,2118,2,37746,58,792967,99,1433,2,13877us,562ms,1351ms,18692us,54835us,287ms,980ms,525us,14018ms,1056ms,46us,12355ms

btrfs from ext4 partition

umount /mnt
fsck.ext3 -f /dev/sda9
btrfs-convert /dev/sda9
mount -t btrfs -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   462  98 62854   5 30782   4  3065  88 88883   8 313.1   7
Latency             63644us     272ms     206ms   38178us     241ms     409ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 36868  85 598431  98 26007  93 32002  73 756164  99 21975  84
Latency             15858us     427us    1003us     471us     157us    2161us
1.96,1.96,colo7,1,1337660385,8G,,462,98,62854,5,30782,4,3065,88,88883,8,313.1,7,512,,,,,36868,85,598431,98,26007,93,32002,73,756164,99,21975,84,63644us,272ms,206ms,38178us,241ms,409ms,15858us,427us,1003us,471us,157us,2161us

btrfs without conversion

mkfs -t btrfs /dev/sda9
mount -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   468  98 60274   4 29605   4  3629 100 89250   8 301.5   7
Latency             55633us     345ms     196ms    3767us     229ms    1119ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 26078  60 603783  99 26027  92 25617  58 754598  99 21935  84
Latency               452us     423us    1029us     426us      16us    2314us
1.96,1.96,colo7,1,1337661202,8G,,468,98,60274,4,29605,4,3629,100,89250,8,301.5,7,512,,,,,26078,60,603783,99,26027,92,25617,58,754598,99,21935,84,55633us,345ms,196ms,3767us,229ms,1119ms,452us,423us,1029us,426us,16us,2314us

xfs defaults

mkfs -t xfs /dev/sda9
mount -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G  1391  96 65559   5 31315   3  2984  99 103339   4 255.8   3
Latency              5625us   33224us     221ms   10524us     103ms     198ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512  7834  37 807425  99 14627  50  8612  41 790321 100  1169   4
Latency              1182ms     123us     837ms    2037ms      18us    7031ms
1.96,1.96,colo7,1,1337660479,8G,,1391,96,65559,5,31315,3,2984,99,103339,4,255.8,3,512,,,,,7834,37,807425,99,14627,50,8612,41,790321,100,1169,4,5625us,33224us,221ms,10524us,103ms,198ms,1182ms,123us,837ms,2037ms,18us,7031ms

xfs tuned

mkfs -t xfs -d agcount=32 -l size=64m /dev/sda9
mount -o noatime,logbsize=262144,logbufs=8 /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G  1413  96 64640   5 31226   3  2977  99 104762   4 246.8   3
Latency              5616us     370ms     235ms   10530us   62654us     206ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 14763  70 793694  98 23959  81 15104  72 790204  99  2290   8
Latency               482ms     118us     274ms     683ms      17us    5201ms
1.96,1.96,colo7,1,1337666959,8G,,1413,96,64640,5,31226,3,2977,99,104762,4,246.8,3,512,,,,,14763,70,793694,98,23959,81,15104,72,790204,99,2290,8,5616us,370ms,235ms,10530us,62654us,206ms,482ms,118us,274ms,683ms,17us,5201ms

btrfs with a snapshot

mkfs -t btrfs /dev/sda9
mount -o noatime,subvolid=0 /dev/sda9 /mnt
wget http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.4.tar.bz2
tar xjf linux-3.4.tar.bz2
btrfs subvolume snapshot /mnt/ /mnt/@_2012_05_22
Version  1.96       ------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
colo7            8G   469  98 58400   5 30092   4  2999  85 89761   8 321.1   3
Latency             17017us     267ms     240ms   22907us     300ms     359ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 31715  72 598360  98 25780  92 26411  59 756110  99 22058  84
Latency               102ms     424us     844us     472us      20us    2171us
1.96,1.96,colo7,1,1337664006,8G,,469,98,58400,5,30092,4,2999,85,89761,8,321.1,3,512,,,,,31715,72,598360,98,25780,92,26411,59,756110,99,22058,84,17017us,267ms,240ms,22907us,300ms,359ms,102ms,424us,844us,472us,20us,2171us

Deleted kernel, left it in snapshot, reran test

Version  1.96       ------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
colo7            8G   469  98 63934   5 31244   4  3208  94 90227   8 296.3   7
Latency             17009us     282ms     217ms    3746us     224ms    1269ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 28758  66 596074  97 26185  93 25714  59 755464  99 21893  84
Latency             42108us     424us     993us     445us      17us    2245us
1.96,1.96,colo7,1,1337671128,8G,,469,98,63934,5,31244,4,3208,94,90227,8,296.3,7,512,,,,,28758,66,596074,97,26185,93,25714,59,755464,99,21893,84,17009us,282ms,217ms,3746us,224ms,1269ms,42108us,424us,993us,445us,17us,2245us

Updated results using some different parameters. Same hardware, same hard drive.

leaf and btree size of 16384

mkfs -t btrfs -l 16384 -n 16384 /dev/sda9
mount -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   472  98 37514   2 14395   2  3135  89 80600   7 294.0   6
Latency             16820us     781ms     383ms   19736us     230ms     379ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 17447  46 621480  99 24345  94 14984  39 754999  99 19873  82
Latency               303us     494us     900us     412us     107us    3127us
1.96,1.96,colo7,1,1338411461,8G,,472,98,37514,2,14395,2,3135,89,80600,7,294.0,6,512,,,,,17447,46,621480,99,24345,94,14984,39,754999,99,19873,82,16820us,781ms,383ms,19736us,230ms,379ms,303us,494us,900us,412us,107us,3127us

leaf and btree size of 32768

mkfs -t btrfs -l 32768 -n 32768 /dev/sda9
mount -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   468  97 26136   2 17256   2  3135  89 84450   7 306.5   7
Latency             43238us     923ms     330ms   12632us     367ms     986ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 17958  61 624570  99 19930  95 14506  50 753354  99 15976  80
Latency             15384us     514us     937us     431us     144us    4782us
1.96,1.96,colo7,1,1338409200,8G,,468,97,26136,2,17256,2,3135,89,84450,7,306.5,7,512,,,,,17958,61,624570,99,19930,95,14506,50,753354,99,15976,80,43238us,923ms,330ms,12632us,367ms,986ms,15384us,514us,937us,431us,144us,4782us

leaf and btree size of 65536

mkfs -t btrfs -l 65536 -n 65536 /dev/sda9
mount -o noatime /dev/sda9 /mnt
Version  1.96       ------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
colo7            8G   467  97 25097   2 17349   2  2845  87 86653   8 300.2   7
Latency             56046us     772ms     414ms    4101us     249ms     241ms
Version  1.96       ------Sequential Create------ --------Random Create--------
colo7               -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                512 15372  68 626336  98 14723  96 13137  58 753463 100 11652  80
Latency             15825us     395us   77890us     428us      19us   15727us
1.96,1.96,colo7,1,1338410439,8G,,467,97,25097,2,17349,2,2845,87,86653,8,300.2,7,512,,,,,15372,68,626336,98,14723,96,13137,58,753463,100,11652,80,56046us,772ms,414ms,4101us,249ms,241ms,15825us,395us,77890us,428us,19us,15727us

Analysis

Last time I tested ext4, xfs and btrfs, deletions really lagged behind. Now, it looks like btrfs is quite a bit more robust. Additionally, there are better repair and recovery tools which were basically missing before. btrfs doesn’t lag behind like it used to, and while it is a little slower in some cases, it’s only a few percent. However, it makes up for that with some of the random and sequential creation and deletions.

Rough analysis at this point – if you need a versioning filesystem and don’t mind being a bit on the bleeding edge, btrfs has made substantial strides.

Updated Analysis

For the hardware in question, it appears that the larger block sizes with Bonnie++ don’t benefit things, but, make sure you test with your workload.

Test Equipment

  • Linux colo7 3.4.0 #1 SMP Mon May 21 00:29:58 EDT 2012 x86_64 GNU/Linux
  • Intel(R) Xeon(R) CPU X3220 @ 2.40GHz
  • WDC WD7500AACS-0 01.0 PQ: 0 ANSI: 5
  • ahci enabled
  • 100gb partition
  • machine rebooted between each test

ImportError: cannot import name urandom

May 10th, 2012
    import os, sys, imp, types, tempfile, optparse
  File "/usr/lib/python2.7/tempfile.py", line 34, in 
    from random import Random as _Random
  File "/usr/lib/python2.7/random.py", line 47, in 
    from os import urandom as _urandom
ImportError: cannot import name urandom

You’re using a virtualenv, you’re probably on Debian or Ubuntu, and you upgraded python through apt-get.

To fix it:

virtualenv /path/to/your/virtualenvironment

iPhone app for SnapReplay – Release Candidate being tested

May 7th, 2012

After sixteen days of whirlwind development, digging through APIs, bad code examples, bugs in documentation (31 of 33 reported documentation bugs have been fixed), this afternoon I released 0.9.11 of the SnapReplay iPhone app to the beta testers through TestFlightApp.com.

On Friday, we had a pending list of bugs and features which were whittled down this weekend to a set of bugs that were blockers, a set of features that would make version 1.1, and a final set of features that would be used for 1.0. Sitting and looking at a set of features and having to decide which make the cut is not an easy task. Compromises were made based on how useful that feature would be in everyday use and whether it should hold up submission to the App Store.

Over the weekend, Paul and I went through twelve versions of the app, fixing bugs, tweaking features leaving one final feature we felt couldn’t be ignored left. Hours of pouring through documentation, bug reports, code sprinkled throughout the net, resulted in me finally deciding that the feature could wait until morning. I pushed out a last test build which included everything but the last feature for testing and went to sleep for a few hours.

When I woke up, I realized that my approach to the problem was not unique, it had to have been solved previously and then I realized that the examples were deleting from the wrong class and that Apple had a convenience class method to handle this very situation. A few minutes later, that was working.

A few tweaks and 0.9.11 was pushed to TestFlightApp for beta testing.

Phew.

Hopefully this evening I’ll push 1.0.0 and submit it to the App Store.

IPhone app development for SnapReplay retrospective

April 26th, 2012

While the SnapReplay iPhone app isn’t finished, it is mostly feature complete compared to the Android app. The first photo was uploaded last night via iPhone and the first event, Dunsborough Sharks Pre Season Party, with both an iPhone and Android phone was taken.

When was the last time I used Objective C? early to mid ’90s? This was going to be fun.

I had XCode installed but only played with the emulator to see how web pages looked. I didn’t own an iPhone and have used Android as my preferred OS for smartphones.

Comparing the development environment between the two, XCode definitely won this hands down. Within minutes of sitting down, I had my storyboard put in place, pages looked consistent when run through the emulator as buttons were connected. All that was left was to write some of the logic behind the pages… and that is where the problems started.

I can’t say I ever recall Eclipse crashing on me. XCode? After submitting 20-30 crash reports, I just click reopen without sending a bug report. Searching google shows that I’m not the only person experiencing these problems. Assistant mode gets confused which requires you to delete the files, and reimport them – and when it reconnects things, things tend to work as documented.

Since the app is based around the camera, we find that the iPhone Simulator doesn’t handle the camera. Even the Android emulator plays a repeating grid pattern with a bouncing ball to simulate the camera, but, with the iPhone Simulator, you get a blank page, your event fires, but, it isn’t really helpful. So, I purchased an iPhone 3GS for testing.

Objective C is reasonably easy to jump back into, but, Cocoa, Apple’s framework, is a nightmare. I submitted close to thirty documentation fixes, and to Apple’s credit, they are acted upon promptly. Numerous blog posts talk about the problems, API documentation errors and example code that won’t even compile, but, you can see that Apple does handle things pretty quickly when posted on their system rather than a random post on the Internet. While I wasn’t doing anything extremely complex, I did find it frustrating that the two features I needed to access were rife with documentation problems. That aside, a seven day whirlwind of coding resulted in an app that is functional and points out some other issues.

When I chose my smartphone, I chose Android for two reasons. One, I could develop applications for it for myself – of which I’ve done none of the things I envisioned. And two, I felt I needed a keyboard on my phone. I tried TMobile for 47 frustrating hours and got a bill for over $80 for dozens of missed text messages – not delayed, but missed, and several calls that went to voicemail even though I can see their antenna from my backyard. I ended up unlocking a TMobile phone and I run that on AT&T – experiencing the world at 2G speeds. When you iPhone people complain that AT&T has throttled you – that’s the speed I run at, 24×7. :) I have contemplated bringing a Desire Z down from Canada.

However, the Android experience is a bit different than iPhone and it was difficult for Paul, my brave alpha tester, to explain how iPhone apps normally worked. After actually sitting down with an iPhone and working with it, several UI issues cropped up due to me porting Android ‘feel’ to the iPhone. After some rework, I have an app that feels more like an iPhone app but still maintains a fairly good parity with the Android version. There is no substitute for actually using the platform you’re developing for. While I wouldn’t switch to the iPhone, the UI/UX is very nicely done.

TestFlightApp – absolutely invaluable. After getting the app to a point where I felt it was worth sending to Paul to test, a few hoops and stumbles and we’ve got an .ipa that can be deployed. Paul downloads the app and the first test text from a phone is a success. It takes a little time to wire up the SDK from TestFlightApp and navigating their interface is a little cumbersome, but, we are collecting data and getting things working.

    SNAPlib *lib = [[SNAPlib alloc] init];
    [TestFlight takeOff:@"api key"];
    [TestFlight setDeviceIdentifier:[lib get_PhoneID]];

While they suggest that you use the UUID, I ended up writing a library function around the CFUUID which we maintain for persistence. Since I already had this function, I didn’t need to use the phone UUID class which is deprecated and is a blocker for new apps being submitted to the App Store. I haven’t used checkpoints, but, it is immensely easier than having Paul hook up DDMS and send me a chunk of logs.

TestFlightApp wraps exception handling, which put this in the console, making remote debugging a bit easier.

Unacceptable type of value for attribute: property = "event_auth_token"; desired type = NSString; given type = __NSCFNumber; value = 6629.
7 SnapReplay 0x000aabb3 -[srAppDelegate get_from_storage:val_name:def_value:new_value:] + 775
8 SnapReplay 0x000b1bf1 -[SNAPlib set_EventAuthToken:] + 121...

If you develop for iPhones, TestFlightApp is very nice. Thank you to Craig Agranoff for the heads up there.

All in all I found the experience to be quite good. Today marks day 75 since the genesis of SnapReplay and while I wanted to write the iPhone app much sooner, it is close.

With that said, if you have an iPhone or Android phone and would like to help test – even if you only take a few random photos to send to the beta stream, it would be appreciated.

Entries (RSS) and Comments (RSS).
Cluster host: li