The Blog

Apr 20, 2009

Client-Side Data Synchronization with Flex and BlazeDS 

by Maxim Porges @ 12:29 AM | Link | Feedback (8)

As a Flex developer, unless you have been living under a rock for the last four years you know that LiveCycle Data Services offers Data Synchronization. This is implemented as a server-side product, and allows you to quite easily manage concurrent data access for many clients connected to the same server.

But what if you need more than a single server? Cluster, you might say. Unfortunately, that doesn't really work at any serious scale.

At Highwinds, we build everything to scale to ridiculous levels because we're a CDN and that's how we roll. As a result, we use a lot of eventually consistent systems and design every component to scale horizontally. So, when we took to designing the next generation of the StrikeTracker platform, we wanted data synchronization and conflict resolution but we (a) didn't want to manage it statefully at the server tier and (b) didn't want to pay Adobe a bunch of money for LCDS just yet.

So, we decided to implement client-side data sync. In essence, it boils down to the following elements.

1) Whenever a client wants to change data (i.e. add something new, change something, delete something), it calls the server tier using RPC via RemoteObject.

2) The server completes the call in a transaction against a replicated MySQL instance set up in master-slave configuration with hot failover. When the server call completes, the modification to the object has been persisted and a version number on the object has been incremented.

3) The calling client receives a response saying "yep, the server processed your request - hold for confirmation from the message bus." The object that was just processed by the server is placed in to a "limbo" state by the client.

4) Using AOP, "after" advice is applied to the server-side component to dispatch a message to our message bus indicating what just happened.

5) All clients are connected to one of many BlazeDS servers, which are in turn listening to command-and-control channels on our message bus for messages from all BlazeDS servers. All the BlazeDS servers eventually get the messages dispatched by their peers indicating which changes happened on other clients in the system via the message bus's flood-fill algorithm.

6) As the BlazeDS servers receive the messages from the bus, they push them to their clients. The clients then compare the type of message (new, change, or delete) with a collection of objects of the same type in their local memory, looking for matches based upon unique object IDs.

7) If a match is found, the clients do some comparisons between version numbers to figure out if they have the latest version of the data or not. If they don't have the latest data, they can react to conflicts much like LCDS does today. If the client which caused the original request to be processed receives confirmation from the bus that its message was broadcast to everybody, it takes its local copy out of limbo state indicating to the user that the process has finished executing.

The system works on the principle of every object being uniquely identifiable by ID and class type, and being versioned in an ever-increasing fashion. By using a client-side controller mechanism for requesting data changes and putting the controller in charge of all RPC communication, message bus listening, and data management, we've come up with a very robust model for dealing with data synchronization on the client side.

We can also give the client immediate feedback regarding their pending requests, and know that all clients will see the same data appear near-simultaneously. In the case where we have network latency or issues for clients in different parts of the world, the clients are built smart enough to deal with the latencies. Finally, we can use the approach without having to use JMS, which means one less server component - and if you already have a message bus (like we do) then you'll know how nice it feels not to have to light up yet another one.

We're currently coding the implementation and expect to have it working this week, and after we get it going I expect to discuss it further on my blog. There's certainly a fair bit of code to handle corner cases and concurrency issues, so I'm not sure how proprietary this approach is going to be, but I'm hoping that if we get it working I will be able to get permission to open source it to the community. In light of the fact that LCDS's approach does not make sense for all use cases, this seems like something that a lot of people could put to use.

I'll keep you all posted as we go.