Tuesday, May 27, 2008

Bulk Upload to Amazon SimpleDB

This weekend I was helping a friend with loading some data to Amazon's SimpleDB. The problem was fairly simple. He had a flat file with 170K lines of data. Each line represented a video from YouTube along with some metadata about the video. He wanted to turn that file into a "table" on SimpleDB, where each line (video) from the file would become a "row" in the table.

I decided to use Java for the task. I found a useful Java library for using SimpleDB. Some users of the library didn't like it, as it uses JAXB to turn Amazon's XML based API into a Java based API directly. That didn't bother me so I used it.

I wrote a quick program to do the upload. I knew it would take a while to run, but didn't think too much about it. I had some other things to do, so I set it running. Some three hours later, it was still going. I felt pretty silly. I should have done some math on how long this was going to take. So I scrapped it and adjusted my program.

Amazon has no bulk API, and this is the source of the problem. So you literally have to add one item at a time to SimpleDB. The best I could do was to parallelize the upload, i.e. load multiple items simultaneously, one per thread. Java's concurrency APIs made this very easy. Here is the code that I wrote.

import java.io.BufferedReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

import com.amazonaws.sdb.AmazonSimpleDB;

import com.amazonaws.sdb.AmazonSimpleDBClient;
import com.amazonaws.sdb.AmazonSimpleDBException;

import com.amazonaws.sdb.model.CreateDomain;
import com.amazonaws.sdb.model.CreateDomainResponse;

import com.amazonaws.sdb.model.PutAttributes;
import com.amazonaws.sdb.model.ReplaceableAttribute;

import com.amazonaws.sdb.util.AmazonSimpleDBUtil;


public class Parser {

private static final String DATA_FILE="your file here";
private static final String ACCESS_KEY_ID = "your key here";
private static final String SECRET_ACCESS_KEY = "your key here";
private static final String DOMAIN = "videos";
private static final int THREAD_COUNT = 40;

public static void main(String[] args) throws Exception{

List<Video> videos = loadVideos();
AmazonSimpleDB service =
new AmazonSimpleDBClient(ACCESS_KEY_ID, SECRET_ACCESS_KEY);
setupDomain(service);
addVideos(videos,service);
}


private static List<Video> loadVideos() throws IOException {

InputStream stream =
Thread.currentThread().getContextClassLoader().getResourceAsStream(DATA_FILE);
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
List<Video> videos = new ArrayList<Video>();
String line = reader.readLine();
while (line != null){

Video video = Video.parseVideo(line);
videos.add(video);
line = reader.readLine();
}

return videos;
}

// This creates a table in SimpleDB
private static void setupDomain(AmazonSimpleDB service) {

CreateDomain request = new CreateDomain();
request.setDomainName(DOMAIN);
try {

CreateDomainResponse response = service.createDomain(request);
System.out.println(response);
} catch (AmazonSimpleDBException e) {

e.printStackTrace();
}
}

// adds all videos to SimpleDb
private static void addVideos(List<Video> videos, final AmazonSimpleDB service) throws Exception{

// create a thread pool
ThreadPoolExecutor pool =
new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, 10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(videos.size()));
// Create a task for each video, and give the collection to the thread pool

for (final Video v : videos){
Runnable r= new Runnable(){

public void run() {
addVideo(v, service);
}

};
pool.execute(r);
}
}

// This adds a single item to SimpleDB

private static void addVideo(Video v, AmazonSimpleDB service){

PutAttributes request = new PutAttributes();
request.setDomainName(DOMAIN);
request.setItemName(v.getVideoId());
List<ReplaceableAttribute> attrs = videoToAttrs(v);
request.setAttribute(attrs);
try {

service.putAttributes(request);
} catch (AmazonSimpleDBException e) {

e.printStackTrace();
}
}

// Turns a video into a list of name-value pairs

private static List<ReplaceableAttribute> videoToAttrs(Video v){
ReplaceableAttribute author = new ReplaceableAttribute();
author.setName("author");
author.setValue(v.getAuthor());
ReplaceableAttribute date = new ReplaceableAttribute();
date.setName("date");
date.setValue(Long.toString(v.getDate().getTime()));
// for votes we pad so we can sort

ReplaceableAttribute votes = new ReplaceableAttribute();
votes.setName("votes");
votes.setValue(AmazonSimpleDBUtil.encodeZeroPadding(v.getVotes(), 4));
return Arrays.asList(author, date, votes);
}



}



And for completeness, here is the Video class:

import java.util.Date;


public class Video {

private final String videoId;
private final int votes;
private final Date date;
private final String author;
private Video(String videoId, int votes, long date, String author) {

super();
this.videoId = videoId;
this.votes = votes;
this.date = new Date(date);
this.author = author;
}

public String getVideoId() {
return videoId;
}

public int getVotes() {
return votes;
}

public Date getDate() {
return date;
}

public String getAuthor() {
return author;
}


public static Video parseVideo(String data){
String[] fields = data.split(" ");
return new Video(fields[1], Integer.parseInt(fields[0]), 1000*Long.parseLong(fields[2]), fields[3]);
}

}


Some interesting things... I played around with the number of threads to use. Everything seemed to max out at around 3-4 threads, regardless of whether I ran it on my two core laptop or four core workstation. Something seemed amiss. I opened up the Amazon Java client code. I was pleased to see it used a multi-threaded version of the Apache HttpClient, but it was hard-coding the maximum number of connections per host to ... 3. I switched to compiling against source so I could set the maximum number of connections to be the same as the number of threads I was using.

Now I was able to achieve much better throughput. I kept number of threads and max number of http connections the same. For my two-core laptop, I got optimal throughput for 16 threads and connections. For my four-core workstation, I got optimal throughput for 40 threads and connections. I think I will re-factor the Amazon Java API and offer it to the author as a patch. There is no reason to hard code the number of connections to three, just make it configurable. The underlying HttpClient code is highly optimized to allow for this.

Friday, May 23, 2008

PECS in Action

One funny bit at JavaOne was Josh Bloch introducing the mnemonic PECS. Well actually what was most funny about it was the picture of Arnold Schwarzenegger that accompanied it... Anyways, PECS stands for producer-extends, consumer-super. Instead of repeating what Josh said or plagiarizing Effective Java, I will give an example of how PECS helped me out yesterday.

One there was an API that existed back in olden times, before Java 1.5 It looked like this:

void runInboundCycles(final Module[] modules)

