Random Post: It wasn't a race, but . . .
RSS .92| RSS 2.0| ATOM 0.3
  • Home
  • About
  •  

    NCache Concurrency Controls

    March 8th, 2012

    Recently I have been looking into .NET-friendly in-memory data grid (IMDG) software. One of the products I have been experimenting with is Alachisoft’s NCache.

    When I analyze a distributed caching product, I like to focus first on concurrency control. Before examining performance, scalability, or advanced architectural features, it is valuable to confirm that the software can be used to construct applications that remain reliable when data is shared and subject to updates. To do this I usually construct a test case involving extreme data hot spots, and exercise the features offered by the product for managing data access in these heavy contention situations.

    If the product I’m working with offers features for dividing work into ACID style transactions then I explore those features as part of my analysis of concurrent data access. NCache does not provide features for using ACID transactions, so my work with NCache has focused on its locking features.

    NCache provides both optimistic and pessimistic concurrency control. The pessimistic locking model turns out to be unusual, at least in my experience. Although the documentation on this feature is quite sparse, with help from the (very responsive) tech. support team at Alachisoft I was able to get an understanding of how NCache’s pessimistic locking works and how to use the NCache API to control it.

    Here are some key points about NCache’s pessimistic locking:

    1. The NCache data access API provides specialized methods for acquiring locks, releasing locks and accessing items in the cache under locking constraints.

    2. Each lock represents the state of an item of data in the cache.

    3. Locks are not granted to a thread or a process. Every lock is equally visible to and manipulable by all cache client threads, regardless of which thread originally requested the lock.

    4. Locking is completely cooperative. If locking is used to control access to a data item then, to ensure that exclusive access to that item will be maintained until the routine that requested the lock relinquishes it, every potentially concurrent access to that item must be made using the locking form of the NCache data access API. Using a non-locking API method to access a locked item will bypass the lock.

    5. In at least one case, using a non-locking API method will not only bypass the lock but will remove it.

    6. NCache’s locks will never cause a client thread to block. Data access methods that access a locked item in the cache will return right away. Clients must include logic to interpret the results of the method invocation and retry it if appropriate.

    7. In some cases, to determine why a method has not been successful the side effects of the method must be examined.

    This final point requires a fuller explanation. Most of the the pessimistic locking API methods take a reference to an object called a LockHandle as one of their arguments. A LockHandle stores the information that identifies a lock on a data item. The values stored in the LockHandle you pass in may be changed by the method. Depending on which method you use and the state (locked or unlocked) of the item you are trying to access, to interpret the results of your locking call it may be necessary to inspect the values in the LockHandle you passed in. Here is an example:

    The Get() method takes a key and returns the associated cached item if there is one. The locking form of the Get() method takes three additional arguments, one of which is a LockHandle reference. Depending on the values of those arguments, the method can be used to lock an object and retrieve it in a single call. In this case the method will return null if there is no object in the cache with the specified key value, or if the object exists but is already locked. To distinguish between these two cases the caller must examine the post-call values in the LockHandle.

    NCache’s concurrency features work as intended, and with them I was able to maintain the data consistency required by my test scenario. Both the optimistic and the pessimistic features delivered correct results when data items were subject to vigorous concurrent access. However, when compared with the concurrency features provided by well known competitors, NCache’s concurrency controls are quirky and not particularly developer-friendly. Its vulnerability to pessimistic locks being accidentally bypassed or removed by clients that don’t conform to the cooperative lock management protocol makes applications that depend on exclusive data access prone to bugs. The non-blocking approach may be welcome in some situations, but the lack of blocking calls is likely to lead to increased client code complexity and reduced performance in many cases. The lack of support for ACID transactions poses a significant obstacle to building applications that must maintain strict data consistency. For some use cases NCache’s pessimistic locking or its optimistic concurrency features may suffice, but developers designing a system with shared access to writable data should study its concurrency control feature set carefully before choosing NCache.


    .NET Support in GigaSpaces XAP

    February 24th, 2012

    Introduction

    (download PDF version with illustrations)

    Some work I did for a client a few months ago started me thinking about distributed caching options for .NET. Among the well known distributed caching products, only NCache was designed specifically for .NET, with Microsoft’s own Velocity project expected to mature into a viable product shortly.  Others such as Oracle Coherence, Terracotta with EHCache , VMWare GemFire and  GigaSpaces XAP are based on non-.NET (primarily Java) ecosystems, but most of them provide some level of support for .NET applications.  This post provides a simple framework for comparing the .NET support provided by these products and a summary of what I found when I began exploring GigaSpaces’ XAP .NET edition.

    Framework for Comparison

    .NET support can be divided into a three tiered hierarchy:

    Tier One – It must be possible for a .NET client program to store .NET objects to the cache, retrieve them from the cache, and operate on them.  This is the sine qua non of .NET support in a caching product.

    Tier Two – For caching products that allow the user (developer) to implement server-side processing that is closely integrated with the cache, the second tier of .NET support lets the developer implement the server-side logic in a .NET language.

    Tier Three – A caching product could be implemented entirely in a .NET language and be fully harmonized with the broader .NET ecosystem. As we have already observed, none of the best known products are built this way.

    An orthogonal consideration – supported languages – can be applied to all three tiers.  Compatibility with one .NET language doesn’t imply compatibility with all .NET languages; cross-language interoperability depends on compliance with Microsoft’s Common Language Specification (CLS) http://msdn.microsoft.com/en-us/library/730f1wy3(v=vs.71).aspx.  So, although C# is likely to be the language of interest for most organizations that are looking for a .NET-compatible distributed caching solution, it is worth noting whether or not the .NET support provided in these products extends to other .NET languages as well.

    GigaSpaces XAP .NET

    First, a refresher on XAP:  This GigaSpaces product began as a distributed caching framework designed to reduce latency when accessing application data, be highly scalable, and simplify the construction of “real-time” (event-driven) systems.  Unlike most caches which are based on the key – value paradigm, GigaSpaces is based on Java spaces, which is an implementation of tuple spaces http://en.wikipedia.org/wiki/Tuple_space.  Tuples provide more generalized data matching features than do key – value pairs, and tuple spaces feature strong, built-in concurrency controls.

    As new capabilities were added, the XAP framework evolved from a caching system to a full-featured application development and management platform.  In addition to highly available caching,  XAP now provides several server-side processing options, event notification and distribution, an SLA-based managed execution environment, dynamic scalability, multi-site replication and – of course – .NET support.

    In the terminology of the framework described in the previous section, XAP .NET provides tiers one and two functionality, with support for C# and Visual Basic (VB .NET).

    Tier One Support

    XAP .NET includes a C# API for use when writing a caching client.  The API provides classes and methods for creating or connecting to a cache, writing data to the cache, reading data from the cache, and querying or searching the cache for objects that match specific criteria.  Transactional and concurrency control features are also available for use when needed.

    Tier Two Support

    Server side application logic can be implemented for execution on demand or in response to data events or conditions with GigaSpaces .NET. On-demand logic takes the form of remote procedure calls.  Processing logic is implemented in C#, as is the interface contract.  Clients use features of the C# API to invoke the remote procedures.

    Event driven logic can be implemented as either a polling mechanism or an event notification mechanism.  The polling conditions or event registration (depending on which mechanism is in use) are specified in C#, as is handler code that receives the result of the polling operation or is notified of the data event.

    Note that the client program that triggers the server-side event need not be a .NET program.