wild_nothing
wild_nothing

Reputation: 3051

Correct use of Hazelcast EntryProcessor

We're trying to work out the best way to use Hazelcast's IMap without using pessimistic locking.

EntryProcessor seems like the correct choice, however we need to apply two different types of operations: 'create' when containsKey is false, and 'update' when containsKey is true.

How can I utilise EntryProcessor to support these logic checks?

If two threads hit the containsKey() at the same time and it returns false to both of them, I don't want both of them to create the key. I'd want the second thread to apply an update instead.

This is what we have so far:

public void put(String key, Object value) {

    IMap<String, Object> map = getMap();

    if (!map.containsKey(key)) {
           // create key here
    } else {
        // update existing value here
        // ...
        map.executeOnKey(key, new TransactionEntryProcessor({my_new_value}));
    }
}

private static class MyEntryProcessor implements
        EntryProcessor<String, Object>, EntryBackupProcessor<String, Object>, Serializable {

    private static final long serialVersionUID = // blah blah

    private static final ThreadLocal<Object> entryToSet = new ThreadLocal<>();

    MyEntryProcessor(Object entryToSet) {
        MyEntryProcessor.entryToSet.set(entryToSet);
    }

    @Override
    public Object process(Map.Entry<String, Object> entry) {
        entry.setValue(entryToSet.get());
        return entry.getValue();
    }

    @Override
    public EntryBackupProcessor<String, Object> getBackupProcessor() {
        return MyEntryProcessor.this;
    }

    @Override
    public void processBackup(Map.Entry<String, Object> entry) {
        entry.setValue(entryToSet.get());
    }
}

You can see that two threads can enter the put method and call containsKey at the same time. The second will overwrite the outcome of the first.

Upvotes: 1

Views: 4321

Answers (1)

wildnez
wildnez

Reputation: 1098

EntryProcessor by definition is a processing logic that gets executed on the entry itself, eliminating the need of serializing/deserializing the value. Internally, EPs are executed by partition threads, where one partition thread takes care of multiple partitions. When an EP comes to HC, it is picked by the owner thread of the partition where the key belongs. Once the processing is completed, the partition thread is ready to accept and execute other tasks (which may well be same EP for same key, submitted by another thread). Therefore, it may seem so but EPs should not be used as alternatives to pessimistic locking.

If you are insistent and really keen on using EP for this then you could try putting a null check inside process method. Something like this:

    public Object process(Map.Entry<String, Object> entry) {
        if(null == entry.getValue()) {
            entry.setValue("value123");
        }
        return entry.getValue();
    }

This way two things will happen: 1. The other thread will wait for partition thread to be available again 2. Since the value already exists, you wont overwrite anything

Upvotes: 6

Related Questions