Search code examples
javachroniclechronicle-map

chronicle map - value data types


What's the best way of implementing a chronicle map where the value side is a map or a set?

I need data structures similar to the following where I can store multiple versions of some data with a specific id:

chronicle-map: String -> Map<String,V>
$id -> {v0-> value-v0, v1-> value-v1, v2 -> value-v2}

or possibly two maps:

chronicle-map-1: String -> Set<String>
key-$id -> Set{v0,v1,v2}

chronicle-map-2: String -> V
version-$id-v0 -> value-v0
version-$id-v1 -> value-v1
version-$id-v2 -> value-v2

(Atomicity and serialisation performance are my main concerns). acquireUsingLocked/getUsingLocked methods don't seem to work with standard map/set implementations.


Solution

    1. The thing you should certainly do is to switch to Chronicle Map 3.x, because this version defines the new, solid set of patterns and abstractions, that will evolve to support "native" Multimap sometimes.

    [The following regards Chronicle Map 3.x]

    1. To ensure atomicity (thread-safety), you could either:

      • use Java 8's new Map methods: compute(), computeIfAbsent(), computeIfPresent() or merge() methods, like:

        static <K, V> void multiMapAdd(ChronicleMap<K, Set<V>> map, K key, V value) {
            map.compute(key, (k, v) -> {
                if (v == null)
                    v = new HashSet<>();
                v.add(value);
                return v;
            });
        }
        
      • acquire a context, and operate with value bytes, for optimizing some serialization/deserialization costs, probably. E. g.

        interface LimitedSet {
            public static final int MAX_VALUES_SIZE = 20;
        
            byte getSize();
            void setSize(byte);
        
            MyValue getValue(int index);
            void setValue(@MaxSize(MAX_VALUES_SIZE) int index, MyValue value);
        }
        
        ...
        try (ExternalMapQueryContext<K, LimitedSet, ?> cxt = map.queryContext(key) {
            cxt.writeLock().lock();
            MapEntry<K, LimitedSet> entry = cxt.entry();
            if (entry == null) {
                MapAbsentEntry<K, LimitedSet> absentEntry = cxt.absentEntry();
                cxt.insert(absentEntry, absentEntry.defaultValue());
                entry = cxt.entry();
                assert entry != null;
            }
            LimitedSet values = entry.value().get();
            int size = values.getSize();
            for (int i = 0; i < size; i++) {
                if (same(values.getValue(i), value))
                    return false;
            }
            if (size == MAX_VALUES_SIZE)
                throw new IllegalStateException("values set overflow");
            values.set(size, value);
            values.setSize((byte) (size + 1));
        }
        

        You could also find a couple of such advanced "MultiMap" usages of Chronicle Map, which reveals additional abilities as CRDT replication and multi-entry locking, in Chronicle Map README: