Implicit Marshalled Values - Lazy deserialization of cached objects
The purpose of implicit marshalled values in JBoss Cache is to provide lazy unmarshalling of cached objects when they are retrieved from the cache. These cached objects may have originated from persistent storage (such as a cache loader) or a remote cache instance in a cluster, and hence are only available as a stream of bytes. The benefit of lazy deserialization are twofold:
1. Much better replication performance.
Since synchronous replication implies that a cache operation does not return until state is replicated across a cluster and an acknowledgement is returned, any time saved in deferring deserialization can be a bit plus in terms of performance. In fact, we have noticed that a very significant amount of time spent in most cache operations is in serialization and deserialization of objects for replication and/or persistent storage. Even if the cache is configured to use asynchronous replication, this can still save on CPU cycles on recipient caches. And in all cases, this speeds up cache loading since not all loading calls actually need or use cached data (such as counting the number of attributes in a node).
2. Much simpler alternative to region-based class loading
Currently the only way to assign a different class loader for different cached objects is to define a region and assign a class loader to the region. This is necessary/useful when different web applications, for example, share a single cache to replicate application data across a cluster. Since marshalled values deserialize lazily, it will only deserialize if an application that explicitly uses it's state requests the data, and it will be deserialized using the context class loader on the caller's thread. Using marshalled values instead will lead to much simpler and far more performant code paths than using regions.
When do we plan to do this?
JBoss Cache 2.1.0 Alegrias is the target release for implicit marshalled values.
What do I have to do to enable this?
Nothing. Just be aware that your cached objects are deserialized lazily so writing a further layer on top of the cache to do the same thing is wasteful. If you do have such a further layer, it may make sense to remove that layer. Also, be sure to disable region-based marshalling (disabled by default).
Can I disable this?
Yes. A configuration element - UseLazyDeserialization - will be available. This will default to true but can be disabled.
What actually happens under the hood?
An interceptor - MarshalledValueInterceptor - will be inserted into the cache's interceptor stack just after the TxInterceptor.
All cache read and write methods will be intercepted. In the case of writes, all cached objects will be wrapped in a MarshalledValue object (unless they are in the list of excluded types). In the case of reads (as well as return values for some write operations), the values are inspected and if they are MarshalledValues, they will be replaced with the actual cached object - deserializing if necessary - before returning to the caller.
Types excluded from being wrapped in a MarshalledValue would just be a limited set, consisting of java.lang.String and Java primitives (and their Object wrapper counterparts), as well as arrays of these types (but not other collections of these types).
Using custom objects as attribute keys
While using custom objects (which get implicitly wrapped as MarshalledValues) works well as attribute values in a tree node, the usefulness in laz deserialization is limited when used as attribute keys. This is because, for proper functioning, MarshalledValue.hashCode() and equals() methods need to work off the deserialized instance and not the byte stream, and hashCode() is queried every time an object is placed in a Map as a key. Further, if there are hash collisions, then equals() is called as well.
So while using custom objects as attribute values certainly work well, using them as attribute keys may not fully benefit the goodness of lazy deserialization.
This new API method will allow the user to control flushing of all marshalled value object references to their byte buffers - a sort of lazy serializing - which may be useful when the class representations of the objects are due to change.