Saturday, May 12, 2012

Server Side Versioning

Hello World!(Nothing yet on the new greeting front), I was reading a very good post about versioned put on Ben Stopford blog's, this stimulated me to run some tests and write something to perform server side versioning.

As Ben explained quite well on his blog many caches will need some concept of versioning, for instance, you might want to know not only the latest version of a piece of market data for a given instrument, but you might also want the last 30, to have a view of the overall movement of that instrument.

Why Server Side Versioning

I wanted to write about server side versioning because that is the type of versioning i prefer to use, I find that Client side versioning makes distributed puts non trivial, can create bottlenecks and misses the opportunity to take advantage of Coherence’s distributed infrastructure.

The Goals

  • Multiple distributed clients must be able to push data at the same time and this should not affect the versioning.
  • Multiple Objects with the same business key must be kept in the cache in the same partition.
  • At any given time, a client, using the well known latest version marker (-1) must be able to access the latest version of an object with a simple get.

The steps

  • The first step was to create a key class (in this example called versioned key) that wraps the business key and version, and that implements KeyAssociation, using the business key as the associated key. This will ensure that all of the objects for that same business key will share a partition.
  • My Model class needed a version placeholder field, this will be explained in the the map trigger step,  and is one of the main drawbacks of this approach.
  • Creation of a map trigger that takes advantage of the Entries<link> getPreviousBinaryEntry() method to access the previous version of an entry, update its version and directly put it in the backing map.
  • Whenever a new VersionedKey Object is created its version is set to the well known latest version marker, in this example, -1.
  • When an Entity is added for that key is added to the cache, the VersioningMapTrigger will be executed, having two different flows, one if it is the first object to be added, and another if there are already other versions of that Entity in the cache.
  • If there are no other entries with that key in the cache (getPreviousBinaryEntry is null)  all you need to do is to set the version in the Entity (not its key) to 1 using a POF updater to avoid deserializing it.
  • If another entry exists in the cache for that same key (getPreviousBinaryEntry is not null), several step are required
    • The first step is to get the previous real version(not the version in the key, as it will be -1 since it was the latest) from the Entity that was already in the cache and set the version in the key of that Entity to the same value.
    • We then set the version on the Entity that was just added to that value +1, again using a POF Updater.
    • Finally we put the previous entry and previous key directly in the backing map.
This approach meets all of my requirements, and to be honest there is only one thing I do not like about it, and that is having the version on my model objects, however, since I cannot easily change the value of the key that will be added after a map trigger I decided it was worth it.  I also tried some different approaches, such as my own implementation of a local cache and a backing map listener, however both of them required more backing map calls and were less performant, so I decided against it.