Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
Fix NPE caused by txn commit race condition (#5940)
Browse files Browse the repository at this point in the history
Fixed a race condition where if a transaction was just committed by another thread, we may return null values from TransactionService.get.
  • Loading branch information
gsheasby authored Mar 8, 2022
1 parent 8ec85e3 commit 50349f6
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ private Map<Long, Long> processReads(Map<Cell, byte[]> reads, Map<Long, Cell> st
// if we reach here, actual is guaranteed to be a staging value
try {
store.checkAndTouch(cell, actual);
checkAndTouch.put(cell, actual);
} catch (CheckAndSetException e) {
PutUnlessExistsValue<Long> kvsValue = encodingStrategy.decodeValueAsCommitTimestamp(
startTs, Iterables.getOnlyElement(e.getActualValues()));
Expand All @@ -100,10 +101,11 @@ private Map<Long, Long> processReads(Map<Cell, byte[]> reads, Map<Long, Cell> st
+ "was found in the KVS",
SafeArg.of("kvsValue", kvsValue),
SafeArg.of("stagingValue", currentValue));
continue;
} finally {
// If we got here after catching CheckAndSetException, then some other thread committed this
// transaction, and we must therefore return the commit timestamp.
resultBuilder.put(startTs, commitTs);
}
checkAndTouch.put(cell, actual);
resultBuilder.put(startTs, commitTs);
}
store.put(KeyedStream.stream(checkAndTouch)
.map(encodingStrategy::transformStagingToCommitted)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.CheckAndSetException;
import com.palantir.atlasdb.keyvalue.api.KeyAlreadyExistsException;
import com.palantir.atlasdb.keyvalue.api.TableReference;
import com.palantir.atlasdb.keyvalue.impl.InMemoryKeyValueService;
Expand Down Expand Up @@ -86,6 +88,29 @@ public void pueThatThrowsIsCorrectedOnGet() throws ExecutionException, Interrupt
verify(spiedStore).put(anyMap());
}

@Test
public void getReturnsStagingValuesThatWereCommittedBySomeoneElse()
throws ExecutionException, InterruptedException {
TwoPhaseEncodingStrategy strategy = TwoPhaseEncodingStrategy.INSTANCE;

long startTimestamp = 1L;
long commitTimestamp = 2L;
Cell timestampAsCell = strategy.encodeStartTimestampAsCell(startTimestamp);
byte[] stagingValue =
strategy.encodeCommitTimestampAsValue(startTimestamp, PutUnlessExistsValue.staging(commitTimestamp));
byte[] committedValue =
strategy.encodeCommitTimestampAsValue(startTimestamp, PutUnlessExistsValue.committed(commitTimestamp));
spiedStore.putUnlessExists(timestampAsCell, stagingValue);

List<byte[]> actualValues = ImmutableList.of(committedValue);

doThrow(new CheckAndSetException("done elsewhere", timestampAsCell, stagingValue, actualValues))
.when(spiedStore)
.checkAndTouch(timestampAsCell, stagingValue);

assertThat(pueTable.get(startTimestamp).get()).isEqualTo(commitTimestamp);
}

@Test
public void onceNonNullValueIsReturnedItIsAlwaysReturned() {
PutUnlessExistsTable<Long, Long> putUnlessExistsTable = new ResilientCommitTimestampPutUnlessExistsTable(
Expand Down
8 changes: 8 additions & 0 deletions changelog/@unreleased/pr-5940.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
type: fix
fix:
description: 'Fixed a race condition where if a transaction was just committed by
another thread, users would receive null values from TransactionService.get. This
caused users to observe that a completed transaction was still running, but did
not lead to any correctness issue. '
links:
- https://github.com/palantir/atlasdb/pull/5940

0 comments on commit 50349f6

Please sign in to comment.