0

Looking into ChronicleMap (2.1.7), and I'm not exactly clear on the proper usage of WriteContext when calling acquireUsingLocked();

The scenario I'm interested in is a function where I need to take 2 actions atomically from the viewpoint of the function caller. One of them is adding an entry to the map, if not present. The other one should only take place if there was previously no value for the given key in the map.

If there was previously no value for a given key in the map, and this second action fails, the map should not be updated, so that subsequent tests would find no value for the corresponding key.

If there was already an entry in the map for the given key, I don't want to update its original value, and I don't want to undertake this second action. But, I do need to use the original entry's value in order to construct the return value for the caller.

The documentation for WriteContext.created() says that it returns true only if the entry was previously present. If the entry was previously present, is it possible to get a reference to the previous value from the context (or some other way - like calling map.get() within the WriteContext scope)?

What does WriteContext.value() refer to? The previous entry's value, or the potentially updated one I provided in the call to acquireUsingLocked()?

Also, if there wasn't previously an entry in the map, and I don't want to update the map, should I call WriteContext.removeEntry(), or WriteContext.dontPutOnClose()?

Here's an example of the logic I'm thinking I need:

    EntryData newValue = new EntryData();
    EntryData originalValue = null;
    try (WriteContext<String, EntryData> context = _map.acquireUsingLocked(key, newValue) ) {
        if ( !context.created() ) {
            if ( doSomething() ) {
                result = createResult(newValue);
            }
            else {
                context.removeEntry();
                result = null;
            }
        }
        else {
            context.dontPutOnClose();
            originalValue = context.value();
            result = createResult(originalValue);
        }
    }
leventov
  • 14,760
  • 11
  • 69
  • 98
Hoobajoob
  • 2,748
  • 3
  • 28
  • 33

1 Answers1

0

To be honest, it is really hard to parse your question.

One of them is adding an entry to the map, if not present.

The other one should only take place if there was previously no value for the given key in the map.

I don't understand the difference. For me, these sentenses are synonymic.


What does WriteContext.value() refer to? The previous entry's value, or the potentially updated one I provided in the call to acquireUsingLocked()?

writeContext.value() is always identical (==) to the value, you have provided to acquireUsingLocked() method, newValue in your example. It if is not feasible, some RuntimeException is thrown.

Also, if there wasn't previously an entry in the map, and I don't want to update the map, should I call WriteContext.removeEntry(), or WriteContext.dontPutOnClose()?

WriteContext.removeEntry(), because by the time acquireUsingLocked() returns, the entry is already put into the map.


Method acquireUsingLocked() in ChronicleMap, version 2.x is tailored for two types of values: data value generated and CharSequence, via StringBuilder.

Data value generated interface is a pointer to off-heap memory. If acquireUsingLocked() called when there was no entry for the key in the map, a new entry is created, filled with zero bytes and usingValue made to point to off-heap bytes, already stored in the map:

try (WriteContext<K, LongValue> wc = map.acquireUsingLocked(k, value)) {
    // update off-heap bytes, already serving as a value for the key
    // now: k -> LongValue { 0 }
    value.setValue(42);
    // now: k -> LongValue { 42 }
}

In CharSequence value case, when acquireUsingLocked() is called, and observes no value for the key, it puts empty char sequence to the key and sets the given StringBuilder value to zero length: sb.setLength(0). You can update the value within WriteContext, like sb.append("Hello"). When WriteContext.close() is called (end of try-with-resources block), this sb value is picked up and put for the queried key into the map, again. To prevent this, you could call dontPutOnClose().


All the above sounds really awkward, so Chronicle Map 3.x introduces the new, coherent "contexts" pattern. It doesn't update the map implicitly, in any way.

try (ExternalMapQueryContext<K, V, ?> cxt = map.queryContext(key)) {
    cxt.writeLock().lock();
    MapEntry<K, V> entry = map.entry();
    if (entry != null) {
        // entry is already present in the map.
        map.value.get(); // access the _previous_ value;
        // value is not updated until you call:
        cxt.replaceValue(entry, cxt.wrapValueAsData(newValue));
        // or remove:
        cxt.remove(entry);
    } else {
        // entry is absent.
        // can insert a value using:
        cxt.insert(cxt.absentEntry(), cxt.wrapValueAsData(newValue));
    }
}
// no implicit inserts, removes, updates.
leventov
  • 14,760
  • 11
  • 69
  • 98