/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.test.it.keyupdate; import com.foundationdb.ais.model.AkibanInformationSchema; import com.foundationdb.ais.model.Index; import com.foundationdb.ais.model.Table; import com.foundationdb.ais.model.TableIndex; import com.foundationdb.server.service.session.Session; import com.foundationdb.server.store.IndexKeyVisitor; import com.foundationdb.server.test.it.ITBase; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.CyclicBarrier; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; // Inspired by bug 985007 @Ignore public final class KeyUpdateAcrossTransactionsIT extends ITBase { @Before public final void before() throws Exception { testStore = new TestStore(store()); } @Test public void testUniqueViolationAcrossTransactions() throws Exception { final String TABLE_NAME = "t"; final String SCHEMA_NAME = "s"; final int tableId= createTable(SCHEMA_NAME, TABLE_NAME, "id int not null primary key", "u int", "unique(u)"); writeRows(row(tableId, 0, 0)); AkibanInformationSchema ais = ddl().getAIS(session()); Table table = ais.getTable(tableId); Index uIndex = null; for (TableIndex index : table.getIndexes()) { if (index.getKeyColumns().get(0).getColumn().getName().equals("u")) { uIndex = index; } } assertNotNull(uIndex); final Index finalUIndex = uIndex; CyclicBarrier barrier = new CyclicBarrier(2); TestThread t1 = createThread(barrier, tableId, 100, 999); TestThread t2 = createThread(barrier, tableId, 101, 999); t1.join(); t2.join(); final Set<Long> uniqueKeys = new HashSet<>(); transactionally(new Callable<Void>() { public Void call() throws Exception { testStore.traverse(session(), finalUIndex, new IndexKeyVisitor() { @Override protected void visit(List<?> key) { Long u = (Long) key.get(0); boolean added = uniqueKeys.add(u); assertTrue(key.toString(), added); } }, -1, 0); return null; } }); } public TestThread createThread(CyclicBarrier barrier, int tableId, int id, int u) { TestThread thread = new TestThread(barrier, tableId, id, u); thread.start(); return thread; } private TestStore testStore; private class TestThread extends Thread { @Override public void run() { try { Session session = createNewSession(); txnService().beginTransaction(session); try { System.out.println(String.format("(%s, %s), %s: Starting", id, u, session)); barrier.await(); System.out.println(String.format("(%s, %s), %s: About to write", id, u, session)); writeRows(session, Arrays.asList(row(session, tableId, id, u))); System.out.println(String.format("(%s, %s), %s: Wrote", id, u, session)); barrier.await(); System.out.println(String.format("(%s, %s), %s: Exiting", id, u, session)); txnService().commitTransaction(session); } finally { txnService().rollbackTransactionIfOpen(session); } } catch (Exception e) { fail(e.getMessage()); } } public TestThread(CyclicBarrier barrier, int tableId, int id, int u) { this.barrier = barrier; this.tableId = tableId; this.id = id; this.u = u; } private final CyclicBarrier barrier; private final int tableId; private final int id; private final int u; } }