@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(); } }