/*
* Copyright 2015-2016 OpenCB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opencb.opencga.storage.mongodb.utils;
import com.mongodb.client.model.Updates;
import org.bson.Document;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.opencb.commons.datastore.core.QueryOptions;
import org.opencb.commons.datastore.mongodb.MongoDBCollection;
import org.opencb.commons.datastore.mongodb.MongoDataStoreManager;
import org.opencb.opencga.storage.mongodb.variant.MongoDBVariantStorageTest;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.opencb.opencga.storage.core.variant.VariantStorageBaseTest.DB_NAME;
/**
* Created on 13/06/16
*
* @author Jacobo Coll <jacobo167@gmail.com>
*/
public class MongoLockTest implements MongoDBVariantStorageTest {
private MongoLock mongoLock;
@Rule
public ExpectedException thrown = ExpectedException.none();
private MongoDBCollection collection;
@BeforeClass
public static void setUpClass() throws Exception {
}
@Before
public void setUp() throws Exception {
clearDB(DB_NAME);
MongoDataStoreManager mongoDataStoreManager = getMongoDataStoreManager(DB_NAME);
collection = mongoDataStoreManager.get(DB_NAME).getCollection("locks");
mongoLock = new MongoLock(collection);
}
@Test
public void testLock() throws Exception {
int lockId = 1;
insertDocument(lockId);
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
long lock = mongoLock.lock(lockId, 10, 10);
System.out.println("lock = " + lock);
mongoLock.unlock(lockId, lock);
}
}
@Test
public void testConcurrentLock() throws Exception {
int lockId = 2;
insertDocument(lockId);
AtomicInteger counter = new AtomicInteger(0);
Set<String> threadWithLock = Collections.synchronizedSet(new HashSet<>());
int nThreads = 20;
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
List<Future> futures = new ArrayList<>();
for (int t = 0; t < nThreads; t++) {
futures.add(executorService.submit(() -> {
try {
for (int i = 0; i < 5; i++) {
System.out.println("i = " + i);
long lock = mongoLock.lock(lockId, 1000, 20000);
System.out.println("[" + Thread.currentThread().getName() + "] Enter LOCK");
assertEquals(threadWithLock.toString(), 0, threadWithLock.size());
threadWithLock.add(Thread.currentThread().getName());
assertEquals(threadWithLock.toString(), 1, threadWithLock.size());
int value = counter.addAndGet(1);
Thread.sleep(100);
assertEquals(threadWithLock.toString(), 1, threadWithLock.size());
assertEquals(threadWithLock.toString(), value, counter.get());
threadWithLock.remove(Thread.currentThread().getName());
System.out.println("lock = " + lock);
System.out.println("[" + Thread.currentThread().getName() + "] Exit LOCK");
mongoLock.unlock(lockId, lock);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}));
}
executorService.shutdown();
executorService.awaitTermination(2000, TimeUnit.SECONDS);
for (Future future : futures) {
assertTrue(future.isDone());
future.get();
}
}
@Test
public void testLockAndLock() throws Exception {
int lockId = 3;
insertDocument(lockId);
long lock = mongoLock.lock(lockId, 1000, 2000);
System.out.println("lock = " + lock);
thrown.expect(TimeoutException.class);
mongoLock.lock(lockId, 1000, 1000);
}
@Test
public void testLockAfterExpiring() throws Exception {
int lockId = 4;
insertDocument(lockId);
long lock = mongoLock.lock(lockId, 1000, 1000);
System.out.println("lock = " + lock);
Thread.sleep(2000);
System.out.println("Expired lock = " + lock);
lock = mongoLock.lock(lockId, 1000, 1000);
System.out.println("Unlock = " + lock);
mongoLock.unlock(lockId, lock);
}
public void insertDocument(Object id) {
collection.update(new Document("_id", id),
Updates.set("_id", id),
new QueryOptions(MongoDBCollection.UPSERT, true));
}
}