Version 3

    This feature of Infinispan is tracked by ISPN-78, and the goal is to be able to support the storage of entries that exceed the size of any given JVM in the cluster and make use of distribution's ability to push chunks around the network to realise the total available memory in the grid.

     

    To do so, a streaming interface is necessary since passing in large objects would create issues around serialization.

     

    The streaming interface

    One approach would be to add the following methods to the Cache interface (or perhaps the AdvancedCache interface):

     

    OutputStream writeToKey(K key);
    
    InputStream readFromKey(K key);
    

     

    Enabling Large Object support

    LO support would need to be enabled in the cache's configuration as it would involve adding an interceptor to handle chunking.  Users would also need to configure a chunk size.

     

    The LargeObjectInterceptor

    This interceptor would need to map traditional Key -> Value entries to something like:

     

    Key -> LargeObjectMetadata

     

    where LargeObjectMetadata is an internal class as follows:

     

    class LargeObjectMetadata {
      int totalSize;
      String[] chunkKeys;
    }
    

     

    Cache API methods

    The following cache methods will be unsupported if the key passed in maps to a LargeObjectMetadata instance, and would throw a CacheException.

     

    V put(K key, V value);
    V remove(K key, V oldValue);
    V putForExternalRead(K key, V value);
    boolean replace(K key, V oldValue, V value);
    V get(K key)
    

     

    The only supported methods would be:

    InputStream readFromKey(K key);
    OutputStream writeToKey(K key);
    V remove(K key); // except that this will always return a null
    boolean containsKey(K key);
    V replace(K key, V value); // except that this will always return a null
    

     

    The streams

    The streams created by readFromKey() and writeToKey() would need to read bytes and, upon hitting chunk sizes, would need to retrieve the next appropriate chunk from the cache. 

     

    Transactions

    All stream operations are transactional and happen within the scope of a JTA transaction.  This is to enable proper locking of chunks.  And to prevent running out of memory by storing all chunks within a single transaction's context, eager locking is required.

     

    Steps: writeToKey(K k)

    1. tx.begin();
    2. put(k, new  LargeObjectMetadata())
    3. return new CacheOutputStream()
    4. Read bytes
      1. as chunk size is hit, move bytes to a new byte[]
      2. Store byte array: chunkKey = new UUID(); put(chunkKey, chunk)
      3. Update  LargeObjectMetadata: totalSize += chunk.length, chunkKeys += chunkKey
    5. stream.close()
      1. Finalise remaining bytes into a partial chunk (same as step 5)
      2. tx.commit() to finalise tx and release locks.