/* * Copyright (c) 2013-2017 Cinchapi Inc. * * 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 com.cinchapi.concourse; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Assert; import org.junit.Test; import com.cinchapi.concourse.Concourse; import com.cinchapi.concourse.Timestamp; import com.cinchapi.concourse.TransactionException; import com.cinchapi.concourse.lang.Criteria; import com.cinchapi.concourse.test.ConcourseIntegrationTest; import com.cinchapi.concourse.thrift.Operator; import com.cinchapi.concourse.time.Time; import com.cinchapi.concourse.util.TestData; import com.google.common.base.Strings; /** * ETE tests for transaction related workflows. * * @author Jeff Nelson */ public class TransactionWorkflowTest extends ConcourseIntegrationTest { /** * A second client that is connected to the server on behalf a different, * non-admin user. */ private Concourse client2; @Override protected void beforeEachTest() { String username = null; while (Strings.isNullOrEmpty(username)) { username = TestData.getSimpleString(); } String password = TestData.getString(); while (Strings.isNullOrEmpty(password) || password.length() < 3) { password = TestData.getString(); } grantAccess(username, password); client2 = Concourse.connect(SERVER_HOST, SERVER_PORT, username, password); } @Test public void testIsolation() { String key = TestData.getSimpleString(); Object value = TestData.getObject(); long record = TestData.getLong(); client.stage(); client2.stage(); client.add(key, value, record); Assert.assertTrue(client.verify(key, value, record)); Assert.assertFalse(client2.verify(key, value, record)); } @Test public void testDurabilityAfterServerRestart() { String key = TestData.getSimpleString(); Object value = TestData.getObject(); long record = TestData.getLong(); client.stage(); client.add(key, value, record); client.commit(); restartServer(); Assert.assertTrue(client.verify(key, value, record)); } @Test public void testConcurrentTransactionsForSameUserAreCorrectlyRouted() { client2 = Concourse.connect(SERVER_HOST, SERVER_PORT, "admin", "admin"); client.stage(); client2.stage(); client.add("foo", "bar", 1); client2.add("foo", "bar", 2); Assert.assertTrue(client.verify("foo", "bar", 1)); Assert.assertFalse(client.verify("foo", "bar", 2)); Assert.assertTrue(client2.verify("foo", "bar", 2)); Assert.assertFalse(client2.verify("foo", "bar", 1)); } @Test public void testConcurrentTransactionsForSameUserCanBeCommitted() { client2 = Concourse.connect(SERVER_HOST, SERVER_PORT, "admin", "admin"); client.stage(); client2.stage(); client.add("foo", "bar", 1); client2.add("foo", "bar", 2); Assert.assertTrue(client.commit()); Assert.assertTrue(client2.commit()); } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionGet() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.get("foo", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionRevert() { try { client.stage(); client.get("foo", 1); Timestamp ts = Timestamp.now(); client.set("foo", "bar", 1); client2.set("foo", "baz", 1); client.revert("foo", 1, ts); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionAdd() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.add("foo", "grow", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionAudit() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.audit("foo", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionAuditRecord() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.audit(1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionBrowseRecord() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.select(1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionBrowseKey() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.browse("foo"); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionChronologize() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.chronologize("foo", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testChronologizeWithFutureEndTimestampGrabsLock(){ try{ client.stage(); client.chronologize("foo", 1, Timestamp.epoch(), Timestamp.fromMicros(Time.now() + 100000000)); client2.set("foo", "baz", 1); Assert.assertFalse(client.commit()); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionClear() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.clear("foo", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionClearRecord() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.clear(1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionCommit() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.commit(); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionDescribeRecord() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.describe(1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionDescribeKeyFetch() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.select("foo", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionFind() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.find("foo", Operator.EQUALS, "bar"); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionFindCriteria() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.find(Criteria.where().key("foo").operator(Operator.EQUALS) .value("bar")); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionInsert() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.insert("{\"foo\": \"bar\"}", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionInsertNewRecord() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.insert("{\"foo\": \"bar\"}"); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionPing() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.ping(1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionRemove() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.remove("foo", "baz", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionSearch() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.search("foo", "bar"); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionSet() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.set("foo", "bar", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionVerify() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.verify("foo", "bar", 1); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionVerifyAndSwap() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.verifyAndSwap("foo", "bar", 1, "baz"); } finally { client.abort(); } } @Test(expected = TransactionException.class) public void testPreCommitTransactionFailuresAreIndicatedWithExceptionVerifyOrSet() { try { client.stage(); client.get("foo", 1); client2.set("foo", "baz", 1); client.verifyOrSet("foo", "bar", 1); } finally { client.abort(); } } @Test public void testCommitWhenNotInTransactionReturnsFalse() { Assert.assertFalse(client.commit()); } @Test public void testStageRunnableCommit() { boolean committed = client.stage(new Runnable() { @Override public void run() { client.add("name", "Ron", 1); client.add("name", "Stacy", 2); } }); Assert.assertTrue(committed); Assert.assertEquals("Ron", client.get("name", 1)); Assert.assertEquals("Stacy", client.get("name", 2)); Assert.assertEquals("Ron", client2.get("name", 1)); Assert.assertEquals("Stacy", client2.get("name", 2)); } @Test public void testStageRunnableFailsOnConfilct() throws InterruptedException { final AtomicBoolean t1Go = new AtomicBoolean(false); final AtomicBoolean t2Go = new AtomicBoolean(false); final AtomicBoolean committed = new AtomicBoolean(true); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { committed.set(client.stage(new Runnable() { public void run() { client.add("name", "Ron", 1); t2Go.set(true); while (!t1Go.get()) { continue; } } })); } catch (TransactionException e) { committed.set(false); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { while (!t2Go.get()) { continue; } client2.add("name", "Bron", 1); t1Go.set(true); } }); t1.start(); t2.start(); t2.join(); t1.join(); Assert.assertFalse(committed.get()); Assert.assertFalse(client.select("name", 1).contains("Ron")); } }