There was also a runOubound, but you get the picture. The class Module is an interface that has many implementations. This API got tweaked courtesy of Java 1.5:

void runInboundCycles(final List<Module> modules);

Before the change you could do this:

runInboundCycles(new Module[] { new MyModule() } );

A logical uprev would be:

runInboundCycles(Arrays.asList(new MyModule()));

Turns out that won't compile! The Arrays.asList call will return a List and the Java compiler says that is not a List. So instead you have to do something annoying like:

List<Module> modules = new ArrayList<Module>(1);
modules.add(new MyModule());
runInboundCycles(modules);

This is particularly annoying if modules is actually a member variable, as now you cannot declare it to be final. Enter PECS.

My API is using the Modules, thus my parameter is a producer to the API. Remember producer-extends, so refactor the API like this:

void runInboundCycles(List<? extends Module> modules);

Now you can pass a List<MyModule> and the compiler won't complain.

There are a couple of things about this that bother me. When my crazy brain looks at the API, it thinks "the API consumes Modules." Maybe that's just me. The other thing that bothers me is writing ? extends Module because Module is an inteface. Now granted it would suck to have to write ? implements Module just because Module is an interface and write ? extends Module just because Module was a class, so I am not advocating the altnernative. It just feels weird to write extends in front of an interface type. Maybe I have been programming in Java too long.

Armchair Architects

Funny post by @al3x about Twitter architecture. Oh wait it wasn't supposed to be funny, oops. The fact is that he should totally expect more people to diss Twitter and pretend that they could easily solve all of the problems. I am not just being cynical about people, they actually have some good reasons to do so:

  • Twitter crashes a lot. If your site did not crash so much, then people would not think you are an idiot and that they could easily do a better job. The people may all be wrong, but that does not matter. Why do you think Microsoft has come to have such a bad reputation? People do not care about what MSFT did to Netscape, Sun, or Apple. They care about BSODs. People hate Vista because Microsoft did not make it as backwards compatible with 3rd party drivers that did lots of bad, hacky things. But now Vista crashes, so people complain about MSFT.
  • Twitter seems simple. You put a 140 character limit on updates and what do you expect? Part of Twitter's appeal is its simplicity, but that same simplicity creates expectations and makes people think they could do it themselves better. Maintenance is expected for things that seem complex, like cars or Photoshop, but not for (seemingly) simple things like iPods or Twitter. If you think hard about it, Twitter is much more complex than it seems, but who wants to think hard?
  • Ruby developers are obnoxious. Oh this is my favorite. Ruby developers are a small but very vocal group. They love rubbing it in your face that Ruby is so much more expressive or object-oriented or whatever than anything else on the planet. The Rails sub-cult is even worse about this. So when the most high-profile Rails site starts failing constantly, you must expect a lot of smug developers to wag their fingers. It is kind of a shame that Twitter is paying for DHH's bad karma ... but then again @blaine did make that infamous claim about how easy it was to scale Rails. Of course he's gone now, but there is still enough bad karma to go around. How many Ruby developers would admit how bad their software is? Think about that.

Wednesday, May 21, 2008

SVJUG: JPA 2.0

Last night I went to the monthly SVJUG meeting. Patrick Linskey from BEA, err Oracle, spoke about JPA 2.0. First off, Patrick is an excellent speaker. I belive he was acuired by BEA via the SolarMetric acquisition, and now he's been acquired by Oracle. If nothing else, he is a much better speaker than anyone I've ever heard from Oracle. There were two very interesting things I saw in JPA 2.0.

The most interesting was query introspection. To me this move really makes it possible for JPA to get "pushed down the stack" if you will. I think it will allow for more abstracted frameworks to use JPA and hide it from the programmer. Working recently with Grails made me realize why this is important. Grails adds many JPA EntityManager methods to the domain classes. In many cases, you don't even have to call these methods. A typical Grails crud action does something like def myDomainObject = MyDomainObject.get(params.id) and then myDomainObject.properties = params and that's it. There is no explicit call to save() or persist(), etc. One could argue that this is a very good thing, as code like entityManager.persist(myDomainObject) is clearly boilerplate.

Now it would be pretty hard to get the similar functionality in Java, as you cannot add methods to objects at compile-time. You could make your domain objects extend an abstract class, but we are all too in love with POJOs to allow for that. However, a container of some sort could do these things for you. Introspection APIs into the JPA are a key to such a thing working. I don't know if the ones being added in JPA 2.0 are sufficient, or if that is even what the JPA folks have in mind, it's just an interesting possibility I see.

