/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.omid.transaction; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import java.io.IOException; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.Coprocessor; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.filter.BinaryComparator; import org.apache.hadoop.hbase.filter.CompareFilter; import org.apache.hadoop.hbase.filter.FamilyFilter; import org.apache.hadoop.hbase.filter.Filter; import org.apache.hadoop.hbase.filter.FilterList; import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; import org.apache.hadoop.hbase.filter.SubstringComparator; import org.apache.hadoop.hbase.util.Bytes; import org.apache.omid.TestUtils; import org.apache.omid.committable.CommitTable; import org.apache.omid.committable.hbase.HBaseCommitTableConfig; import org.apache.omid.metrics.NullMetricsProvider; import org.apache.omid.timestamp.storage.HBaseTimestampStorageConfig; import org.apache.omid.tso.TSOServer; import org.apache.omid.tso.TSOServerConfig; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import static org.testng.Assert.fail; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Guice; import com.google.inject.Injector; public class TestSnapshotFilter { private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotFilter.class); private static final String TEST_FAMILY = "test-fam"; private static final int MAX_VERSIONS = 3; private AbstractTransactionManager tm; private Injector injector; private Admin admin; private Configuration hbaseConf; private HBaseTestingUtility hbaseTestUtil; private MiniHBaseCluster hbaseCluster; private TSOServer tso; private CommitTable commitTable; private PostCommitActions syncPostCommitter; private Connection connection; @BeforeClass public void setupTestSnapshotFilter() throws Exception { TSOServerConfig tsoConfig = new TSOServerConfig(); tsoConfig.setPort(5679); tsoConfig.setConflictMapSize(1); tsoConfig.setWaitStrategy("LOW_CPU"); injector = Guice.createInjector(new TSOForSnapshotFilterTestModule(tsoConfig)); hbaseConf = injector.getInstance(Configuration.class); hbaseConf.setBoolean("omid.server.side.filter", true); hbaseConf.setInt("hbase.hconnection.threads.core", 5); hbaseConf.setInt("hbase.hconnection.threads.max", 10); // Tunn down handler threads in regionserver hbaseConf.setInt("hbase.regionserver.handler.count", 10); // Set to random port hbaseConf.setInt("hbase.master.port", 0); hbaseConf.setInt("hbase.master.info.port", 0); hbaseConf.setInt("hbase.regionserver.port", 0); hbaseConf.setInt("hbase.regionserver.info.port", 0); HBaseCommitTableConfig hBaseCommitTableConfig = injector.getInstance(HBaseCommitTableConfig.class); HBaseTimestampStorageConfig hBaseTimestampStorageConfig = injector.getInstance(HBaseTimestampStorageConfig.class); setupHBase(); connection = ConnectionFactory.createConnection(hbaseConf); admin = connection.getAdmin(); createRequiredHBaseTables(hBaseTimestampStorageConfig, hBaseCommitTableConfig); setupTSO(); commitTable = injector.getInstance(CommitTable.class); } private void setupHBase() throws Exception { LOG.info("--------------------------------------------------------------------------------------------------"); LOG.info("Setting up HBase"); LOG.info("--------------------------------------------------------------------------------------------------"); hbaseTestUtil = new HBaseTestingUtility(hbaseConf); LOG.info("--------------------------------------------------------------------------------------------------"); LOG.info("Creating HBase MiniCluster"); LOG.info("--------------------------------------------------------------------------------------------------"); hbaseCluster = hbaseTestUtil.startMiniCluster(1); } private void createRequiredHBaseTables(HBaseTimestampStorageConfig timestampStorageConfig, HBaseCommitTableConfig hBaseCommitTableConfig) throws IOException { createTableIfNotExists(timestampStorageConfig.getTableName(), timestampStorageConfig.getFamilyName().getBytes()); createTableIfNotExists(hBaseCommitTableConfig.getTableName(), hBaseCommitTableConfig.getCommitTableFamily(), hBaseCommitTableConfig.getLowWatermarkFamily()); } private void createTableIfNotExists(String tableName, byte[]... families) throws IOException { if (!admin.tableExists(TableName.valueOf(tableName))) { LOG.info("Creating {} table...", tableName); HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName)); for (byte[] family : families) { HColumnDescriptor datafam = new HColumnDescriptor(family); datafam.setMaxVersions(MAX_VERSIONS); desc.addFamily(datafam); } int priority = Coprocessor.PRIORITY_HIGHEST; desc.addCoprocessor(OmidSnapshotFilter.class.getName(),null,++priority,null); desc.addCoprocessor("org.apache.hadoop.hbase.coprocessor.AggregateImplementation",null,++priority,null); admin.createTable(desc); try { hbaseTestUtil.waitTableAvailable(TableName.valueOf(tableName),5000); } catch (InterruptedException e) { e.printStackTrace(); } } } private void setupTSO() throws IOException, InterruptedException { tso = injector.getInstance(TSOServer.class); tso.startAndWait(); TestUtils.waitForSocketListening("localhost", 5679, 100); Thread.currentThread().setName("UnitTest(s) thread"); } @AfterClass public void cleanupTestSnapshotFilter() throws Exception { teardownTSO(); hbaseCluster.shutdown(); } private void teardownTSO() throws IOException, InterruptedException { tso.stopAndWait(); TestUtils.waitForSocketNotListening("localhost", 5679, 1000); } @BeforeMethod public void setupTestSnapshotFilterIndividualTest() throws Exception { tm = spy((AbstractTransactionManager) newTransactionManager()); } private TransactionManager newTransactionManager() throws Exception { HBaseOmidClientConfiguration hbaseOmidClientConf = new HBaseOmidClientConfiguration(); hbaseOmidClientConf.setConnectionString("localhost:5679"); hbaseOmidClientConf.setHBaseConfiguration(hbaseConf); CommitTable.Client commitTableClient = commitTable.getClient(); syncPostCommitter = spy(new HBaseSyncPostCommitter(new NullMetricsProvider(),commitTableClient, connection)); return HBaseTransactionManager.builder(hbaseOmidClientConf) .postCommitter(syncPostCommitter) .commitTableClient(commitTableClient) .build(); } @Test(timeOut = 60_000) public void testGetFirstResult() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testGetFirstResult"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put row1 = new Put(rowName1); row1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, row1); tm.commit(tx1); Transaction tx2 = tm.begin(); Get get = new Get(rowName1); Result result = tt.get(tx2, get); assertTrue(!result.isEmpty(), "Result should not be empty!"); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); tm.commit(tx2); Transaction tx3 = tm.begin(); Put put3 = new Put(rowName1); put3.addColumn(famName1, colName1, dataValue1); tt.put(tx3, put3); tm.commit(tx3); Transaction tx4 = tm.begin(); Get get2 = new Get(rowName1); Result result2 = tt.get(tx4, get2); assertTrue(!result2.isEmpty(), "Result should not be empty!"); long tsRow2 = result2.rawCells()[0].getTimestamp(); assertEquals(tsRow2, tx3.getTransactionId(), "Reading differnt version"); tm.commit(tx4); tt.close(); } // This test will fail if filtering is done before snapshot filtering @Test(timeOut = 60_000) public void testServerSideSnapshotFiltering() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); byte[] dataValue2 = Bytes.toBytes("testWrite-2"); String TEST_TABLE = "testServerSideSnapshotFiltering"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName1, colName1, dataValue2); tt.put(tx2, put2); Transaction tx3 = tm.begin(); Get get = new Get(rowName1); // If snapshot filtering is not done in the server then the first value is // "testWrite-2" and the whole row will be filtered out. SingleColumnValueFilter filter = new SingleColumnValueFilter( famName1, colName1, CompareFilter.CompareOp.EQUAL, new SubstringComparator("testWrite-1")); get.setFilter(filter); Result results = tt.get(tx3, get); assertTrue(results.size() == 1); } // This test will fail if filtering is done before snapshot filtering @Test(timeOut = 60_000) public void testServerSideSnapshotScannerFiltering() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); byte[] dataValue2 = Bytes.toBytes("testWrite-2"); String TEST_TABLE = "testServerSideSnapshotFiltering"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName1, colName1, dataValue2); // tt.put(tx2, put2); Transaction tx3 = tm.begin(); // If snapshot filtering is not done in the server then the first value is // "testWrite-2" and the whole row will be filtered out. SingleColumnValueFilter filter = new SingleColumnValueFilter( famName1, colName1, CompareFilter.CompareOp.EQUAL, new SubstringComparator("testWrite-1")); Scan scan = new Scan(); scan.setFilter(filter); ResultScanner iterableRS = tt.getScanner(tx3, scan); Result result = iterableRS.next(); assertTrue(result.size() == 1); } @Test(timeOut = 60_000) public void testGetWithFamilyDelete() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] famName2 = Bytes.toBytes("test-fam2"); byte[] colName1 = Bytes.toBytes("col1"); byte[] colName2 = Bytes.toBytes("col2"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testGetWithFamilyDelete"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY), famName2); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName2, colName2, dataValue1); tt.put(tx2, put2); tm.commit(tx2); Transaction tx3 = tm.begin(); Delete d = new Delete(rowName1); d.addFamily(famName2); tt.delete(tx3, d); Transaction tx4 = tm.begin(); Get get = new Get(rowName1); Filter filter1 = new FilterList(FilterList.Operator.MUST_PASS_ONE, new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(TEST_FAMILY))), new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(famName2))); get.setFilter(filter1); Result result = tt.get(tx4, get); assertTrue(result.size() == 2, "Result should be 2"); try { tm.commit(tx3); } catch (RollbackException e) { if (!tm.isLowLatency()) fail(); } Transaction tx5 = tm.begin(); result = tt.get(tx5, get); if (!tm.isLowLatency()) assertTrue(result.size() == 1, "Result should be 1"); tt.close(); } @Test(timeOut = 60_000) public void testReadFromCommitTable() throws Exception { final byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); final String TEST_TABLE = "testReadFromCommitTable"; final byte[] famName2 = Bytes.toBytes("test-fam2"); final CountDownLatch readAfterCommit = new CountDownLatch(1); final CountDownLatch postCommitBegin = new CountDownLatch(1); final AtomicBoolean readFailed = new AtomicBoolean(false); final AbstractTransactionManager tm = (AbstractTransactionManager) newTransactionManager(); createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY), famName2); doAnswer(new Answer<ListenableFuture<Void>>() { @Override public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { LOG.info("Releasing readAfterCommit barrier"); readAfterCommit.countDown(); LOG.info("Waiting postCommitBegin barrier"); postCommitBegin.await(); ListenableFuture<Void> result = (ListenableFuture<Void>) invocation.callRealMethod(); return result; } }).when(syncPostCommitter).updateShadowCells(any(HBaseTransaction.class)); Thread readThread = new Thread("Read Thread") { @Override public void run() { try { LOG.info("Waiting readAfterCommit barrier"); readAfterCommit.await(); Transaction tx4 = tm.begin(); TTable tt = new TTable(connection, TEST_TABLE); Get get = new Get(rowName1); Filter filter1 = new FilterList(FilterList.Operator.MUST_PASS_ONE, new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(TEST_FAMILY))), new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(famName2))); get.setFilter(filter1); Result result = tt.get(tx4, get); if (result.size() == 2) { readFailed.set(false); } else { readFailed.set(false); } postCommitBegin.countDown(); } catch (Throwable e) { readFailed.set(false); LOG.error("Error whilst reading", e); } } }; readThread.start(); TTable table = new TTable(connection, TEST_TABLE); final HBaseTransaction t1 = (HBaseTransaction) tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); table.put(t1, put1); tm.commit(t1); readThread.join(); assertFalse(readFailed.get(), "Read should have succeeded"); } @Test(timeOut = 60_000) public void testGetWithFilter() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] famName2 = Bytes.toBytes("test-fam2"); byte[] colName1 = Bytes.toBytes("col1"); byte[] colName2 = Bytes.toBytes("col2"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testGetWithFilter"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY), famName2); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName2, colName2, dataValue1); tt.put(tx2, put2); tm.commit(tx2); Transaction tx3 = tm.begin(); Get get = new Get(rowName1); Filter filter1 = new FilterList(FilterList.Operator.MUST_PASS_ONE, new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(TEST_FAMILY))), new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(famName2))); get.setFilter(filter1); Result result = tt.get(tx3, get); assertTrue(result.size() == 2, "Result should be 2"); Filter filter2 = new FilterList(FilterList.Operator.MUST_PASS_ONE, new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(TEST_FAMILY)))); get.setFilter(filter2); result = tt.get(tx3, get); assertTrue(result.size() == 1, "Result should be 2"); tm.commit(tx3); tt.close(); } @Test(timeOut = 60_000) public void testGetSecondResult() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testGetSecondResult"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName1, colName1, dataValue1); tt.put(tx2, put2); Transaction tx3 = tm.begin(); Get get = new Get(rowName1); Result result = tt.get(tx3, get); assertTrue(!result.isEmpty(), "Result should not be empty!"); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); tm.commit(tx3); tt.close(); } @Test(timeOut = 60_000) public void testScanFirstResult() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testScanFirstResult"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put row1 = new Put(rowName1); row1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, row1); tm.commit(tx1); Transaction tx2 = tm.begin(); ResultScanner iterableRS = tt.getScanner(tx2, new Scan().setStartRow(rowName1).setStopRow(rowName1)); Result result = iterableRS.next(); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); assertFalse(iterableRS.next() != null); tm.commit(tx2); Transaction tx3 = tm.begin(); Put put3 = new Put(rowName1); put3.addColumn(famName1, colName1, dataValue1); tt.put(tx3, put3); tm.commit(tx3); Transaction tx4 = tm.begin(); ResultScanner iterableRS2 = tt.getScanner(tx4, new Scan().setStartRow(rowName1).setStopRow(rowName1)); Result result2 = iterableRS2.next(); long tsRow2 = result2.rawCells()[0].getTimestamp(); assertEquals(tsRow2, tx3.getTransactionId(), "Reading differnt version"); assertFalse(iterableRS2.next() != null); tm.commit(tx4); tt.close(); } @Test(timeOut = 60_000) public void testScanWithFilter() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] famName2 = Bytes.toBytes("test-fam2"); byte[] colName1 = Bytes.toBytes("col1"); byte[] colName2 = Bytes.toBytes("col2"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testScanWithFilter"; createTableIfNotExists(TEST_TABLE, famName1, famName2); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName2, colName2, dataValue1); tt.put(tx2, put2); tm.commit(tx2); Transaction tx3 = tm.begin(); Scan scan = new Scan(); scan.setFilter(new FilterList(FilterList.Operator.MUST_PASS_ONE, new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(TEST_FAMILY))))); scan.setStartRow(rowName1).setStopRow(rowName1); ResultScanner iterableRS = tt.getScanner(tx3, scan); Result result = iterableRS.next(); assertTrue(result.containsColumn(famName1, colName1)); assertFalse(result.containsColumn(famName2, colName2)); scan.setFilter(new FilterList(FilterList.Operator.MUST_PASS_ONE, new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(TEST_FAMILY))), new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(famName2)))); iterableRS = tt.getScanner(tx3, scan); result = iterableRS.next(); assertTrue(result.containsColumn(famName1, colName1)); assertTrue(result.containsColumn(famName2, colName2)); tm.commit(tx3); tt.close(); } @Test(timeOut = 60_000) public void testScanSecondResult() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] famName1 = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); String TEST_TABLE = "testScanSecondResult"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName1, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName1); put2.addColumn(famName1, colName1, dataValue1); tt.put(tx2, put2); Transaction tx3 = tm.begin(); ResultScanner iterableRS = tt.getScanner(tx3, new Scan().setStartRow(rowName1).setStopRow(rowName1)); Result result = iterableRS.next(); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); assertFalse(iterableRS.next() != null); tm.commit(tx3); tt.close(); } @Test (timeOut = 60_000) public void testScanFewResults() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] rowName2 = Bytes.toBytes("row2"); byte[] rowName3 = Bytes.toBytes("row3"); byte[] famName = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] colName2 = Bytes.toBytes("col2"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); byte[] dataValue2 = Bytes.toBytes("testWrite-2"); String TEST_TABLE = "testScanFewResults"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName, colName1, dataValue1); tt.put(tx1, put1); tm.commit(tx1); Transaction tx2 = tm.begin(); Put put2 = new Put(rowName2); put2.addColumn(famName, colName2, dataValue2); tt.put(tx2, put2); tm.commit(tx2); Transaction tx3 = tm.begin(); ResultScanner iterableRS = tt.getScanner(tx3, new Scan().setStartRow(rowName1).setStopRow(rowName3)); Result result = iterableRS.next(); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); result = iterableRS.next(); tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx2.getTransactionId(), "Reading differnt version"); assertFalse(iterableRS.next() != null); tm.commit(tx3); tt.close(); } @Test (timeOut = 60_000) public void testScanFewResultsDifferentTransaction() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] rowName2 = Bytes.toBytes("row2"); byte[] rowName3 = Bytes.toBytes("row3"); byte[] famName = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] colName2 = Bytes.toBytes("col2"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); byte[] dataValue2 = Bytes.toBytes("testWrite-2"); String TEST_TABLE = "testScanFewResultsDifferentTransaction"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName, colName1, dataValue1); tt.put(tx1, put1); Put put2 = new Put(rowName2); put2.addColumn(famName, colName2, dataValue2); tt.put(tx1, put2); tm.commit(tx1); Transaction tx2 = tm.begin(); put2 = new Put(rowName2); put2.addColumn(famName, colName2, dataValue2); tt.put(tx2, put2); tm.commit(tx2); Transaction tx3 = tm.begin(); ResultScanner iterableRS = tt.getScanner(tx3, new Scan().setStartRow(rowName1).setStopRow(rowName3)); Result result = iterableRS.next(); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); result = iterableRS.next(); tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx2.getTransactionId(), "Reading differnt version"); assertFalse(iterableRS.next() != null); tm.commit(tx3); tt.close(); } @Test (timeOut = 60_000) public void testScanFewResultsSameTransaction() throws Throwable { byte[] rowName1 = Bytes.toBytes("row1"); byte[] rowName2 = Bytes.toBytes("row2"); byte[] rowName3 = Bytes.toBytes("row3"); byte[] famName = Bytes.toBytes(TEST_FAMILY); byte[] colName1 = Bytes.toBytes("col1"); byte[] colName2 = Bytes.toBytes("col2"); byte[] dataValue1 = Bytes.toBytes("testWrite-1"); byte[] dataValue2 = Bytes.toBytes("testWrite-2"); String TEST_TABLE = "testScanFewResultsSameTransaction"; createTableIfNotExists(TEST_TABLE, Bytes.toBytes(TEST_FAMILY)); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put1 = new Put(rowName1); put1.addColumn(famName, colName1, dataValue1); tt.put(tx1, put1); Put put2 = new Put(rowName2); put2.addColumn(famName, colName2, dataValue2); tt.put(tx1, put2); tm.commit(tx1); Transaction tx2 = tm.begin(); put2 = new Put(rowName2); put2.addColumn(famName, colName2, dataValue2); tt.put(tx2, put2); Transaction tx3 = tm.begin(); ResultScanner iterableRS = tt.getScanner(tx3, new Scan().setStartRow(rowName1).setStopRow(rowName3)); Result result = iterableRS.next(); long tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); result = iterableRS.next(); tsRow = result.rawCells()[0].getTimestamp(); assertEquals(tsRow, tx1.getTransactionId(), "Reading differnt version"); assertFalse(iterableRS.next() != null); tm.commit(tx3); tt.close(); } @Test (timeOut = 60_000) public void testFilterCommitCacheInSnapshot() throws Throwable { String TEST_TABLE = "testFilterCommitCacheInSnapshot"; byte[] rowName = Bytes.toBytes("row1"); byte[] famName = Bytes.toBytes(TEST_FAMILY); createTableIfNotExists(TEST_TABLE, famName); TTable tt = new TTable(connection, TEST_TABLE); Transaction tx1 = tm.begin(); Put put = new Put(rowName); for (int i = 0; i < 200; ++i) { byte[] dataValue1 = Bytes.toBytes("some data"); byte[] colName = Bytes.toBytes("col" + i); put.addColumn(famName, colName, dataValue1); } tt.put(tx1, put); tm.commit(tx1); Transaction tx3 = tm.begin(); Table htable = connection.getTable(TableName.valueOf(TEST_TABLE)); SnapshotFilterImpl snapshotFilter = spy(new SnapshotFilterImpl(new HTableAccessWrapper(htable, htable), tm.getCommitTableClient())); Filter newFilter = TransactionFilters.getVisibilityFilter(null, snapshotFilter, (HBaseTransaction) tx3); Table rawTable = connection.getTable(TableName.valueOf(TEST_TABLE)); Scan scan = new Scan(); ResultScanner scanner = rawTable.getScanner(scan); for(Result row: scanner) { for(Cell cell: row.rawCells()) { newFilter.filterKeyValue(cell); } } verify(snapshotFilter, Mockito.times(0)) .getTSIfInSnapshot(any(Cell.class),any(HBaseTransaction.class), any(Map.class)); tm.commit(tx3); tt.close(); } @Test (timeOut = 60_000) public void testFilterCommitCacheNotInSnapshot() throws Throwable { String TEST_TABLE = "testFilterCommitCacheNotInSnapshot"; byte[] rowName = Bytes.toBytes("row1"); byte[] famName = Bytes.toBytes(TEST_FAMILY); createTableIfNotExists(TEST_TABLE, famName); TTable tt = new TTable(connection, TEST_TABLE); //add some uncommitted values Transaction tx1 = tm.begin(); Put put = new Put(rowName); for (int i = 0; i < 200; ++i) { byte[] dataValue1 = Bytes.toBytes("some data"); byte[] colName = Bytes.toBytes("col" + i); put.addColumn(famName, colName, dataValue1); } tt.put(tx1, put); //try to scan from tx Transaction tx = tm.begin(); Table htable = connection.getTable(TableName.valueOf(TEST_TABLE)); SnapshotFilterImpl snapshotFilter = spy(new SnapshotFilterImpl(new HTableAccessWrapper(htable, htable), tm.getCommitTableClient())); Filter newFilter = TransactionFilters.getVisibilityFilter(null, snapshotFilter, (HBaseTransaction) tx); Table rawTable = connection.getTable(TableName.valueOf(TEST_TABLE)); Scan scan = new Scan(); ResultScanner scanner = rawTable.getScanner(scan); for(Result row: scanner) { for(Cell cell: row.rawCells()) { newFilter.filterKeyValue(cell); } } verify(snapshotFilter, Mockito.times(1)) .getTSIfInSnapshot(any(Cell.class),any(HBaseTransaction.class), any(Map.class)); tt.close(); } }