Sunday, June 20, 2010

As3 ArrayCollection Sort "Feature"

Consider my confusion when I setup the debugger:

this._listData.owner.dataProvider.getItemIndex(this._data) = -1 [0xffffffff]
this._listData.owner.dataProvider.getItemAt(11)===this._data = true

As a background you should know that this._listData.owner.dataProvider is an ArrayCollection. Based on what I know about "equality" in as3 the above is an impossible situation.

this._listData.owner.dataProvider.getItemAt(11)===this._data

That means that the 2 reference points to the same object. So if the 11th element is same that this.data why is it not found in the collection?????

Well the explanation is filed as a bug @Adobe. The problem is that the bug is more like an undocumented feature:
If you set a sort on the collection and the sort's comparator has a bug or simply does not pass the equality check for the same object(sounds like a bug) than you get to see the above.

In my case the trials are not over. The comparator's equality does work...

After hours of testing and heated debates..I spare you details... here is the explanation:

Our sort comparator used a mutable variable that described sort order ASC or DESC. Because the variable was mutable, it was changed by other things in the application. Leading to a an unpleasant and hard to debug problem:
  1. We create a sort function and assign it to an ArrayCollection.
  2. We add records to the ArrayCollection and call sort.
  3. Change the sort order variable.
  4. Call getItemIndex(this._data) and get -1
The problem is that the internals of the Collection assume that collection is sorted(it is not sorted by the sort as of #3). It tries to walk a tree(I assume after debug) using the comparator, but because the tree was created with a different sort function the tree walking (search) will fail.

Lesson #1: It is very important that any data that is in the sort function that is not passed as itemA/itemB can not change. If it needs to change recreate the sort/comparator. 

Lesson #2: Use a sort/comparator if you want to modify the default behavior of getItemIndex(). If you want to allow for customizing equality, use a Sort object on an ArrayCollection.  

Sunday, February 21, 2010

No RPC for Async IO. A case for Flash As3 Thrift Server over HTTP Full Duplex.

Recently I was playing with Thrift in the hope of replacing BlazeDS. Using an existing patch I was able to make a Thrift client in AS3 and send messages to Java, even get return values. However I faced a few strange things:


  1. Flash unlike Java does not support blocking IO for network operations( Java in the other hand has no async IO like Flash, not until 1.7 nio2). At the same time Flash also have no concept of threads.
  2. Any RPC call I made from flash to java, needed callback handlers in Flash, to handle the return values.
In traditional Java remote calls, even most HTTP based remote call we seem to make a fundamental assumption that the client is blocked waiting for response until the server processing is complete. That assumption falls apart in Flash.

The basic As3 Thrift Http client creates a new http call for each method invocation. The flash clients send the request, and moves on. To illustrate the problem:

In Java:
  1. call service A, wait until returns Foo.
  2. call service B and send Foo.
In Flash:
  1. call Service A, add method gotResponse as subscriber for the return value, move on and do other things.
  2. gotResponse called with Foo, call serviceB with Foo. 
Be Java or Flash if your application has many small messages this is somewhat wasteful. The HTTP headers often times are bigger that thrift payload. Opening and closing all these connections have a fixed cost overhead.

In my previous post I talked about using Http Chunk encoding, to reuse the open socket, HTTP request.

The problem is however that Thrift messaging API, is built with the assumptions of blocking io. The clients assume exclusive use of the transport, multiple clients using the same transport is kinda tricky to implement, since the transport does not know when one message starts or ends.

The simple way to solve the problem is to serialize access to the RPC client. The problem with serialized access is that it takes a long time.

Than I noticed oneway message and I think my problems are solved:


See instead of having callback methods for each RPC invocation, in an environment with Async IO we should just use one way messages.

In my case Flash as client sends one way messages to Java server. Once a message is processed on the Java Server side, Java will send one way messages to the Flash server.

Having mutexes on the oneway messaging clients only serializes message sending, which is an improvement over having to wait for a response before sending the next messages.

In addition this method easily supports sending events from a Java server to a flash client, without the client having to poll for it.

I hope this explanation was somewhat tought provoking and my patch for Full duplex Java and As3 clients will get accepted.

Thursday, December 31, 2009

Using Thrift with HTTP Transfer-Encoding As3 and Java

A few months ago I started to look for something that could take a place of BlazeDS for my flash game. BlazeDS is nice but I had a few concerns:
  1. BlazeDS by default uses threaded IO. Each connection uses one thread. If you want to use Java NIO and hence better performance you should use the commercial offering.
  2. BlazeDS is open source, but I encourage everyone to look at the code, debug a call stack and make their own judgement. At minimum a faulty config file should not cause a Nullpointer exception.
  3. Finally BlazeDS, AMF is very much As3->Java only. While AS3 and Java is what I use today, for multi platform game development having different language bindings is a good things to have.

So I started to look around and came across Google Protocol Buffer. Cool product, I wish I could be google cool using it but sadly the AS3 port really has very little traction. More importantly it was a little too complicated to construct my own As3 port. Luckily there is a Google Protocol Buffer Diet version, called Thrift. After reading the history of Thrift, it is fair to say I was not the only one who found Google Protocol Buffer a little too feature rich and more complex than needed.

Incidentally there was a pretty functional Thrift As3 port. It was a patch much like with Google but it was more complete patch, and more importantly a straight port of Java which made it easy to follow and debug.

Using Thrift AS3 101.

By default the AS3 port only uses HTTP as transport with a class called HTTPClient. Which is fine because Flash and raw socket connections are troublesome to manage anyway with Flash 10 security sandbox. Also most game platform has some method of "web access" which is pretty hard to do without HTTP.

HttpClient uses a POST request to send a service request in the body, and the service response in the body of the HTTP response. Like this:

My problem was that with small messages this is a fairly wasteful transport. Since Thrift can condense the message into a nice little payload, often times the HTTP headers end up bigger than the payload. Plus unless you use Keep-Alive, the connection will be created and destroyed every time.

Next step I started to wonder. HTTP is just text over a socket, a few headers in request, a few headers in response but after that is exchanged, you get input and output on both side.. Can I just tunnel a socket connection over HTTP?

Not exactly.
  1. The URLConnection will not let you write to a connection after you read.
  2. The As3 URLConnection/Loader really does not have semantics for such operation.
So is that means I can not tunnel over HTTP? No, I just have to follow the HTTP spec.
Thank to a really good explanation for Rex Young I found HTTP/1.1 Chunked Encoding .

The good news is you can tunnel over HTTP (I know ADOBE did it). Jetty as a Servlet container handles the Chunk encoded request by letting you write and read the streams in a single invocation of service(). The bad news it default Java/Flash http clients do not support it AFAIK. So I had to take Rex's example make my own Transport handler to get a Java client (just to test). For Flash I had to create and process and HTTP request over Socket.

The result:

I know some of your reading might say, HTTP header, socket opening not that expensive, to worth the hassle. I would agree, but it depends on the use case. For my int:getTime() benchmark it was 40ms for chunked request, 100ms for all new post request each time. Therefore it depends on the frequency,size and acceptable latency of the messages.
In my case for games with lot of small messages, with low latency HTTP chunk encoding is a significant performance improvement.