The other interesting addition to JPA 2.0 is adding a Cache interface to the spec. This is a nod to the implementers (like BEA's Kodo) that all use some type of "second level" cache. The API simply allows you to evict things from the cache, which seems reasonable enough. Cache invalidation is one of the hardest things out there, so it is nice to have explicit APIs for doing this. 

JPA is a topic close to my heart. I started working with Hibernate about six years ago. In some ways there are two philosophies out there when it comes to web-scale systems. One school of thought rejects relational databases. Take a look at Google's Big Table, Amazon's SimpleDB, or Ning's Content Store for examples of this. The other school embraces the relational database and says that they can be scaled. If you saw the eBay presentation at JavaOne, then you know what school eBay belongs to. I think that other RDBMS believers include Yahoo and Facebook. Historically ORMs get in the way of that scaling, and JPA is no exception. I'm not quite ready to give up on them yet.

American Idol

Yes another season of American Idol will finally end tonight. I think the show is really showing its age, of course I've thought the same thing for a couple of years now. Last night's "showdown" demonstrated the evil of the show. It is a show powered by the music industry, you know the same guys who like to sue kids for downloading music, etc. Thus there is evil in its core, and it really came out last night.

Before the show, my wife asked me who I thought would win. I told her that I thought David Cook had the edge. He was more original and a better performer. I thought that David Archuleta was probably a better singer, but was so immature and annoying. He is unable to sing anything but slow songs, and I thought that people had started to catch on to this, including the judges. Ah, but I should have realized that evil that lies in the heart of men and known that these were the reasons that David A. was assured of victory.

Amazingly David A. performed three ballads. I thought this was his best tactic, but that surely he would catch a lot of criticism for it. Nope. Instead he was praised for song choice! In other words, he was praised for embracing his limitation. Even worse, when given the choice of any song to perform he recycled a song from earlier in the season.

David A. played things as safe as he possibly could. He took no chances at all. That is fine, but how many times have we heard the judges blast contestants for this? Not this time. Instead he was praised. To make it even worse, his opponent was criticized for not doing the same thing: Simon Cowell told David C. that he screwed up by not recycling a song.

It was all complete hypocrisy that reeked of an agenda. The evil empire is clearly at work here. The actual performances did not matter. The judges completely contradicted everything they've ever said in the past in an effort to promote one contestant over the other. It will surely work as well.

Don't be too upset, though. The evil empire is falling apart. The internet put a stake in their heart a long time ago. Go listen to some NIN or Radiohead on your iPod and laugh at the music industry. One day MBA students will study them as a perfect example of how to ruin a business by being too conservative and afraid of (technological) change. It's the same short sighted, greedy principles that worked against David C. last night that ruined a huge, billion dollar industry.

Sunday, May 18, 2008

How do You Use Generics in Java?

Cedric has fired up some more flames in the never-ending dynamic vs. static language wars. I won't bother with all of the obviously flammable objects in that post, but instead with this quote that I found really surprising:
90% of the Java programmers (including myself) only ever use Generics for Collections.

Is this true? It's not for me, obviously or I wouldn't have been surprised by it. Generics are so useful beyond collections. One of the most commonly implemented interfaces I deal with looks like:

interface Service<R,S> {
   S processRequest(R request);
}

This is a very simple use of generics to add type safety to a common construct. What would this look like without generics? Either everything is an Object, or there are some wrapper interfaces that would have some kind of getData() method that returns ... an Object. It's not type safe but nothing else could be reusable. 

A lot of people think that Java took a wicked turn with the 1.5 release, and generics are probably the biggest reason for this. To me, the key was if you had programmed in C++ before or not. If you had, then generics seemed natural. It was definitely not the same as C++ templates, but the concepts were similar and the syntax very similar. If you had not experienced C++ templates, then generics were terrifying. I would guess that camp probably would agree with Cedric's post, i.e. they only use them for collections.

Of course the next great scary Java feature is closures. The BGGA proposal leverages generics to specify checked Exceptions, such as below:

public static <T,throws E extends Exception>
T withLock(Lock lock, {=>T throws E} block) throws E { ... }

Even scarier? Maybe so... Anyways, I am genuinely curious how other folks use generics. Is it just for collections?

Tuesday, May 13, 2008

Travelers Insurance Rip-Off

Time for a quick rant...

A few months ago, my car was hit. It was parked at work. I actually got an email on my Blackberry from eBay Security grimly stating "contact us as soon as possible about your car." That could only be trouble, and it was. 

The gentleman who hit my car was kind enough to leave a note. He was at eBay on business, and the car was a rental paid by his company. His company had insurance through Travelers Insurance. They contacted me, sent out somebody to assess the damage, and told me to take the car in to the repair shop of my choice. They instructed me to have the repair shop deal with them directly, so no money would come out of my pocket. They also told me I was entitled to a rental car while my car was in the shop.

So I followed their directions. They actually suggested I just go to the local Volkswagen dealership. The local dealership did not do body work, but recommended a body shop to me, Michael J's. So that is what I did.

A couple of weeks later, I get a call from the rental car company that I used while my car was in the shop, Enterprise. They said that Travelers was refusing to pay for the rental car. I contacted Travelers and they claimed that the repairs took longer than they thought they should, so they would not pay for the rental car. 

So in recap, my car gets hit in the parking lot, and I wind up being out $$$ thanks to Travelers Insurance. If they were my insurance company, I would simply drop them. But they aren't. So at the very least, I'm ranting about it on my blog. That's what blogs are for, right?

New Music 2008

There has been some pretty good music to come out this year. Here is what I have been listening to.

Attack & Release by The Black Keys : This album was an immediate hit for me. It is hard not to draw comparisons to The White Stripes. White Stripes + Black Keys = piano keyboard? This is a great blues/rock album. Favorite tracks "I Got Mine" and "Strange Times."

The Seldom Seen Kid by Elbow : I really did not like this album at first. It really took several listens for it to grow on me. I think part of the problem is that I really do not like the first song "Starlings." The album is really quite good besides that song. In particular I like "Grounds for Divorce."

Accelerate by R.E.M. : Wow, R.E.M. is still going! I bought every R.E.M. album when they were released, starting with Document in 1987 and ending with Reveal in 2001. That's eleven years and eight albums. Reveal was not good. I did not buy Around the Sun in 2004. I was quite skeptical when I heard about the release of Accelerate, but it is a good album. I really like "Living Well is the Best Revenge" and "Accelerate". 

Consolers of the Lonely by The Raconteurs : I love the White Stripes, but I really did not like the first album The Raconteurs. It was underwhelming. This one is much better. I still sometimes wish that Jack White would just take over and turn it into a White Stripes album, but it's usually a "this is good but ohh, how it could be better" kind of thing. Favorite songs are the title track and "Old Enough."

Vampire Weekend by Vampire Weekend : This album took awhile to grow on me, too. It was one of those albums that sounded much better in my car than on headphones. Pitchfork says they are pop not rock. I don't agree. I think they are a little too clever to for pop, and maybe too catchy for "alternative"? Who knows. It is a fun and original album. Favorite songs "Campus" and "Oxford Comma."

The Slip by Nine Inch Nails : Oh, NIN released an album? Well actually they released Ghosts as well, but I must admit that I haven't listened to that much since I downloaded it. The Slip is amazingly good. The only bad thing is that by releasing so much music, Trent Reznor is making it seem too easy. For me, this album really clearly defined the post-Fragile NIN sound. It obviously started with With Teeth, but The Slip has made it crystal clear. Best tracks "1,000,000", "Discipline", and "The Four of Us are Dying."

Friday, May 09, 2008

Flex at JavaOne

Earlier today I attended a talk that introduced Flex and showed how it could be used with Java. It really didn't work all that well, mostly because there was too much information. I think it was too much for people to digest. 

Currently I'm watching talk where they are building an app with Flex, JSF, and WPF (Silverlight?) The speaker is developing live on stage. This seems dangerous, but it is going quite well. It is barely tapping the potential of Flex, but is very convincing. An interesting comment was that Flash is limited because it cannot do any 3D. Of course this comment is coming from a NVDIA engineer, but it is a valid point. 

It is very nice to have so many non-Java related talks at JavaOne. The content of JavaOne has really aligned well with the interests of the attendees. I think next year it will be even more so. I predict that next year the dynamic languages will take over and command the biggest audiences. 

JavaOne 2008

I've been at JavaOne all week, but not blogging. That is partially because of the horrible Wi-Fi at JavaOne this year. Luckily it is good today, so I am blogging before Josh Bloch's Effective Java talk. I also just picked up the new edition of Effective Java, as if I could somehow read the whole thing before the talk ... Probably should have waited to buy it at the end of the day since I won't be reading it until the train ride home. Anyways...

So what's been good at JavaOne this year? Well ... no big news, really. We see more meat on JavaFX and I guess that was the lead story at the opening keynote. It seems like JFX is about where Silverlight was a year ago. The JVM 6, update 10 does rock. You can do so much more with Java than with Flash or Silverlight, regardless of JFX, so it will be interesting to see if people leverage that.

Along those lines, the most interesting session I saw this week was by the guys from Ajaxian, Dion Almaer and Ben Galbraith. Their talk was titled "What's new in Ajax." The subject matter was interesting, but that's not what made their talk so awesome. They are outstanding speakers, and they seem to have a great rapport between them. Their talk seemed very conversational. It also definitely stood out for NOT having to follow the Sun presentation template. It was a Keynote presentation full of cool graphics, animations, and even some short video interviews with some of the top dogs in the world of Ajax frameworks. 

They also did two cool demos, one with Fluid. I fee like such a nov for not having seen this before. It is awesome. They also did an awesome demo where they "threw" a dart at a dart board. The dart board was an Ajax app running the in the browser. The dart was a Wiimote! They used the Bluetooth connection of the Wiimote to send to the PC, and then bridged the Bluetooth stack on the PC to the browser using (drum roll please) a Java applet. The applet was just for communicating with the OS, all of the graphics were in HTML and the interactivity was JavaScript. Very freakin' cool. 

The other good session I went to was Alex Miller's talk on design patterns. Most of this was stuff I was familiar with, like problems with the Singleton and Visitor patterns. What was interesting to me was that Alex showed how closures could dramatically change the implementation of some of these patterns. He showed this for a template pattern. Both template and strategy patterns are kind of obvious targets for refactoring with closures. What surprised me was how a visitor pattern could be refactored using closures. I was inspired to start playing with design patterns in Scala, since that is the future of Java (in my opinion.) I tweeted this to Alex, and he pointed me to some writings on that exact topic. I still plan on doing my own exposition, as I think it will be fun.

Tuesday, May 06, 2008

Dynamic Language Performance

A couple of days ago, I read Charlie's post explaining the performance boost seen in Groovy 1.6. Reading stuff like this always leaves me with a great feeling. Not only do you learn something, but it makes other things make more sense. It brings order to chaos, or something like that. Around the same time I read that, I was working a new article about Grails, so the Groovy angle was particularly interesting. I love benchmarks, so it was time to have some fun.

I wrote a Groovy version of the same Ruby code I had used to benchmark JRuby. This was an extremely straightforward port. I was amazed at just how similar Groovy's syntax is to Ruby. Here is the code:


def expo(n,p){
def r = n % p
def exp = 0
def div = p
while (r == 0){
exp += 1
div *= p
r = n % div
}
return exp
}

def factor(n){
def factors = new java.util.HashMap<Integer,Integer>()
def s = n * 0.5
def p = (2..s).toArray()
p.each{
if (it) {
def r = expo(n,it)
if (r){
factors[it] = r
}
def val = it*2
while (val <= s){
p[val -2] = null
val += it
}
}
}
return factors
}

def numDivisors(n){
def total = 1
factor(n).values().each{
total *= (it+1)
}
return total
}

def n = 2
def num = 1
def max = Integer.parseInt(this.args[0])
def Integer triangle = 0
while (num <= max){
triangle = n*(n+1) * 0.5
num = numDivisors(triangle)
n += 1
}
println(triangle)


Anyways, here is the chart.

There is definitely a performance boost for long running processes where JIT'ing can happen more easily in 1.6. It was not as dramatic as I thought it might be, but it is there. Of course this is just one silly benchmark that is heavy in integer math, so take that for what it's worth. 

I also compared Groovy and JRuby. This was also surprising: 



Pretty close! Groovy seems to start-up a little slower, but pulled ahead slightly on bigger tasks. Perhaps the apprentice has overtaken the master.

Also, just for kicks, I tried out Scala. Here is the code:


import scala.collection.mutable._

object Euler12{
def expo(n:int, p:int):int = {
var r = n % p
var exp = 0
var div = p
while (r == 0){
exp = exp + 1
r = n % div
div = div * p
}
if (exp == 0 ) 0 else (exp-1)
}

def factor(n:int):Map[int,int] = {
var factors = new HashMap[int,int]()
var s:int = (n/2) + 1
val p = (2 until s).toArray
p.foreach( (num) => {
if (num > 1){
val r = expo(n, num)
if (r > 0){
factors.put(num, r)
}
var i = num*2
while ((i-2) < p.length){
p(i - 2) = 0
i = i + num
}
}
})
return factors
}

def numDivisors(n:int):int = {
var total = 1
factor(n).values.foreach((num) => {
total = total * (num+1)
})
factor(n).values.foldLeft(1)((p,m) => {
p * (m+1)
})
}

def main(args:Array[String]) : Unit = {
val t = new java.util.Date()
var n = 1
var num = 1
val max = Integer.parseInt(args(0))
var triangle = 3
while (num <= max){
triangle = n*(n+1)/2
num = numDivisors(triangle)
n = n + 1
}
println(triangle)
}
}


This turned out to not be fair. Scala's performance is exactly on par with Java and thus blows away JRuby and Groovy. 


I guess that is what happens when you have a language written by a guy who once wrote javac... Actually I would guess this is mostly a function of the static typing in Scala. It certainly bodes well for initiatives to bring features of Scala, like (BGGA-style) closures and type inference, to Java. It seems possible to implement all of this with no impact on performance, even on a JVM that has not been made to support such features. 

Saturday, May 03, 2008

Twitter Me This

No Twitter running off the Rails discussion tonight. One reason I write about Twitter is because I really value the service.  It was particularly useful to me today.

I took my oldest son, Michael, Jr. to Maker Faire today. We left right after lunch. I set Twitter to deliver messages via IM, which for me means Google Talk. I have a Google Talk client on my Blackberry, so all updates went to my phone via IM. I did a "track #makerfaire". Just as I was about to hit the road I see tweet saying how bad traffic was on the 101 near San Mateo, where Maker Faire takes place. I also see a tweet saying the best way to avoid the street traffic was to take the Hillsdale Blvd. exit to Saratoga Drive, where there is free parking. These were not tweets from people I follow, but from people going to Maker Faire, and they were right on. So I took 280 to 92 instead of 101, and used the Hillsdale Blvd tip to find free parking. I got to see a parking lot on the freeway near the San Mateo fairgrounds, as well as on Delaware Avenue (where most people got off the freeway) getting onto Saratoga Drive. I did not have to deal with any of that traffic. Thank you Twitter!

Not long after I got home from Maker Faire, I checked Twitter and saw the first mention of Microsoft withdrawing their bid for Yahoo. I had turned off my Blackberry setup, but immediately change my settings back to IM and turned on iChat on my MacBook. I did this so I could track Yahoo and Microsoft on Twitter. All I can say is ... wow. It was amazing to watch the collective conscious of ... well at least Silicon Valley ... react to such surprising news. Now I'm not going to exaggerate, most of the tweets were redundant and few had any particular insight. That is not the point. Crowd sourcing may be great for traffic info, but not business and technology analysis (just ask a communist survivor!) But it is fun to see how some people were relieved, while others were disappointed because they knew that YHOO stock was doomed to plummet on Monday. 

EclipseDay

I will be speaking at EclipseDay next month at Google. I will be talking about how we use Eclipse at eBay. I am going to try to demo and show off eBay's highly customized Eclipse-based development environment. Of course anytime you do a demo, you are at the mercy of the demo-gods! Hopefully they will be merciful.

Friday, May 02, 2008

As the Bird Turns



Another week, another Twitter outage ... and a new round of technology questions and rumors. TechCrunch now thinks that Twitter is abandoning Rails. This time out, Arrington attempts to be a little more fair-and-balanced than when he wrote about Blaine leaving. He points other sites that claim to have scaled Rails. This was of particular interest to me, so let's take a look.

Scribd -- Slide 7 claims three databases! Uh oh, is DHH right and I'm going to have to eat crow? Well maybe, but not because of Scribd. They only use master-slave relationship, but cleverly offload expensive queries to the slaves. Still only one place to write data. When (if) their data gets too big for a single database, they are going to be the ones singing "bring that beat back!"

Friends for Sale -- I could write a lot just about these guys and how ... umm ... interesting their setup is. I'll just quote them: "The most important thing we learned is that your scalability problems is pretty much always, always, always the database" but "on the database side we're still with a monolithic master and we're trying to push off sharding for as long as we can." They still have no problem claiming that "The whole 'but does Rails scale?' discussion sounds like a bunch of masturbation - the point is moot." You can't make up stuff like this!

Monday, April 28, 2008

Web 3x and Web Lite

Earlier today, I read this interesting article about the growth of web pages. In short, average web page size has tripled in the last five years.
Yes this swell in page size corresponds very nicely with everyone's favorite cliche, Web 2.0. It is obviously not a coincidence. More features and interactivity is going to be mean more initial download, which is the very coarse metric being used here. What may have been four web pages may now be one Ajax-ified one, whose weight is three times as much. More interesting tidbits.

  • Average page weight: 310 KB
  • Average total JS: 68 KB
  • Average # of external JS files: 7
  • Average # of unique external JS files: 6 (gotta love duplicates!)
  • Average total CSS: 15 KB
  • Average total image pixels: 49,144
  • Percentage of HTTP requests dedicated to images: 75%

That last stat is not really as bad as it seems. Images are (can be) loaded in parallel. There is a limit of connections per domain, however (generally two.) A common trick is if you have to load 20 images all from the same server, trick the browser. Make the first two images from images0.mydomain.com, the next two from images1.mydomain.com, etc. Obviously images0 and images1 point to the same IP address, but you get the gist. The browser will load all of the images in parallel (or close to it.) The one disadvantage is you can lose some browser caching. The browser will think that http://images0.mydomain.com/foo.gif and http://images1.mydomain.com/foo.gif are different images.

The other interesting thing talked about in the article, is that broadband speed has more than kept up with page bloat, err Web 2.0. However, not everybody has broadband, and you are basically suffering now more than ever if you do not. These studies are only talking about home users, what about mobile phones? If you are an iPhone user on EDGE, how do you like 310 KB pages?

It seems likely that we will start seeing "lite" versions of websites in the future. We kind of see this for mobile devices, i.e. m.mydomain.com or mobile.mydomain.com. At some point, will we start directing dial-up users to the lite sites? If you were a dial-up user, how would you feel about that? I'm from the South, so it kind of feels like segregation to me: "equal but separate." 

Sunday, April 27, 2008

My JavaOne 2008 Schedule

Here is my schedule. I will probably miss a few of these, that always seems to happen. In particular, I may have to miss the opening day(5/6) because of an important project at work. 

Thursday, April 24, 2008

Diophantine Equation

Earlier today I solved Problem 31 on Project Euler. The problem is to find the number of ways of making change for 2 British pounds (or 200 pence) given 8 types of coins. For a mathematical person like myself, the problem reduces to a Diophantine Equation:

a + 2b + 5c + 10d + 20e + 50f + 100g + 200h = 200

Where 1,2,5,10,20,50,100,200 are the values of the various types of coins. I had some meetings to go to, so I wrote a brute force solution (in Ruby) and let it run. I got back an hour later, and it was still running. I felt quite stupid. I then came up with a much better solution using some dynamic programming and recursion. It ran in 0.23s. Here it is:


def solve(n,coefficients)
x = coefficients.pop
if (coefficients.length == 0)
if (n % x == 0)
return 1
else
return 0
end
else
d = (n/x).to_i
cnt = 0
0.upto(d){|y|
xc = coefficients.collect{|p| p} #must copy since we pop array
cnt += solve(n-x*y,xc)
}
return cnt
end
end

c = [1,2,5,10,20,50,100,200]
n = 200
puts solve(n,c)

Wednesday, April 23, 2008

Why Johnny Can't Scale

Today TechCrunch "reported" that Blaine Cook has left Twitter. I had the pleasure of interviewing Blaine and his fellow Twitter developer Alex Payne last year. I was working on a book about Ruby on Rails, so they made the perfect people to talk to. After all, they had written a Ruby on Rails application that was being truly pushing the scalability limits of Rails. I was most interested in those limits and how they were attacking them. Let's get back to the present though.

Michael Arrington pins a lot of Twitter's notorious instability squarely on Blaine. He has a point. He points out that Blaine did a presentation at a Rails conference on how Twitter had scaled Rails. If you go out and say "here's how to scale Rails", your app is part of your proof. Facebook can go out and tout the scalability of PHP, MySQL, and memcache. You can dispute their rationale, but their results are hard to argue against. Blaine could have a great rationale behind how they scaled Rails, but the instability of Twitter discredits any argument.

Some of TechCrunch's reader object to this. They point out that Twitter only had three developers. Others say only ignorant non-programmers would blame Rails or Blaine. Maybe they are right. Here goes my explanation.

Take a look at that presentation that Blaine did. In particular, look at slide 3. I quote "180 Rails instances (Mongrel). Growing fast." That is essentially 180 web servers. That is 180 instances of the Twitter application serving requests made by Twitter users. Keep in mind that this was a year ago, so you can only imagine what the numbers are like now. Now take a look at the next line in that slide "1 Database Server". No mention of that growing. I won't claim to know the intricacies of Twitter's operations, but this is an obvious bottleneck.

Since then Blaine & co. did a lot to alleviate the pressure on their bottleneck. They created an innovative messaging system called Starling. They made heavier user of memcache. To my knowledge, they still have that 1 Database Server.

If you are familiar with Rails, then you know that this is a flaw in Rails (a flaw in ActiveRecor to be more precise.) RoR associates a class to a database connection. You can write code that alters this behavior, but it is very hard to do this efficiently. Now let's be fair. This is true of a lot of frameworks, maybe all of them. Java practitioners like me know that our favorite technology with similar functionality, Hibernate, has the same flaw. Google has kindly produced an extension, Shards, to address this. Certainly the general JavaEE specification does not address this.

So it's not just a Rails problem, and we shouldn't blame Rails, right? Not so fast. Rails is the poster child for rapid, high productivity development frameworks. Remember how TC readers pointed out that Twitter only had three develoeprs? Rails is a big part of that story. In general, Rails is one of many technologies that seek to simplify web development as much as possible. This allows three developers to build such a popular site, but that's also the problem.

It's easier than ever to build a website. Social media makes it easier than ever to gain eyeballs. But it is just as hard to scale your site to handle massive traffic. Actually, maybe it is harder. When you rely heavily on frameworks and out-of-the-box goodness, it is that much harder to rip it out or reject it when you outgrow it. Rails magnifies this problem, because it is not just a technology, it is a philosophy. Rails developers pride themselves on the elegance and terseness of their code. If you are already writing ugly code, it's a lot easier to throw it away and write code that is an order of magnitude uglier but solves your scale problems. When you write beautiful code and don't know of a beautiful way to solve your scale problem, what do you do?

Finally, it is easy to trivialize Twitter's problems. "Oh they just need to use PHP" or whatever. Think about their application a little more before you say that. Let's think about a really naive approach to their application. You have a User who creates Updates. One User has many Updates. For example, I am a Twitter User and today I have made three Updates. I follow 65 other Users. The naive approach (also the one a DBA would tell you to use) is to have a join table to associate who you follow. So what has to happen when I load my home page? First, we need to figure out the 65 people I follow. Next we need to get their Updates. How many of their Updates? Well there are 20 Updates shown, but they are the 20 newest across all of my 65 friends. So do you grab all of the updates from all of my friends and then sort it? That is certainly the most naive approach. In that naive approach, we need one query to get my friends and then N queries to get all of their updates. We can put a sort on those N queries and then do a merge sort, quitting once we get the first 20 potentially. Still each of those N queries could be returning a lot of data. We all follow @Scobleizer, right?

So even if we only needed "1 Database Server", things are non-trivial. What about splitting your database? Obviously you want to split the Updates table. Let's think about your home page. Ideally all of the Updates on that page would be in the same database, so all of your friends need to be the same database server. But what about the other people following your friends? You see where this is going. The Kevin Bacon six degrees of separation problem is going to kick your ass.

The point is that even though Twitter might seem simple to the outsider, it is not. It is complex. Even if they didn't use Rails, it is non-trivial. Rails definitely does not help, at least in some regards, and that's the real story. It may be easier than ever to build something and, perhaps because of social networking, easier to get an audience. But all of the syntactic sugar in the world doesn't make it any easier to scale an application.

Tuesday, April 22, 2008

New Scala Article

IBM published my first Scala article today. It's on using Scala to work with XML. It was a lot of fun to write about Scala. I am very lucky to get to share something that I enjoy. However, it was also a little challenging. It is so easy to be so terse with Scala that it seemed to carry over into my writing. I thought it would have the opposite effect, i.e. I would feel like I would need to write a lot more to describe what was going on with Scala code than I would with Java. Instead I had to really force myself to be descriptive about things. I would look at some of the code and just think: "the reader does not need me to explain that, it is so obvious." That is a testimony to Scala. Its handling of XML takes advantage of its DSL-friendly nature. A lot of complex and powerful expressions in Scala simply do exactly what they look like they should do. Or maybe its just my mathematical mind that sees things that way, who knows. Anyways, I have another article in the works for developerWorks that will cover Lift, a web framework developed with Scala.

Monday, April 21, 2008

Video Solution

Last week my oldest son, Michael, Jr. was in a musical production at his preschool. Of course we videotaped it so we could share it. This wasn't the first time I had shared video online, but this time really made me annoyed. I was really annoyed with the poor quality of sharing sites like YouTube. I tried Facebook's video sharing, and it wasn't much better. I decided it was time to roll my own. After some experimenting here is the stack of tools and service I came up with.

Video Importing/Editing: iMovie. This is what I was already using. iMovie is famously easy to use. The importing is easy, the editing is easy. The default export is .m4v files, which are H.264 files designed to work on iPods. This is important.

Video Playing: Flash. I wrote my own video player using Flex. The Flash player has built-in support for H264 video. Thus the videos I export out of iMovie play naturally in the Flash player. They just need some Flash code to load and control the video stream. I wrote this myself as it was pretty straightforward. I created an external XML file as a metadata repository. This tells my player what videos are available and information about each video, like its size. I hosted the custom built player and metadata files on Google Page Creator.

Video Storage: Amazon S3. This was the most difficult part. S3 is relatively cheap. It is NOT easy to use, contrary to what others might say. I was shocked to discover that there is no interface (web or desktop) for uploading and managing files stored on S3. I wound up using S3Fox. It seems like a nice interface, but buggy. Amazon really should offer administrative tools.

Next Steps: Ideally I would build a plugin for iMovie that would upload the video to S3 and write the metadata about the movie to my Google Pages. I am thinking of doing an AIR app for this first, and then maybe go Cocoa. I must also monitor my usage to make sure that S3 is cheaper than other alternatives like Box.net.

Saturday, April 19, 2008

NBA Playoffs

I am very excited about the NBA playoffs, and for one very simple reason...


Go Magic!

I went to my first Magic game before they had played a regular season game (it was a pre-season game in Tallahassee.) I've lived through the betrayals of both Shaquille O'Neal and Tracy McGrady. Now we have Superman. Nobody is talking about Orlando, and that is perfect. 

Now I have to admit that Boston scares me. I really think Orlando can handle Detroit in the second round, but Boston... I also think Orlando could do well against any team in the West, but Boston... People don't seem to realize that Boston just had a historically good season. If Orlando wasn't so awesome this year, I would be rooting for Boston big time, as Kevin Garnett is one of my all time favorite players to watch.

Back in the West, everybody around here is pissed that Golden State didn't make the playoffs despite having a much better record than most of the teams in the East. It's a good reason to be upset, but Golden State controlled their own destiny. If they would have beaten Denver, at home on April 10, they would almost certainly be in the playoffs and Denver fans would be the ones pissed about the Atlanta Hawks being in the playoffs while their Nuggets were not.

Finally, here are some predictions:

East
First Round Winners: Boston(4), Detroit(6), Orlando(5), Washington (6)
Second Round Winners: Boston (4), Orlando(7)
Eastern Finals: Orlando (7)

West
First Round Winners: Lakers (6), Dallas (7), Utah (5), Phoenix (6)
Second Winners: Phoenix (6), Utah (6)
Western Finals: Utah (6)

Utah, like Orlando is much better than people realize. Phoenix traded for Shaq so they can deal with San Antonio and the Lakers, but they won't be able to deal with Utah. 

NBA Finals: Orlando over Utah (6)

Wednesday, April 16, 2008

eBay Wins MySQL Award

Yesterday eBay won an award at the O'Reilly MySQL conference. Congratulations to the Chris Kasten's team. Chris also did a presentation today at the MySQL conference. The caching layer his team created allows us to store "session" data on the server. This data can actually be persisted across sessions, depending on the use case, so it is really a little better than your typical HTTPSessions. Plus it actually scales :-)

