Additionally if a lock is held by another thread lock.tryLock() appears to
block.
Demonstrated by the following code. See header comment for a full description.
The error at line 85 should not be printed.
Found on Ignite 2.5.0. Reproducible on Windows and RHL.
Note : The test is technically timing dependant but the sleep in the remote
thread is long enough that, on any reasonable system, the parent should check
the lock before it dies.
package igniteCacheLockTest;
import org.apache.ignite.Ignition;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import
org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteLock;
/**
* Test demonstrating a cache lock held by one ignite node not being accessible
on a different node.
*
* The parent starts an ignite node and creates a cache lock. It then creates a
thread which starts a second node, gets a handle to the same
* lock (proven by the fact that the set value can be read back) and locks it.
*
* The parent then uses isLocalLocked() to check if the lock is held. It isn't.
*
* The parent also tries a trylock(). This was done to check if maybe the lock
is really held but, maybe, isLocalLocked() is returning
* incorrect information. Incorrectly the, non-blocking, trylock() blocks until
the thread dies and then gets the lock.
*
* If the thread is modified to not take the lock (comment out the lock.lock()
line). The trylock() gets the lock and returns immediately as
* expected.
*
* There is some interaction between the threads based on the lock location but
it is not working as expected.
*/
public class CacheLockTest {
public final static String LOCKNAME = "LOCKNAME";
public final static String CACHENAME = "CACHENAME";
public final static int TESTVALUE = 123;
public static void main(String[] args) {
System.out.println("Starting test");
// Make a uniquely named ignite node.
IgniteConfiguration config1 = new
DefaultIgniteConfig();
config1.setIgniteInstanceName("INSTANCE1");
// Start node.
Ignite node1 = Ignition.start(config1);
// Get a reference to the cache
IgniteCache<String, Integer> cache =
node1.getOrCreateCache(CACHENAME);
// Write a pattern to the location so we can
confirm we are connected to the same cache.
cache.put(LOCKNAME, TESTVALUE);
// Make a lock
Lock lock = cache.lock(LOCKNAME);
// Check is initially unlocked
if (cache.isLocalLocked(LOCKNAME, true)) {
System.out.println("Is
initially locked local");
}
if (cache.isLocalLocked(LOCKNAME, false)) {
System.out.println("Is
initially locked remote");
}
// Create a remote thread.
TestThread thread = new TestThread(LOCKNAME);
// Run thread. Will get a handle to the lock
and lock it.
thread.start();
try {
// Give thread a while to run.
Thread.sleep(5000);
} catch (Exception e) {
System.out.println("Could not
sleep.");
}
// Thread should still be alive and have taken
the lock. Check it.
if (!cache.isLocalLocked(LOCKNAME, false)) {
System.out.println("ERROR.
Thread failed to take lock");
}
// To confirm it is really not locked try to
take the lock.
System.out.println("Parent about to do
non-blocking trylock().");
if (lock.tryLock()) {
System.out.println("ERROR. Lock
was not held.");
// Just took the lock. Tidy.
lock.unlock();
} else {
System.out.println("Lock is
held as expected.");
}
// Close node
node1.close();
System.out.println("Test done");
// Run of end and allow thread to self tidy.
thread = null;
}
// Remote thread that takes a lock.
private static class TestThread extends Thread {
private Ignite node2;
private String lockName;
public TestThread(String lockName) {
// Remember lock name.
this.lockName = lockName;
// Start a new, uniquely named,
ignite node for the thread,
IgniteConfiguration config2 =
new DefaultIgniteConfig();
config2.setIgniteInstanceName("INSTANCE2");
// Start an ignite node. No
caches required.
node2 = Ignition.start(config2);
}
public void run() {
// Get a handle to the lock and
lock it.
// Get a reference to the cache
IgniteCache<String, Integer>
cache = node2.getOrCreateCache(CACHENAME);
// Make a lock
Lock lock =
cache.lock(lockName);
// Read the value to check we
are talking to the same cache.
int value = cache.get(lockName);
if (TESTVALUE != value) {
System.out.println("Value is wrong = " + value);
}
// Take the lock.
lock.lock();
// Check we got the lock
if
(!cache.isLocalLocked(lockName, true)) {
System.out.println("Thread could not get lock");
}
// Wait for long enough for
parent to check the lock. i.e. significantly longer than the wait in the parent.
try {
// Give thread
a while to run.
System.out.println("Thread sleeping.");
Thread.sleep(20000);
} catch (Exception e) {
System.out.println("Could not sleep.");
}
System.out.println("Thread
closing.");
node2.close();
}
}
// A simple, canned, ignite config.
private static class DefaultIgniteConfig extends
IgniteConfiguration {
public DefaultIgniteConfig() {
// Some basic config.
setIgniteInstanceName("IGNITE_LOCK_TEST");
// Need a server to connect to.
So be a server.
setClientMode(false);
setPeerClassLoadingEnabled(true);
// Set up node discovery
TcpDiscoverySpi discoSpi = new
TcpDiscoverySpi();
TcpDiscoveryMulticastIpFinder
finder = new TcpDiscoveryMulticastIpFinder();
List<String> addresses =
Arrays.asList("127.0.0.1:47500..47502");
finder.setAddresses(addresses);
discoSpi.setIpFinder(finder);
setDiscoverySpi(discoSpi);
// Set up data storage
DataStorageConfiguration
storeConfig = new DataStorageConfiguration();
DataRegionConfiguration
regionConfig = storeConfig.getDefaultDataRegionConfiguration();
regionConfig.setName("IGNITE_LOCK_REGION");
// For locking should not need
persistence.
regionConfig.setPersistenceEnabled(false);
setDataStorageConfiguration(storeConfig);
// Set up a cache. Same for
both instances.
CacheConfiguration<String,
Integer> cacheConfig = new CacheConfiguration<String, Integer>();
cacheConfig.setName(CACHENAME);
cacheConfig.setCacheMode(CacheMode.REPLICATED);
cacheConfig.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
// Not sure we need the
following
cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
cacheConfig.setBackups(0);
this.setCacheConfiguration(cacheConfig);
}
}
}
The information in this e-mail and any attachments is confidential and may be
legally privileged. It is intended solely for the addressee or addressees. Any
use or disclosure of the contents of this e-mail/attachments by a not intended
recipient is unauthorized and may be unlawful. If you have received this e-mail
in error please notify the sender. Please note that any views or opinions
presented in this e-mail are solely those of the author and do not necessarily
represent those of TEMENOS. We recommend that you check this e-mail and any
attachments against viruses. TEMENOS accepts no liability for any damage caused
by any malicious code or virus transmitted by this e-mail.