@@ -357,10 +357,11 @@ public class RSRpcServices implements HBaseRPCErrorHandler, AdminService.Blockin
357357
358358 private ScannerIdGenerator scannerIdGenerator ;
359359 private final ConcurrentMap <String , RegionScannerHolder > scanners = new ConcurrentHashMap <>();
360- // Hold the name of a closed scanner for a while. This is used to keep compatible for old clients
361- // which may send next or close request to a region scanner which has already been exhausted. The
362- // entries will be removed automatically after scannerLeaseTimeoutPeriod.
363- private final Cache <String , String > closedScanners ;
360+ // Hold the name and last sequence number of a closed scanner for a while. This is used
361+ // to keep compatible for old clients which may send next or close request to a region
362+ // scanner which has already been exhausted. The entries will be removed automatically
363+ // after scannerLeaseTimeoutPeriod.
364+ private final Cache <String , Long > closedScanners ;
364365 /**
365366 * The lease timeout period for client scanners (milliseconds).
366367 */
@@ -3134,8 +3135,18 @@ private RegionScannerHolder getRegionScanner(ScanRequest request) throws IOExcep
31343135 RegionScannerHolder rsh = this .scanners .get (scannerName );
31353136 if (rsh == null ) {
31363137 // just ignore the next or close request if scanner does not exists.
3137- if (closedScanners .getIfPresent (scannerName ) != null ) {
3138- throw SCANNER_ALREADY_CLOSED ;
3138+ Long lastCallSeq = closedScanners .getIfPresent (scannerName );
3139+ if (lastCallSeq != null ) {
3140+ // Check the sequence number to catch if the last call was incorrectly retried.
3141+ // The only allowed scenario is when the scanner is exhausted and one more scan
3142+ // request arrives - in this case returning 0 rows is correct.
3143+ if (request .hasNextCallSeq () && request .getNextCallSeq () != lastCallSeq + 1 ) {
3144+ throw new OutOfOrderScannerNextException ("Expected nextCallSeq for closed request: "
3145+ + (lastCallSeq + 1 ) + " But the nextCallSeq got from client: "
3146+ + request .getNextCallSeq () + "; request=" + TextFormat .shortDebugString (request ));
3147+ } else {
3148+ throw SCANNER_ALREADY_CLOSED ;
3149+ }
31393150 } else {
31403151 LOG .warn ("Client tried to access missing scanner " + scannerName );
31413152 throw new UnknownScannerException (
@@ -3738,7 +3749,7 @@ public ScanResponse scan(final RpcController controller, final ScanRequest reque
37383749 }
37393750 if (!builder .getMoreResults () || !builder .getMoreResultsInRegion () || closeScanner ) {
37403751 scannerClosed = true ;
3741- closeScanner (region , scanner , scannerName , rpcCall );
3752+ closeScanner (region , scanner , scannerName , rpcCall , false );
37423753 }
37433754
37443755 // There's no point returning to a timed out client. Throwing ensures scanner is closed
@@ -3754,7 +3765,7 @@ public ScanResponse scan(final RpcController controller, final ScanRequest reque
37543765 // The scanner state might be left in a dirty state, so we will tell the Client to
37553766 // fail this RPC and close the scanner while opening up another one from the start of
37563767 // row that the client has last seen.
3757- closeScanner (region , scanner , scannerName , rpcCall );
3768+ closeScanner (region , scanner , scannerName , rpcCall , true );
37583769
37593770 // If it is a DoNotRetryIOException already, throw as it is. Unfortunately, DNRIOE is
37603771 // used in two different semantics.
@@ -3818,7 +3829,7 @@ private void runShippedCallback(RegionScannerHolder rsh) throws ServiceException
38183829 }
38193830
38203831 private void closeScanner (HRegion region , RegionScanner scanner , String scannerName ,
3821- RpcCallContext context ) throws IOException {
3832+ RpcCallContext context , boolean isError ) throws IOException {
38223833 if (region .getCoprocessorHost () != null ) {
38233834 if (region .getCoprocessorHost ().preScannerClose (scanner )) {
38243835 // bypass the actual close.
@@ -3835,7 +3846,9 @@ private void closeScanner(HRegion region, RegionScanner scanner, String scannerN
38353846 if (region .getCoprocessorHost () != null ) {
38363847 region .getCoprocessorHost ().postScannerClose (scanner );
38373848 }
3838- closedScanners .put (scannerName , scannerName );
3849+ if (!isError ) {
3850+ closedScanners .put (scannerName , rsh .getNextCallSeq ());
3851+ }
38393852 }
38403853 }
38413854
0 commit comments