The MySQL cache compliments HTTP cookies. We also use Flash for local storage as another alternative to the tiny world of HTTP cookies. Flash has a lot of advantages. The most obvious is the 100K default limit as opposed to the 4K limit of HTTP cookies. Flash "cookies" are not sent to the server, making them much more secure. One of the fun things I did recently was help setup guidelines on when an application should use plain 'ol HTTP cookies, Flash local storage, or the MySQL cache. I got to become much more familiar with the MySQL cache, and thus it wasn't suprising to me when I heard about it winning the award from MySQL.

Saturday, April 12, 2008

Yes to Bitterness

A lot of folks think that Barack Obama has really shot himself with his comments about "bitter" Americans:
"It's not surprising, then, they get bitter, they cling to guns or religion or antipathy to people who aren't like them or anti-immigrant sentiment or anti-trade sentiment as a way to explain their frustrations."
That's the quote that everybody cites. It incited Obamafan Dave Winer to take umbrage at Obama's quote and state that "To equate geography with intellect is as wrong as to equate it with race, ethnicity, gender or age." That quote is taken from a blog post by Winer titled Is my candidate too elite?

Before I state my opinion on the matter, let's get a few things out of the way. A lot of people would say that I am the worst kind of elitist: a libertarian. I think Republicans are stupid for wanting to tell people what is right and wrong and how to live their lives. I think Democrats are stupid for thinking they are smart enough to fix everyone's lives and treating people like children. In other words, I think everyone is an idiot. I am a supporter of Obama, but only after Ron Paul fell out of things and Obama became my best bet to ending the ongoing atrocity that is the War and Occupation of Iraq.

