Handles the NotServingRegionException for the given RPC. This code will take ownership of the RPC in the sense that it will become responsible for re-scheduling the RPC later once the NSRE situation gets resolved by HBase. NSRE handling logic Whenever we get an NSRE for the fir
(HBaseRpc rpc,
final byte[] region_name,
final RecoverableException e)
| 3446 | * @param e The exception that caused (or may cause) this RPC to fail. |
| 3447 | */ |
| 3448 | void handleNSRE(HBaseRpc rpc, |
| 3449 | final byte[] region_name, |
| 3450 | final RecoverableException e) { |
| 3451 | num_nsre_rpcs.increment(); |
| 3452 | if (rpc.isProbe()) { |
| 3453 | rpc.setSuspendedProbe(true); |
| 3454 | } |
| 3455 | final boolean can_retry_rpc = !cannotRetryRequest(rpc); |
| 3456 | boolean known_nsre = true; // We already aware of an NSRE for this region? |
| 3457 | ArrayList<HBaseRpc> nsred_rpcs = got_nsre.get(region_name); |
| 3458 | HBaseRpc exists_rpc = null; // Our "probe" RPC. |
| 3459 | if (nsred_rpcs == null) { // Looks like this could be a new NSRE... |
| 3460 | final ArrayList<HBaseRpc> newlist = new ArrayList<HBaseRpc>(64); |
| 3461 | // In HBase 0.95 and up, the exists RPC can't use the empty row key, |
| 3462 | // which could happen if we were trying to scan from the beginning of |
| 3463 | // the table. So instead use "\0" as the key. |
| 3464 | exists_rpc = GetRequest.exists(rpc.table, probeKey(rpc.key)); |
| 3465 | newlist.add(exists_rpc); |
| 3466 | if (can_retry_rpc) { |
| 3467 | newlist.add(rpc); |
| 3468 | } |
| 3469 | nsred_rpcs = got_nsre.putIfAbsent(region_name, newlist); |
| 3470 | if (nsred_rpcs == null) { // We've just put `newlist'. |
| 3471 | nsred_rpcs = newlist; // => We're the first thread to get |
| 3472 | known_nsre = false; // the NSRE for this region. |
| 3473 | } |
| 3474 | } |
| 3475 | |
| 3476 | if (known_nsre) { // Some RPCs seem to already be pending due to this NSRE |
| 3477 | boolean reject = true; // Should we reject this RPC (too many pending)? |
| 3478 | int size; // How many RPCs are already pending? |
| 3479 | |
| 3480 | synchronized (nsred_rpcs) { |
| 3481 | size = nsred_rpcs.size(); |
| 3482 | // If nsred_rpcs is empty, there was a race with another thread which |
| 3483 | // is executing RetryNSREd.call and that just cleared this array and |
| 3484 | // removed nsred_rpcs from got_nsre right after we got the reference, |
| 3485 | // so we need to add it back there, unless another thread already |
| 3486 | // did it (in which case we're really unlucky and lost 2 races). |
| 3487 | if (size == 0) { |
| 3488 | final ArrayList<HBaseRpc> added = |
| 3489 | got_nsre.putIfAbsent(region_name, nsred_rpcs); |
| 3490 | if (added == null) { // We've just put `nsred_rpcs'. |
| 3491 | exists_rpc = GetRequest.exists(rpc.table, probeKey(rpc.key)); |
| 3492 | nsred_rpcs.add(exists_rpc); // We hold the lock on nsred_rpcs |
| 3493 | if (can_retry_rpc) { |
| 3494 | nsred_rpcs.add(rpc); // so we can safely add those 2. |
| 3495 | } |
| 3496 | known_nsre = false; // We mistakenly believed it was known. |
| 3497 | } else { // We lost the second race. |
| 3498 | // Here we synchronize on two different references without any |
| 3499 | // apparent ordering guarantee, which can typically lead to |
| 3500 | // deadlocks. In this case though we're fine, as any other thread |
| 3501 | // that still has a reference to `nsred_rpcs' is gonna go through |
| 3502 | // this very same code path and will lock `nsred_rpcs' first |
| 3503 | // before finding that it too lost 2 races, so it'll lock `added' |
| 3504 | // second. So there's actually a very implicit ordering. |
| 3505 | if (can_retry_rpc) { |