use of com.palantir.atlasdb.keyvalue.api.Value in project atlasdb by palantir.
the class SnapshotTransaction method getRanges.
@Override
public Iterable<BatchingVisitable<RowResult<byte[]>>> getRanges(final TableReference tableRef, Iterable<RangeRequest> rangeRequests) {
checkGetPreconditions(tableRef);
if (perfLogger.isDebugEnabled()) {
perfLogger.debug("Passed {} ranges to getRanges({}, {})", Iterables.size(rangeRequests), tableRef, rangeRequests);
}
if (!Iterables.isEmpty(rangeRequests)) {
hasReads = true;
}
return FluentIterable.from(Iterables.partition(rangeRequests, BATCH_SIZE_GET_FIRST_PAGE)).transformAndConcat(input -> {
Timer.Context timer = getTimer("processedRangeMillis").time();
Map<RangeRequest, TokenBackedBasicResultsPage<RowResult<Value>, byte[]>> firstPages = keyValueService.getFirstBatchForRanges(tableRef, input, getStartTimestamp());
validateExternalAndCommitLocksIfNecessary(tableRef, getStartTimestamp());
SortedMap<Cell, byte[]> postFiltered = postFilterPages(tableRef, firstPages.values());
List<BatchingVisitable<RowResult<byte[]>>> ret = Lists.newArrayListWithCapacity(input.size());
for (RangeRequest rangeRequest : input) {
TokenBackedBasicResultsPage<RowResult<Value>, byte[]> prePostFilter = firstPages.get(rangeRequest);
byte[] nextStartRowName = getNextStartRowName(rangeRequest, prePostFilter);
List<Entry<Cell, byte[]>> mergeIterators = getPostFilteredWithLocalWrites(tableRef, postFiltered, rangeRequest, prePostFilter.getResults(), nextStartRowName);
ret.add(new AbstractBatchingVisitable<RowResult<byte[]>>() {
@Override
protected <K extends Exception> void batchAcceptSizeHint(int batchSizeHint, ConsistentVisitor<RowResult<byte[]>, K> visitor) throws K {
checkGetPreconditions(tableRef);
final Iterator<RowResult<byte[]>> rowResults = Cells.createRowView(mergeIterators);
while (rowResults.hasNext()) {
if (!visitor.visit(ImmutableList.of(rowResults.next()))) {
return;
}
}
if ((nextStartRowName.length == 0) || !prePostFilter.moreResultsAvailable()) {
return;
}
RangeRequest newRange = rangeRequest.getBuilder().startRowInclusive(nextStartRowName).build();
getRange(tableRef, newRange).batchAccept(batchSizeHint, visitor);
}
});
}
long processedRangeMillis = TimeUnit.NANOSECONDS.toMillis(timer.stop());
log.trace("Processed {} range requests for {} in {}ms", SafeArg.of("numRequests", input.size()), LoggingArgs.tableRef(tableRef), SafeArg.of("millis", processedRangeMillis));
return ret;
});
}
use of com.palantir.atlasdb.keyvalue.api.Value in project atlasdb by palantir.
the class SnapshotTransaction method throwIfValueChangedConflict.
/**
* This will throw if we have a value changed conflict. This means that either we changed the
* value and anyone did a write after our start timestamp, or we just touched the value (put the
* same value as before) and a changed value was written after our start time.
*/
private void throwIfValueChangedConflict(TableReference table, Map<Cell, byte[]> writes, Set<CellConflict> spanningWrites, Set<CellConflict> dominatingWrites, LockToken commitLocksToken) {
Map<Cell, CellConflict> cellToConflict = Maps.newHashMap();
Map<Cell, Long> cellToTs = Maps.newHashMap();
for (CellConflict c : Sets.union(spanningWrites, dominatingWrites)) {
cellToConflict.put(c.getCell(), c);
cellToTs.put(c.getCell(), c.getTheirStart() + 1);
}
Map<Cell, byte[]> oldValues = getIgnoringLocalWrites(table, cellToTs.keySet());
Map<Cell, Value> conflictingValues = keyValueService.get(table, cellToTs);
Set<Cell> conflictingCells = Sets.newHashSet();
for (Entry<Cell, Long> cellEntry : cellToTs.entrySet()) {
Cell cell = cellEntry.getKey();
if (!writes.containsKey(cell)) {
Validate.isTrue(false, "Missing write for cell: %s for table %s", cellToConflict.get(cell), table);
}
if (!conflictingValues.containsKey(cell)) {
// This error case could happen if our locks expired.
throwIfPreCommitRequirementsNotMet(commitLocksToken, getStartTimestamp());
Validate.isTrue(false, "Missing conflicting value for cell: %s for table %s", cellToConflict.get(cell), table);
}
if (conflictingValues.get(cell).getTimestamp() != (cellEntry.getValue() - 1)) {
// This error case could happen if our locks expired.
throwIfPreCommitRequirementsNotMet(commitLocksToken, getStartTimestamp());
Validate.isTrue(false, "Wrong timestamp for cell in table %s Expected: %s Actual: %s", table, cellToConflict.get(cell), conflictingValues.get(cell));
}
@Nullable byte[] oldVal = oldValues.get(cell);
byte[] writeVal = writes.get(cell);
byte[] conflictingVal = conflictingValues.get(cell).getContents();
if (!Transactions.cellValuesEqual(oldVal, writeVal) || !Arrays.equals(writeVal, conflictingVal)) {
conflictingCells.add(cell);
} else if (log.isInfoEnabled()) {
log.info("Another transaction committed to the same cell before us but their value was the same." + " Cell: {} Table: {}", UnsafeArg.of("cell", cell), LoggingArgs.tableRef(table));
}
}
if (conflictingCells.isEmpty()) {
return;
}
Predicate<CellConflict> conflicting = Predicates.compose(Predicates.in(conflictingCells), CellConflict.getCellFunction());
getTransactionConflictsMeter().mark();
throw TransactionConflictException.create(table, getStartTimestamp(), Sets.filter(spanningWrites, conflicting), Sets.filter(dominatingWrites, conflicting), System.currentTimeMillis() - timeCreated);
}
use of com.palantir.atlasdb.keyvalue.api.Value in project atlasdb by palantir.
the class SnapshotTransaction method getWithPostFilteringInternal.
/**
* This will return all the keys that still need to be postFiltered. It will output properly
* postFiltered keys to the results output param.
*/
private <T> Map<Cell, Value> getWithPostFilteringInternal(TableReference tableRef, Map<Cell, Value> rawResults, @Output Map<Cell, T> results, Function<Value, T> transformer) {
Set<Long> startTimestampsForValues = getStartTimestampsForValues(rawResults.values());
Map<Long, Long> commitTimestamps = getCommitTimestamps(tableRef, startTimestampsForValues, true);
Map<Cell, Long> keysToReload = Maps.newHashMapWithExpectedSize(0);
Map<Cell, Long> keysToDelete = Maps.newHashMapWithExpectedSize(0);
for (Map.Entry<Cell, Value> e : rawResults.entrySet()) {
Cell key = e.getKey();
Value value = e.getValue();
if (value.getTimestamp() == Value.INVALID_VALUE_TIMESTAMP) {
getMeter(AtlasDbMetricNames.CellFilterMetrics.INVALID_START_TS).mark();
// we clean up old values, and this transaction started at a timestamp before the garbage collection.
switch(getReadSentinelBehavior()) {
case IGNORE:
break;
case THROW_EXCEPTION:
throw new TransactionFailedRetriableException("Tried to read a value that has been deleted. " + " This can be caused by hard delete transactions using the type " + TransactionType.AGGRESSIVE_HARD_DELETE + ". It can also be caused by transactions taking too long, or" + " its locks expired. Retrying it should work.");
default:
throw new IllegalStateException("Invalid read sentinel behavior " + getReadSentinelBehavior());
}
} else {
Long theirCommitTimestamp = commitTimestamps.get(value.getTimestamp());
if (theirCommitTimestamp == null || theirCommitTimestamp == TransactionConstants.FAILED_COMMIT_TS) {
keysToReload.put(key, value.getTimestamp());
if (shouldDeleteAndRollback()) {
// This is from a failed transaction so we can roll it back and then reload it.
keysToDelete.put(key, value.getTimestamp());
getMeter(AtlasDbMetricNames.CellFilterMetrics.INVALID_COMMIT_TS).mark();
}
} else if (theirCommitTimestamp > getStartTimestamp()) {
// The value's commit timestamp is after our start timestamp.
// This means the value is from a transaction which committed
// after our transaction began. We need to try reading at an
// earlier timestamp.
keysToReload.put(key, value.getTimestamp());
getMeter(AtlasDbMetricNames.CellFilterMetrics.COMMIT_TS_GREATER_THAN_TRANSACTION_TS).mark();
} else {
// The value has a commit timestamp less than our start timestamp, and is visible and valid.
if (value.getContents().length != 0) {
results.put(key, transformer.apply(value));
}
}
}
}
if (!keysToDelete.isEmpty()) {
// if we can't roll back the failed transactions, we should just try again
if (!rollbackFailedTransactions(tableRef, keysToDelete, commitTimestamps, defaultTransactionService)) {
return rawResults;
}
}
if (!keysToReload.isEmpty()) {
Map<Cell, Value> nextRawResults = keyValueService.get(tableRef, keysToReload);
validateExternalAndCommitLocksIfNecessary(tableRef, getStartTimestamp());
return nextRawResults;
} else {
return ImmutableMap.of();
}
}
use of com.palantir.atlasdb.keyvalue.api.Value in project atlasdb by palantir.
the class AbstractDbWriteTable method put.
@Override
public void put(Collection<Map.Entry<Cell, Value>> data) {
List<Object[]> args = Lists.newArrayListWithCapacity(data.size());
for (Entry<Cell, Value> entry : data) {
Cell cell = entry.getKey();
Value val = entry.getValue();
args.add(new Object[] { cell.getRowName(), cell.getColumnName(), val.getTimestamp(), val.getContents() });
}
put(args);
}
use of com.palantir.atlasdb.keyvalue.api.Value in project atlasdb by palantir.
the class DbKvs method fillOverflowValues.
private void fillOverflowValues(ConnectionSupplier conns, TableReference tableRef, Map<Cell, OverflowValue> overflowValues, @Output Map<Cell, Value> values) {
Iterator<Entry<Cell, OverflowValue>> overflowIterator = overflowValues.entrySet().iterator();
while (overflowIterator.hasNext()) {
Entry<Cell, OverflowValue> entry = overflowIterator.next();
Value value = values.get(entry.getKey());
if (value != null && value.getTimestamp() > entry.getValue().ts()) {
overflowIterator.remove();
}
}
Map<Long, byte[]> resolvedOverflowValues = overflowValueLoader.loadOverflowValues(conns, tableRef, Collections2.transform(overflowValues.values(), OverflowValue::id));
for (Entry<Cell, OverflowValue> entry : overflowValues.entrySet()) {
Cell cell = entry.getKey();
OverflowValue ov = entry.getValue();
byte[] val = resolvedOverflowValues.get(ov.id());
Preconditions.checkNotNull(val, "Failed to load overflow data: cell=%s, overflowId=%s", cell, ov.id());
values.put(cell, Value.create(val, ov.ts()));
}
}
Aggregations