That being said, I also consider myself to have a very scientific mind. Thus Obama's quote must be examined in context. Taking quotes out of context is the worst kind of yellow journalism (and perhaps the most common as well.) Obama was explaining why he was having a hard time winning over white working class. He used the bitterness bit as a counter to the notion that it is just because he is black.

You might be wondering what my point is. This is the kind of argument that always annoys my wife. The semantics of the statement are what is important. Obama was not talking about how certain people (white working class in the midwest) are, but why they are skeptical of him as a leader. Look at the recent history of presidential campaigns. The Republican Party has done an exemplary job of using divisive issues to win elections. What are some of those issues? Things like religion and moral values (abortion), immigration, and gun control. These are the exact things brought up by Obama.

So was Obama saying that midwest white working class people are gun toting, racist, religious zealots? I don't think so. I think he was simply stating the fact that certain issues have been effective in deciding the votes of those people. That is not a stereotype or generalization, that is a statistical fact. In the past those people have been swayed by divisive issues by the Republican Party. Now we are seeing them swayed by divisive issues by Hillary Clinton. Obama is just relating these as the facts that answer the question posed to him.

Finally, my propensity for scientific thought forces me to denounce the hypocrisy of the outrage. It is publicly acceptable to call the people on the East and West coast elitists. Everyone else says that the "liberals" think everyone else are dumb. In other words stereotypes of people in "blue states" is ok, but stereotypes of people in "red states" are not. Why is this? Because there are more red states than blue. Might makes right. This is the reason why divisive issues have worked for the GOP. They can play all of the stereotypical, prejudiced cards they want because the people who will be offended are outnumbered. Well outnumbered from an Electoral College perspective at least...

