/* * 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; /** * Unit tests to show that transactions in Concourse meet the standard for * serializable isolation. These examples are inspired by the Wikipedia entry on * transaction isolation levels at * http://en.wikipedia.org/wiki/Isolation_%28database_systems%29 * * @author Jeff Nelson */ public class TransactionIsolationTest extends ConcourseIntegrationTest { private Concourse client2; @Override protected void beforeEachTest() { client2 = Concourse.connect(SERVER_HOST, SERVER_PORT, "admin", "admin"); } @Test public void testNoDirtyRead() { client.stage(); client2.stage(); client.add("foo", "bar", 1); Assert.assertNotEquals("bar", client2.get("foo", 1)); } @Test(expected = TransactionException.class) public void testNoNonRepeatableRead() { client.add("name", "Jeff Nelson", 1); client.stage(); client.select(1); client2.add("age", 100, 1); System.out.println(client.select(1)); } @Test(expected = TransactionException.class) public void testNoPhantomRead() { client.add("foo", 10, 1); client.stage(); client.find(Criteria.where().key("foo").operator(Operator.BETWEEN) .value(5).value(20)); client2.add("foo", 15, 2); client.find(Criteria.where().key("foo").operator(Operator.BETWEEN) .value(5).value(20)); } @Test(expected = TransactionException.class) public void testNoPhantomReadWithTimeStampInTheFutureUsingBrowse() { Timestamp aheadOfTime = Timestamp.fromMicros(Time.now() + (long) 10e9); client.add("foo", "bar", 100); client.stage(); client.browse("foo", aheadOfTime); client2.add("foo", "bar", 100 + 1); client.commit(); } @Test(expected = TransactionException.class) public void testNoPhantomReadWithTimeStampInTheFutureUsingDescribe() { Timestamp aheadOfTime = Timestamp.fromMicros(Time.now() + (long) 10e9); client.add("foo", "bar", 110); client.stage(); client.describe(110, aheadOfTime); client2.add("bar", "foo", 110); client.commit(); } @Test(expected = TransactionException.class) public void testNoPhantomReadWithTimeStampInTheFutureUsingFind() { Timestamp aheadOfTime = Timestamp.fromMicros(Time.now() + (long) 10e9); client.add("foo", 50, 120); client.stage(); client.find("foo", Operator.BETWEEN, 0, 100, aheadOfTime); client2.add("foo", 75, 120); client.commit(); } @Test(expected = TransactionException.class) public void testNoPhantomReadWithTimeStampInTheFutureUsingGet() { Timestamp aheadOfTime = Timestamp.fromMicros(Time.now() + (long) 10e9); client.add("foo", "bar", 130); client.stage(); client.get("foo", 130, aheadOfTime); client2.add("foo", "foobar", 130); client.commit(); } @Test(expected = TransactionException.class) public void testNoPhantomReadWithTimeStampInTheFutureUsingVerify() { Timestamp aheadOfTime = Timestamp.fromMicros(Time.now() + (long) 10e9); client.add("foo", "bar", 140); client.stage(); client.verify("foo", "bar", 140, aheadOfTime); client2.add("foo", "foobar", 140); client.commit(); } @Test public void testNoWriteSkew() { client.set("balance", 100, 1); client.set("balance", 100, 2); client.stage(); client2.stage(); final AtomicBoolean done1 = new AtomicBoolean(false); final AtomicBoolean done2 = new AtomicBoolean(false); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { client.stage(); client.set("balance", ((int) (client.get("balance", 1))) - 200, 1); if((int) client.get("balance", 1) + (int) client.get("balance", 2) >= 0) { client.commit(); } } catch (TransactionException e) { client.abort(); } finally { done1.set(true); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { client2.stage(); client2.set("balance", ((int) (client2.get("balance", 1))) - 200, 1); if((int) client2.get("balance", 1) + (int) client2.get("balance", 2) >= 0) { client2.commit(); } } catch (TransactionException e) { client2.abort(); } finally { done2.set(true); } } }); t1.start(); t2.start(); while (!done1.get() || !done2.get()) { continue; } Assert.assertEquals(0, (int) client.get("balance", 1) + (int) client.get("balance", 2)); } }