private byte[] encodeStateColumn(ConsumerEntryState state) { // State column content is encoded as (writePointer) + (instanceId) + (state) byte[] stateContent = new byte[Longs.BYTES + Ints.BYTES + 1]; Bytes.putLong(stateContent, 0, transaction.getWritePointer()); Bytes.putInt(stateContent, Longs.BYTES, consumerConfig.getInstanceId()); Bytes.putByte(stateContent, Longs.BYTES + Ints.BYTES, state.getState()); return stateContent; }
@Test public void testCheckpointInvalidate() throws Exception { // start a transaction, using checkpoints between writes transactionContext.start(); Transaction origTx = transactionContext.getCurrentTransaction(); transactionAwareHTable.put( new Put(TestBytes.row).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); transactionContext.checkpoint(); Transaction checkpointTx1 = transactionContext.getCurrentTransaction(); transactionAwareHTable.put( new Put(TestBytes.row2).add(TestBytes.family, TestBytes.qualifier, TestBytes.value2)); transactionContext.checkpoint(); Transaction checkpointTx2 = transactionContext.getCurrentTransaction(); transactionAwareHTable.put( new Put(TestBytes.row3).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); TransactionSystemClient txClient = new InMemoryTxSystemClient(txManager); txClient.invalidate(transactionContext.getCurrentTransaction().getTransactionId()); // check that writes are not visible TransactionAwareHTable txTable2 = new TransactionAwareHTable(new HTable(conf, TestBytes.table)); TransactionContext txContext2 = new TransactionContext(txClient, txTable2); txContext2.start(); Transaction newTx = txContext2.getCurrentTransaction(); // all 3 writes pointers from the previous transaction should now be excluded assertTrue(newTx.isExcluded(origTx.getWritePointer())); assertTrue(newTx.isExcluded(checkpointTx1.getWritePointer())); assertTrue(newTx.isExcluded(checkpointTx2.getWritePointer())); verifyRow(txTable2, TestBytes.row, null); verifyRow(txTable2, TestBytes.row2, null); verifyRow(txTable2, TestBytes.row3, null); Scan scan = new Scan(); ResultScanner scanner = txTable2.getScanner(scan); assertNull(scanner.next()); scanner.close(); txContext2.finish(); }
private void populateRowCache(Set<byte[]> excludeRows, int maxBatchSize) throws IOException { long readPointer = transaction.getReadPointer(); // Scan the table for queue entries. int numRows = Math.max(MIN_FETCH_ROWS, maxBatchSize * PREFETCH_BATCHES); if (scanStartRow == null) { scanStartRow = Arrays.copyOf(startRow, startRow.length); } QueueScanner scanner = getScanner( scanStartRow, QueueEntryRow.getStopRowForTransaction(queueRowPrefix, transaction), numRows); try { // Try fill up the cache boolean firstScannedRow = true; while (entryCache.size() < numRows) { ImmutablePair<byte[], Map<byte[], byte[]>> entry = scanner.next(); if (entry == null) { // No more result, breaking out. break; } byte[] rowKey = entry.getFirst(); if (excludeRows.contains(rowKey)) { continue; } // Row key is queue_name + writePointer + counter long writePointer = Bytes.toLong(rowKey, queueRowPrefix.length, Longs.BYTES); // If it is first row returned by the scanner and was written before the earliest in // progress, // it's safe to advance scanStartRow to current row because nothing can be written before // this row. if (firstScannedRow && writePointer < transaction.getFirstInProgress()) { firstScannedRow = false; scanStartRow = Arrays.copyOf(rowKey, rowKey.length); } // If writes later than the reader pointer, abort the loop, as entries that comes later are // all uncommitted. // this is probably not needed due to the limit of the scan to the stop row, but to be // safe... if (writePointer > readPointer) { break; } // If the write is in the excluded list, ignore it. if (transaction.isExcluded(writePointer)) { continue; } // Based on the strategy to determine if include the given entry or not. byte[] dataBytes = entry.getSecond().get(QueueEntryRow.DATA_COLUMN); byte[] metaBytes = entry.getSecond().get(QueueEntryRow.META_COLUMN); if (dataBytes == null || metaBytes == null) { continue; } byte[] stateBytes = entry.getSecond().get(stateColumnName); int counter = Bytes.toInt(rowKey, rowKey.length - 4, Ints.BYTES); if (!shouldInclude(writePointer, counter, metaBytes, stateBytes)) { continue; } entryCache.put(rowKey, new SimpleQueueEntry(rowKey, dataBytes, stateBytes)); } } finally { scanner.close(); } }
@Test public void testCheckpoint() throws Exception { // start a transaction, using checkpoints between writes transactionContext.start(); transactionAwareHTable.put( new Put(TestBytes.row).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); Transaction origTx = transactionContext.getCurrentTransaction(); transactionContext.checkpoint(); Transaction postCheckpointTx = transactionContext.getCurrentTransaction(); assertEquals(origTx.getTransactionId(), postCheckpointTx.getTransactionId()); assertNotEquals(origTx.getWritePointer(), postCheckpointTx.getWritePointer()); long[] checkpointPtrs = postCheckpointTx.getCheckpointWritePointers(); assertEquals(1, checkpointPtrs.length); assertEquals(postCheckpointTx.getWritePointer(), checkpointPtrs[0]); transactionAwareHTable.put( new Put(TestBytes.row2).add(TestBytes.family, TestBytes.qualifier, TestBytes.value2)); transactionContext.checkpoint(); Transaction postCheckpointTx2 = transactionContext.getCurrentTransaction(); assertEquals(origTx.getTransactionId(), postCheckpointTx2.getTransactionId()); assertNotEquals(postCheckpointTx.getWritePointer(), postCheckpointTx2.getWritePointer()); long[] checkpointPtrs2 = postCheckpointTx2.getCheckpointWritePointers(); assertEquals(2, checkpointPtrs2.length); assertEquals(postCheckpointTx.getWritePointer(), checkpointPtrs2[0]); assertEquals(postCheckpointTx2.getWritePointer(), checkpointPtrs2[1]); transactionAwareHTable.put( new Put(TestBytes.row3).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); // by default, all rows should be visible with Read-Your-Writes verifyRow(transactionAwareHTable, TestBytes.row, TestBytes.value); verifyRow(transactionAwareHTable, TestBytes.row2, TestBytes.value2); verifyRow(transactionAwareHTable, TestBytes.row3, TestBytes.value); // when disabling current write pointer, only the previous checkpoints should be visible transactionContext .getCurrentTransaction() .setVisibility(Transaction.VisibilityLevel.SNAPSHOT_EXCLUDE_CURRENT); Get get = new Get(TestBytes.row); verifyRow(transactionAwareHTable, get, TestBytes.value); get = new Get(TestBytes.row2); verifyRow(transactionAwareHTable, get, TestBytes.value2); get = new Get(TestBytes.row3); verifyRow(transactionAwareHTable, get, null); // test scan results excluding current write pointer Scan scan = new Scan(); ResultScanner scanner = transactionAwareHTable.getScanner(scan); Result row = scanner.next(); assertNotNull(row); assertArrayEquals(TestBytes.row, row.getRow()); assertEquals(1, row.size()); assertArrayEquals(TestBytes.value, row.getValue(TestBytes.family, TestBytes.qualifier)); row = scanner.next(); assertNotNull(row); assertArrayEquals(TestBytes.row2, row.getRow()); assertEquals(1, row.size()); assertArrayEquals(TestBytes.value2, row.getValue(TestBytes.family, TestBytes.qualifier)); row = scanner.next(); assertNull(row); scanner.close(); transactionContext.getCurrentTransaction().setVisibility(Transaction.VisibilityLevel.SNAPSHOT); // check that writes are still not visible to other clients TransactionAwareHTable txTable2 = new TransactionAwareHTable(new HTable(conf, TestBytes.table)); TransactionContext txContext2 = new TransactionContext(new InMemoryTxSystemClient(txManager), txTable2); txContext2.start(); verifyRow(txTable2, TestBytes.row, null); verifyRow(txTable2, TestBytes.row2, null); verifyRow(txTable2, TestBytes.row3, null); txContext2.finish(); // commit transaction, verify writes are visible transactionContext.finish(); txContext2.start(); verifyRow(txTable2, TestBytes.row, TestBytes.value); verifyRow(txTable2, TestBytes.row2, TestBytes.value2); verifyRow(txTable2, TestBytes.row3, TestBytes.value); txContext2.finish(); txTable2.close(); }
@Test public void testNoneLevelConflictDetection() throws Exception { InMemoryTxSystemClient txClient = new InMemoryTxSystemClient(txManager); TransactionAwareHTable txTable1 = new TransactionAwareHTable( new HTable(conf, TestBytes.table), TxConstants.ConflictDetection.NONE); TransactionContext txContext1 = new TransactionContext(txClient, txTable1); TransactionAwareHTable txTable2 = new TransactionAwareHTable( new HTable(conf, TestBytes.table), TxConstants.ConflictDetection.NONE); TransactionContext txContext2 = new TransactionContext(txClient, txTable2); // overlapping writes to the same row + column should not conflict txContext1.start(); txTable1.put( new Put(TestBytes.row).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); // changes should not be visible yet txContext2.start(); Result row = txTable2.get(new Get(TestBytes.row)); assertTrue(row.isEmpty()); txTable2.put( new Put(TestBytes.row).add(TestBytes.family, TestBytes.qualifier, TestBytes.value2)); // both commits should succeed txContext1.finish(); txContext2.finish(); txContext1.start(); row = txTable1.get(new Get(TestBytes.row)); assertFalse(row.isEmpty()); assertArrayEquals(TestBytes.value2, row.getValue(TestBytes.family, TestBytes.qualifier)); txContext1.finish(); // transaction abort should still rollback changes txContext1.start(); txTable1.put( new Put(TestBytes.row2).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); txContext1.abort(); // changes to row2 should be rolled back txContext2.start(); Result row2 = txTable2.get(new Get(TestBytes.row2)); assertTrue(row2.isEmpty()); txContext2.finish(); // transaction invalidate should still make changes invisible txContext1.start(); Transaction tx1 = txContext1.getCurrentTransaction(); txTable1.put( new Put(TestBytes.row3).add(TestBytes.family, TestBytes.qualifier, TestBytes.value)); assertNotNull(tx1); txClient.invalidate(tx1.getWritePointer()); // changes to row2 should be rolled back txContext2.start(); Result row3 = txTable2.get(new Get(TestBytes.row3)); assertTrue(row3.isEmpty()); txContext2.finish(); }