Google App Engine ... PyNing?

There has been a lot of excitement this week about the Google App Engine, and it deserves the press. Most people are comparing it to the Amazon's triumvirate of S3, EC2, and SimpleDB. That is a fair comparison, but there is another one that came to mind for me: Ning.

Ning co-founder Marc Andreessen wrote a now famous (famous in SiValley at least) blog about the three kinds of platforms on the Internet. Andreessen claimed that Ning was the only Level 3 platform, where your code runs directly on the platform. Clearly the Google App Engine is also a Level 3 platform.

Ning and App Engine seem different, and they are. Ning has built an application called the Ning Social Network, which is an application that runs on the Ning platform. When you use Ning, you get the code for your own instance of the Social Network and then customize it from there. Can you rip it all out completely and build something from scratch? I don't know. But essentially that is what Google gives you: Ning with non Social Network app.

But wait, what about BigTable? Ning has a little thing called its content store which lets you store your own data structures and then query them ... a lot like BigTable. I am no expert on either, but any Level 3 platform has a similar need that is satisfied by these technologies.

Of course the other big difference between Ning and App Engine is that Ning is PHP based and App Engine is Python. That is also a major difference they both have from the AWS collection. They both support a single language with a set of advanced platform APIs implemented in that language. The other big difference is that both Ning and App Engine are free.

