@Override protected SnapshotTransaction createTransaction(long immutableTimestamp, Supplier<Long> startTimestampSupplier, LockToken immutableTsLock, PreCommitCondition preCommitCondition) { return new SerializableTransaction( metricsManager, keyValueService, timelockService, transactionService, cleaner, startTimestampSupplier, getConflictDetectionManager(), sweepStrategyManager, immutableTimestamp, Optional.of(immutableTsLock), preCommitCondition, constraintModeSupplier.get(), cleaner.getTransactionReadTimeoutMillis(), TransactionReadSentinelBehavior.THROW_EXCEPTION, allowHiddenTableAccess, timestampValidationReadCache, getRangesExecutor, defaultGetRangesConcurrency, sweepQueueWriter, deleteExecutor, commitProfileProcessor, validateLocksOnReads, transactionConfig); }
private void verifyCells(Transaction readOnlyTransaction) { for (Entry<TableReference, Set<Cell>> tableAndCellsEntry : cellsRead.entrySet()) { TableReference table = tableAndCellsEntry.getKey(); Set<Cell> cells = tableAndCellsEntry.getValue(); final ConcurrentNavigableMap<Cell, byte[]> readsForTable = getReadsForTable(table); for (Iterable<Cell> batch : Iterables.partition(cells, BATCH_SIZE)) { // We don't want to verify any reads that we wrote to cause we will just read our own values. // NB: If the value has changed between read and write, our normal SI checking handles this case Iterable<Cell> batchWithoutWrites = writesByTable.get(table) != null ? Iterables.filter(batch, Predicates.not(Predicates.in(writesByTable.get(table).keySet()))) : batch; ImmutableSet<Cell> batchWithoutWritesSet = ImmutableSet.copyOf(batchWithoutWrites); Map<Cell, byte[]> currentBatch = readOnlyTransaction.get(table, batchWithoutWritesSet); ImmutableMap<Cell, byte[]> originalReads = Maps.toMap( Sets.intersection(batchWithoutWritesSet, readsForTable.keySet()), Functions.forMap(readsForTable)); if (!areMapsEqual(currentBatch, originalReads)) { handleTransactionConflict(table); } } } }
BatchColumnRangeSelection range = e.getKey(); byte[] rangeEnd = e.getValue(); rangesToRows.put(nextLexicographicalRangeEnd(range, rangeEnd), row); BatchingVisitableView.of(cellValuesForRow.getValue()); NavigableMap<Cell, ByteBuffer> readsInRange = Maps.transformValues( getReadsInColumnRangeSkippingWrites(table, row, columnRange), ByteBuffer::wrap); boolean isEqual = visitable.transformBatch(cellValues -> filterWritesFromCells(cellValues, table)) .isEqual(readsInRange.entrySet()); if (!isEqual) { handleTransactionConflict(table);
@Override protected void throwIfReadWriteConflictForSerializable(long commitTimestamp) { Transaction ro = getReadOnlyTransaction(commitTimestamp); verifyRanges(ro); verifyColumnRanges(ro); verifyCells(ro); verifyRows(ro); }
private void markCellsRead(TableReference table, Set<Cell> searched, Map<Cell, byte[]> result) { if (!isSerializableTable(table)) { return; } getReadsForTable(table).putAll(transformGetsForTesting(result)); Set<Cell> cellsForTable = cellsRead.computeIfAbsent(table, unused -> Sets.newConcurrentHashSet()); cellsForTable.addAll(searched); }
private void markRowColumnRangeRead( TableReference table, byte[] row, BatchColumnRangeSelection range, List<Entry<Cell, byte[]>> result) { if (!isSerializableTable(table)) { return; } ConcurrentNavigableMap<Cell, byte[]> reads = getReadsForTable(table); Map<Cell, byte[]> map = Maps2.fromEntries(result); reads.putAll(transformGetsForTesting(map)); setColumnRangeEnd(table, row, range, Iterables.getLast(result).getKey().getColumnName()); }
private void markRangeRead(TableReference table, RangeRequest range, List<RowResult<byte[]>> result) { if (!isSerializableTable(table)) { return; } ConcurrentNavigableMap<Cell, byte[]> reads = getReadsForTable(table); for (RowResult<byte[]> row : result) { Map<Cell, byte[]> map = Maps2.fromEntries(row.getCells()); map = transformGetsForTesting(map); reads.putAll(map); } setRangeEnd(table, range, Iterables.getLast(result).getRowName()); }
private void verifyRanges(Transaction readOnlyTransaction) { // verify each set of reads to ensure they are the same. for (Entry<TableReference, ConcurrentMap<RangeRequest, byte[]>> tableAndRange : rangeEndByTable.entrySet()) { TableReference table = tableAndRange.getKey(); Map<RangeRequest, byte[]> rangeEnds = tableAndRange.getValue(); for (Entry<RangeRequest, byte[]> rangeAndRangeEndEntry : rangeEnds.entrySet()) { RangeRequest range = rangeAndRangeEndEntry.getKey(); byte[] rangeEnd = rangeAndRangeEndEntry.getValue(); if (rangeEnd.length != 0 && !RangeRequests.isTerminalRow(range.isReverse(), rangeEnd)) { range = range.getBuilder() .endRowExclusive(RangeRequests.getNextStartRow(range.isReverse(), rangeEnd)) .build(); } ConcurrentNavigableMap<Cell, byte[]> writes = writesByTable.get(table); BatchingVisitableView<RowResult<byte[]>> bv = BatchingVisitableView.of( readOnlyTransaction.getRange(table, range)); NavigableMap<Cell, ByteBuffer> readsInRange = Maps.transformValues( getReadsInRange(table, range), ByteBuffer::wrap); if (!bv.transformBatch(input -> filterWritesFromRows(input, writes)).isEqual(readsInRange.entrySet())) { handleTransactionConflict(table); } } } }
private void verifyRows(Transaction ro) { for (Map.Entry<TableReference, Set<RowRead>> tableAndRowsEntry : rowsRead.entrySet()) { TableReference table = tableAndRowsEntry.getKey(); Set<RowRead> rows = tableAndRowsEntry.getValue(); ConcurrentNavigableMap<Cell, byte[]> readsForTable = getReadsForTable(table); Multimap<ColumnSelection, byte[]> rowsReadByColumns = Multimaps.newSortedSetMultimap( Maps.newHashMap(), () -> Sets.newTreeSet(UnsignedBytes.lexicographicalComparator())); for (RowRead r : rows) { rowsReadByColumns.putAll(r.cols, r.rows); } for (ColumnSelection cols : rowsReadByColumns.keySet()) { verifyColumns(ro, table, readsForTable, rowsReadByColumns, cols); } } }
handleTransactionConflict(table); Predicates.not(Predicates.in(writesByTable.get(table).keySet()))); if (!areMapsEqual(orignalReads, currentCells)) { handleTransactionConflict(table);
private List<Entry<Cell, ByteBuffer>> filterWritesFromCells( Iterable<Entry<Cell, byte[]>> cells, TableReference table) { return filterWritesFromCells(cells, writesByTable.get(table)); }
private NavigableMap<Cell, byte[]> getReadsInRange(TableReference table, RangeRequest range) { NavigableMap<Cell, byte[]> reads = getReadsForTable(table); if (range.getStartInclusive().length != 0) { reads = reads.tailMap(Cells.createSmallestCellForRow(range.getStartInclusive()), true); } if (range.getEndExclusive().length != 0) { reads = reads.headMap(Cells.createSmallestCellForRow(range.getEndExclusive()), false); } Map<Cell, byte[]> writes = writesByTable.get(table); if (writes != null) { reads = Maps.filterKeys(reads, Predicates.not(Predicates.in(writes.keySet()))); } if (!range.getColumnNames().isEmpty()) { Predicate<Cell> columnInNames = Predicates.compose( Predicates.in(range.getColumnNames()), Cell::getColumnName); reads = Maps.filterKeys(reads, columnInNames); } return reads; }
private void handleTransactionConflict(TableReference tableRef) { transactionOutcomeMetrics.markReadWriteConflict(tableRef); throw TransactionSerializableConflictException.create(tableRef, getTimestamp(), System.currentTimeMillis() - timeCreated); } }
AtlasDbConstraintCheckingMode.NO_CONSTRAINT_CHECKING, transactionReadTimeoutMillis, getReadSentinelBehavior(), allowHiddenTableAccess, timestampValidationReadCache,
@Override protected void throwIfReadWriteConflictForSerializable(long commitTimestamp) { Transaction ro = getReadOnlyTransaction(commitTimestamp); verifyRanges(ro); verifyColumnRanges(ro); verifyCells(ro); verifyRows(ro); }
private void markRowsRead( TableReference table, Iterable<byte[]> rows, ColumnSelection cols, Iterable<RowResult<byte[]>> result) { if (!isSerializableTable(table)) { return; } ConcurrentNavigableMap<Cell, byte[]> reads = getReadsForTable(table); for (RowResult<byte[]> row : result) { Map<Cell, byte[]> map = Maps2.fromEntries(row.getCells()); reads.putAll(transformGetsForTesting(map)); } Set<RowRead> rowReads = rowsRead.computeIfAbsent(table, unused -> Sets.newConcurrentHashSet()); rowReads.add(new RowRead(rows, cols)); }
private void markRangeRead(TableReference table, RangeRequest range, List<RowResult<byte[]>> result) { if (!isSerializableTable(table)) { return; } ConcurrentNavigableMap<Cell, byte[]> reads = getReadsForTable(table); for (RowResult<byte[]> row : result) { Map<Cell, byte[]> map = Maps2.fromEntries(row.getCells()); map = transformGetsForTesting(map); reads.putAll(map); } setRangeEnd(table, range, Iterables.getLast(result).getRowName()); }
private void markRowColumnRangeRead( TableReference table, byte[] row, BatchColumnRangeSelection range, List<Entry<Cell, byte[]>> result) { if (!isSerializableTable(table)) { return; } ConcurrentNavigableMap<Cell, byte[]> reads = getReadsForTable(table); Map<Cell, byte[]> map = Maps2.fromEntries(result); reads.putAll(transformGetsForTesting(map)); setColumnRangeEnd(table, row, range, Iterables.getLast(result).getKey().getColumnName()); }
private void verifyRanges(Transaction readOnlyTransaction) { // verify each set of reads to ensure they are the same. for (Entry<TableReference, ConcurrentMap<RangeRequest, byte[]>> tableAndRange : rangeEndByTable.entrySet()) { TableReference table = tableAndRange.getKey(); Map<RangeRequest, byte[]> rangeEnds = tableAndRange.getValue(); for (Entry<RangeRequest, byte[]> rangeAndRangeEndEntry : rangeEnds.entrySet()) { RangeRequest range = rangeAndRangeEndEntry.getKey(); byte[] rangeEnd = rangeAndRangeEndEntry.getValue(); if (rangeEnd.length != 0 && !RangeRequests.isTerminalRow(range.isReverse(), rangeEnd)) { range = range.getBuilder() .endRowExclusive(RangeRequests.getNextStartRow(range.isReverse(), rangeEnd)) .build(); } ConcurrentNavigableMap<Cell, byte[]> writes = writesByTable.get(table); BatchingVisitableView<RowResult<byte[]>> bv = BatchingVisitableView.of( readOnlyTransaction.getRange(table, range)); NavigableMap<Cell, ByteBuffer> readsInRange = Maps.transformValues( getReadsInRange(table, range), ByteBuffer::wrap); if (!bv.transformBatch(input -> filterWritesFromRows(input, writes)).isEqual(readsInRange.entrySet())) { handleTransactionConflict(table); } } } }
private void verifyRows(Transaction ro) { for (Map.Entry<TableReference, Set<RowRead>> tableAndRowsEntry : rowsRead.entrySet()) { TableReference table = tableAndRowsEntry.getKey(); Set<RowRead> rows = tableAndRowsEntry.getValue(); ConcurrentNavigableMap<Cell, byte[]> readsForTable = getReadsForTable(table); Multimap<ColumnSelection, byte[]> rowsReadByColumns = Multimaps.newSortedSetMultimap( Maps.newHashMap(), () -> Sets.newTreeSet(UnsignedBytes.lexicographicalComparator())); for (RowRead r : rows) { rowsReadByColumns.putAll(r.cols, r.rows); } for (ColumnSelection cols : rowsReadByColumns.keySet()) { verifyColumns(ro, table, readsForTable, rowsReadByColumns, cols); } } }