Contains implementation of the off-loadable contract for EntryProcessor execution on a single key.
### Overview
Implementation of the the Offloadable contract for the EntryProcessor execution on a single key.
Allows off-loading the processing unit implementing this interface to the specified or default Executor.
Currently supported in:
IMap.executeOnKey(Object, EntryProcessor)
IMap.submitToKey(Object, EntryProcessor)
IMap.submitToKey(Object, EntryProcessor, ExecutionCallback)
### Offloadable (for reading & writing)
If the EntryProcessor implements the Offloadable interface the processing will be offloaded to the given
ExecutorService allowing unblocking the partition-thread. The key will be locked for the time-span of the processing
in order to not generate a write-conflict.
If the EntryProcessor implements Offloadable the invocation scenario looks as follows:
- EntryOperation fetches the entry and locks the given key on partition-thread
- Then the processing is offloaded to the given executor
- When the processing finishes
if there is a change to the entry, a EntryOffloadableSetUnlockOperation is spawned
which sets the new value and unlocks the given key on partition-thread
if there is no change to the entry, a UnlockOperation is spawned, which just unlocks the kiven key
on partition thread
There will not be a conflict on a write due to the pessimistic locking of the key.
The threading looks as follows:
1. partition-thread (fetch & lock)
2. execution-thread (process)
3. partition-thread (set & unlock, or just unlock if no changes)
### Offloadable (for reading only)
If the EntryProcessor implements the Offloadable and ReadOnly interfaces the processing will be offloaded to the
givenExecutorService allowing unblocking the partition-thread. Since the EntryProcessor is not supposed to do any
changes to the Entry the key will NOT be locked for the time-span of the processing.
If the EntryProcessor implements Offloadable and ReadOnly the invocation scenario looks as follows:
- EntryOperation fetches the entry and DOES NOT lock the given key on partition-thread
- Then the processing is offloaded to the given executor
- When the processing finishes
if there is a change to the entry -> exception is thrown
if there is no change to the entry -> the result is returned to the user from the executor-thread.
In the read-only case the threading looks as follows:
1. partition-thread (fetch)
2. execution-thread (process)
### Primary partition - main actors
- EntryOperation
- EntryOffloadableSetUnlockOperation
### Backup partitions
Offloading will not be applied to backup partitions. It is possible to initialize the EntryBackupProcessor
with some input provided by the EntryProcessor in the EntryProcessor.getBackupProcessor() method.
The input allows providing context to the EntryBackupProcessor - for example the "delta"
so that the EntryBackupProcessor does not have to calculate the "delta" but it may just apply it.
### Locking
The locking takes place only locally. If a partition locked by an off-loaded task gets migrated, the lock will not
be migrated. In this situation the off-loaded task "relying" on the lock will fail on the unlock operation, since it
will notice that there is no such a lock and therefore the processing for the key will get retried.
The reason behind is that the off-loadable backup-processing does not use locking there cannot be any transfer of
off-loadable locks from the primary replica to backup replicas.
GOTCHA: This operation LOADS missing keys from map-store, in contrast with PartitionWideEntryOperation.