Personally I am thinking of writing a generic web proxy to deploy on App Engine. This would be useful for any Flash/Silverlight development to allow scripting to a site that does not have a nice cross-domain policy, such as ... Ning and Google :-)

Wednesday, April 09, 2008

Robbed

On Sunday, I took my kids to the Oakridge Mall. We often go there on Sundays to get ice cream and to play at the indoor playground there. That's exactly what we were doing this past Sunday. As we were walking to our car, I saw a terrible site. Our car had been broken in to. It was my wife's Toyota Sienna. Somebody had broken the driver side window so they could steal the DVD system we had in the car for the kids to watch. It was a portable system we bought over a year ago, with two monitors, one for each of our kids. One of the major selling points for us was that it was portable, so we could use it either in our minivan or in my car. Turns out that this was a selling point for thieves as well.

Needless to say I was pretty furious to have my car damaged and my property stolen. The thief was obviously an idiot. He forgot to take the power cord to the DVD system, so it will not even play for him. Of course the worst part was having to explain to my children what had happened and why they no longer had a "TV in the car."

One of the other disturbing things that came out of all of this was that it led Crystal to discover CrimeReports.com. We went on there and put in our address. It is truly disturbing to see how much crime goes on. It made me depressed to think that maybe we have picked a bad place to live. I scrolled around the Bay Area, and everywhere was pretty bad. Even very expensive areas like Los Gatos showed a huge amount of theft, burglaries, etc. in just the last week.

