Viraj Jasani created PHOENIX-8053:
-------------------------------------

             Summary: Concurrency bugs related to conditional UPSERTs on same 
row
                 Key: PHOENIX-8053
                 URL: https://issues.apache.org/jira/browse/PHOENIX-8053
             Project: Phoenix
          Issue Type: Bug
            Reporter: Viraj Jasani


Concurrent conditional UPSERT on the same row can fail, return uncommitted 
data, or lose updates.

While one update on the data table is writing to its indexes, the update 
releases the row lock. A later concurrent update on the same row builds on the 
earlier update's pending state in memory (not from disk). However, this 
tracking of the pending updates can cause issues.

Some of the issues as of today:
 * *Non-retryable error after a no-op:* A conditional upsert (ON DUPLICATE KEY) 
whose condition is false changes nothing and needs to return early, but it 
stays recorded as the row's latest pending update. The next concurrent UPSERT 
on the row finds it as completed and fails with a non-retryable 
{*}IllegalArgumentException: the phase of the last batch cannot be POST{*}.
 * *No-op result based on uncommitted data:* A conditional upsert whose 
condition is false returns to the client right away. Its result, including the 
returned row, can be based on the earlier update's pending state in memory. If 
that update fails, the client was provided result based on uncommitted data.
 * *Lost update after a failed update:* The row's latest pending update fails 
while an earlier update is still pending. The next update reads the row from 
disk (latest update failed) and misses the earlier pending write (which has not 
completed yet), and then overwrites it once both commit.
 * *Pending tracking removed too early:* Some updates never register as pending 
on the row: whole-row upserts on an immutable table with a global or uncovered 
index, or on a mutable table whose only indexes are uncovered. When such an 
update completes, it still removes the row's pending entry, even the one that a 
later update created. The next update reads from disk and misses a write that 
is still in progress.

Also, the batches chained on a busy row (the pending ones and the ones already 
done) stay in memory as long as the row has updates in flight.

Sample Exception for first case:

 
{code:java}
Caused by: org.apache.phoenix.execute.CommitException: 
org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException: Failed 1 
action: org.apache.phoenix.hbase.index.builder.IndexBuildingFailureException: 
Failed to build index for unexpected reason! at 
org.apache.phoenix.hbase.index.util.IndexManagementUtil.rethrowIndexingException(IndexManagementUtil.java:218)
       at 
org.apache.phoenix.hbase.index.IndexRegionObserver.preBatchMutate(IndexRegionObserver.java:706)
      at 
org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost$28.call(RegionCoprocessorHost.java:1002)
  at 
org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost$28.call(RegionCoprocessorHost.java:999)
   at 
org.apache.hadoop.hbase.coprocessor.CoprocessorHost$ObserverOperationWithoutResult.callObserver(CoprocessorHost.java:558)
    at 
org.apache.hadoop.hbase.coprocessor.CoprocessorHost.execOperation(CoprocessorHost.java:631)
  at 
org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost.preBatchMutate(RegionCoprocessorHost.java:999)
    at 
org.apache.hadoop.hbase.regionserver.HRegion$MutationBatchOperation.prepareMiniBatchOperations(HRegion.java:3859)
    at 
org.apache.hadoop.hbase.regionserver.HRegion.doMiniBatchMutate(HRegion.java:4577)
    at 
org.apache.hadoop.hbase.regionserver.HRegion.batchMutate(HRegion.java:4515)  at 
org.apache.hadoop.hbase.regionserver.HRegion.batchMutate(HRegion.java:4437)  at 
org.apache.hadoop.hbase.regionserver.RSRpcServices.doBatchOp(RSRpcServices.java:1036)
        at 
org.apache.hadoop.hbase.regionserver.RSRpcServices.doNonAtomicBatchOp(RSRpcServices.java:954)
        at 
org.apache.hadoop.hbase.regionserver.RSRpcServices.doNonAtomicRegionMutation(RSRpcServices.java:919)
 at 
org.apache.hadoop.hbase.regionserver.RSRpcServices.multi(RSRpcServices.java:2897)
    at 
org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:45039)
   at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:416)       at 
org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:124)      at 
org.apache.hadoop.hbase.ipc.RpcHandler.run(RpcHandler.java:102)      at 
org.apache.hadoop.hbase.ipc.RpcHandler.run(RpcHandler.java:82)Caused by: 
java.lang.IllegalArgumentException: the phase of the last batch cannot be POST  
    at 
org.apache.phoenix.thirdparty.com.google.common.base.Preconditions.checkArgument(Preconditions.java:141)
     at 
org.apache.phoenix.hbase.index.IndexRegionObserver.getCurrentRowStates(IndexRegionObserver.java:1264)
        at 
org.apache.phoenix.hbase.index.IndexRegionObserver.preBatchMutateWithExceptions(IndexRegionObserver.java:2029)
       at 
org.apache.phoenix.hbase.index.IndexRegionObserver.preBatchMutate(IndexRegionObserver.java:703)
      ... 18 more
{code}
 

 



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to