Tuesday, April 08, 2008

Twitter API in ActionScript

I noticed that the Twitter ActionScript API was a bit out of date to say the least. I had to update it for a small project I was playing around with. I sent a note to al3x about this, and suggested open sourcing the API. He agreed and so here it is.
It needs a lot of work. I basically removed the old authentication scheme, since it relied on setting the Authorization HTTP header and that is no longer allowed starting in Flash Player 9.0.115. This also let me remove a dependency on a third party base 64 encoder. I guess a base64 encoded string that includes your username and password is supposed to be secure :-) I also fixed the loadFriends method, as this now returns users not status messages. I will get the API up to date, but if anyone wants to help out then just send me a note.

Friday, April 04, 2008

MLB 2007

It's almost a week into the baseball season, and I haven't written about it at all! I am always psyched when baseball starts. This year is no different. Performance enhancing drugs be damned!
I am very optimistic about my favorite team, the Atlanta Braves. The Braves were really one of the top 2 or 3 teams in the National League last year. Their record did not show this, but their stats did. That makes them an easy pick to improve.
I am also optimistic about Jeff Francoeur. Jeff's big weakness has always been his plate discipline. He nearly doubled his walk total and rate last year. This was reflected in his increase in pitches per plate appearance. His home run total was down last year compared to 2006, but his doubles(40) were way up. It's easy to feel optimistic about a young player who is showing more patience and hitting a lot of doubles. Not to mention that he is hitting behind a pair of guys who regularly post .400+ OBP (Chipper Jones and Mark Texeira.)
So if the Braves can just get enough pitching out of their rather old rotation (Hudson, Smoltz, Glavine, Hampton) then it should be a great year. Contrary to popular belief, Turner Field is a hitter's park. People think it is great for pitching just because the Braves had so many great pitchers for so long. It's not. It's actually at the second highest altitude of any MLB park, next to Coors Field of course. So the Braves are going to once again going to score a lot of runs.
Finally, gotta mention the local teams. I don't know what to make of the Oakland A's. I think they will surprise, but then again expectations are so low that they almost have to. The Giants on the other hand are going to be miserable, and they deserve it. They were just a little too happy to get rid of Barry Bonds, the guy who built their current stadium and gave them a future as a big market team (once they finish paying for the stadium.) I do hope to catch a Tim Lincecum game though...

Wednesday, April 02, 2008

Long Division

Earlier this week, I solved another problem from Project Euler. This was #26. Here is the description:

A unit fraction contains 1 in the numerator. The decimal representation of the unit fractions with denominators 2 to 10 are given:

1/2= 0.5
1/3= 0.(3)
1/4= 0.25
1/5= 0.2
1/6= 0.1(6)
1/7= 0.(142857)
1/8= 0.125
1/9= 0.(1)
1/10= 0.1

Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle. It can be seen that 1/7 has a 6-digit recurring cycle.

Find the value of d < 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.

I knew this problem was related to the totient function, but I went a brute force route instead. The brute force route required writing a long division algorithm, which just seemed like a fun thing to do anyways. So I solved the problem, and the solution was plenty fast (0.185s for the calculation.)

I was ready to post my solution to the forums. I was also planning on reading the forums to see if anyone had a clever solution using the totient function. So I was greatly disappointed to see that the forms were down. Still my ego required me to publish my long division algorithm, so here it is:


def cycle(d)

m,i = 1,1
arr = Hash.new

while m > 0
while m < d
m *= 10

i += 1
end
m = m % d

if arr[m]
return i - arr[m]

end
arr[m] = i
end
return 0

end

max = 0
m = 2
3.upto(ARGV[0].to_i){|n|
c = cycle(n)

if c > max
max = c
m = n

end
}
puts m

Obviously this is in Ruby. The cycle calculates the length of the recurring cycle. The m variable is essentially the digits in the decimal representation of 1/d. The algorithm doesn't capture it, since it is not needed for the problem, but you could easily capture it (append to a string or array or whatever) and return it for a true long division algorithm.

Thursday, March 27, 2008

Beta Browsers 2008

This is a big year. Both IE and Firefox will release new versions this year. That hasn't happened ... ever? IE8 had its first beta, whereas Firefox 3 is at beta four. Indeed FF3 is much more polished at this point than IE8. However, I must say that Microsoft has been smarter than the Firefox team in a number of ways.

First, IE8 includes an improved IE Developer Toolbar by default. The IE DevBar is roughly equivalent to Firebug, but Firebug does not work with FF3. Of course you can’t really blame this on FF, but IE8 is much more web developer friendly. Web developers are the primary (only?) audience for beta browsers. IE8 also includes the IE7 emulation mode. Again this is very nice for web developers who have to program for IE7 currently, but need to get a head start on IE8 (wait, isn’t that all web developers?)