23
23
24
24
import java .io .IOException ;
25
25
import java .io .InterruptedIOException ;
26
- import java .util .ArrayList ;
27
- import java .util .List ;
28
26
29
27
import org .apache .hadoop .hbase .DoNotRetryIOException ;
30
28
import org .apache .hadoop .hbase .HConstants ;
31
29
import org .apache .hadoop .hbase .HRegionLocation ;
32
30
import org .apache .hadoop .hbase .RegionLocations ;
33
31
import org .apache .hadoop .hbase .TableName ;
34
32
import org .apache .hadoop .hbase .classification .InterfaceAudience ;
33
+ import org .apache .hadoop .hbase .TableNotEnabledException ;
34
+ import org .apache .hadoop .hbase .util .Pair ;
35
35
import org .apache .hadoop .hbase .client .metrics .ScanMetrics ;
36
36
import org .apache .hadoop .hbase .ipc .RpcControllerFactory ;
37
37
import org .apache .hadoop .hbase .util .Bytes ;
43
43
@ InterfaceAudience .Private
44
44
public class ReversedScannerCallable extends ScannerCallable {
45
45
46
+ private byte [] locationSearchKey ;
47
+
46
48
/**
47
49
* @param connection
48
50
* @param tableName
@@ -70,6 +72,18 @@ public ReversedScannerCallable(ClusterConnection connection, TableName tableName
70
72
super (connection , tableName , scan , scanMetrics , rpcFactory , replicaId );
71
73
}
72
74
75
+ @ Override
76
+ public void throwable (Throwable t , boolean retrying ) {
77
+ // for reverse scans, we need to update cache using the search key found for the reverse scan
78
+ // range in prepare. Otherwise, we will see weird behavior at the table boundaries,
79
+ // when trying to clear cache for an empty row.
80
+ if (location != null && locationSearchKey != null ) {
81
+ getConnection ().updateCachedLocations (getTableName (),
82
+ location .getRegionInfo ().getRegionName (),
83
+ locationSearchKey , t , location .getServerName ());
84
+ }
85
+ }
86
+
73
87
/**
74
88
* @param reload force reload of server location
75
89
* @throws IOException
@@ -79,34 +93,37 @@ public void prepare(boolean reload) throws IOException {
79
93
if (Thread .interrupted ()) {
80
94
throw new InterruptedIOException ();
81
95
}
96
+
97
+ if (reload && getTableName () != null && !getTableName ().equals (TableName .META_TABLE_NAME )
98
+ && getConnection ().isTableDisabled (getTableName ())) {
99
+ throw new TableNotEnabledException (getTableName ().getNameAsString () + " is disabled." );
100
+ }
101
+
82
102
if (!instantiated || reload ) {
83
103
// we should use range locate if
84
104
// 1. we do not want the start row
85
105
// 2. the start row is empty which means we need to locate to the last region.
86
106
if (scan .includeStartRow () && !isEmptyStartRow (getRow ())) {
87
107
// Just locate the region with the row
88
- RegionLocations rl = RpcRetryingCallerWithReadReplicas .getRegionLocations (!reload , id ,
89
- getConnection (), getTableName (), getRow ());
90
- this .location = id < rl .size () ? rl .getRegionLocation (id ) : null ;
91
- if (location == null || location .getServerName () == null ) {
92
- throw new IOException ("Failed to find location, tableName="
93
- + tableName + ", row=" + Bytes .toStringBinary (row ) + ", reload="
94
- + reload );
95
- }
108
+ RegionLocations rl = getRegionLocationsForPrepare (getRow ());
109
+ this .location = getLocationForReplica (rl );
110
+ this .locationSearchKey = getRow ();
96
111
} else {
97
- // Need to locate the regions with the range, and the target location is
98
- // the last one which is the previous region of last region scanner
112
+ // The locateStart row is an approximation. So we need to search between
113
+ // that and the actual row in order to really find the last region
99
114
byte [] locateStartRow = createCloseRowBefore (getRow ());
100
- List <HRegionLocation > locatedRegions = locateRegionsInRange (
101
- locateStartRow , row , reload );
102
- if (locatedRegions .isEmpty ()) {
103
- throw new DoNotRetryIOException (
104
- "Does hbase:meta exist hole? Couldn't get regions for the range from "
105
- + Bytes .toStringBinary (locateStartRow ) + " to "
106
- + Bytes .toStringBinary (row ));
107
- }
108
- this .location = locatedRegions .get (locatedRegions .size () - 1 );
115
+ Pair <HRegionLocation , byte []> lastRegionAndKey = locateLastRegionInRange (
116
+ locateStartRow , getRow ());
117
+ this .location = lastRegionAndKey .getFirst ();
118
+ this .locationSearchKey = lastRegionAndKey .getSecond ();
109
119
}
120
+
121
+ if (location == null || location .getServerName () == null ) {
122
+ throw new IOException ("Failed to find location, tableName="
123
+ + getTableName () + ", row=" + Bytes .toStringBinary (getRow ()) + ", reload="
124
+ + reload );
125
+ }
126
+
110
127
setStub (getConnection ().getClient (getLocation ().getServerName ()));
111
128
checkIfRegionServerIsRemote ();
112
129
instantiated = true ;
@@ -124,33 +141,32 @@ public void prepare(boolean reload) throws IOException {
124
141
}
125
142
126
143
/**
127
- * Get the corresponding regions for an arbitrary range of keys.
144
+ * Get the last region before the endkey, which will be used to execute the reverse scan
128
145
* @param startKey Starting row in range, inclusive
129
146
* @param endKey Ending row in range, exclusive
130
- * @param reload force reload of server location
131
- * @return A list of HRegionLocation corresponding to the regions that contain
132
- * the specified range
133
- * @throws IOException
147
+ * @return The last location, and the rowKey used to find it. May be null,
148
+ * if a region could not be found.
134
149
*/
135
- @ edu .umd .cs .findbugs .annotations .SuppressWarnings (value ="NP_NULL_ON_SOME_PATH" ,
136
- justification ="I thought I'd fixed it but FB still complains; see below" )
137
- private List <HRegionLocation > locateRegionsInRange (byte [] startKey ,
138
- byte [] endKey , boolean reload ) throws IOException {
150
+ private Pair <HRegionLocation , byte []> locateLastRegionInRange (byte [] startKey , byte [] endKey )
151
+ throws IOException {
139
152
final boolean endKeyIsEndOfTable = Bytes .equals (endKey ,
140
153
HConstants .EMPTY_END_ROW );
141
154
if ((Bytes .compareTo (startKey , endKey ) > 0 ) && !endKeyIsEndOfTable ) {
142
155
throw new IllegalArgumentException ("Invalid range: "
143
156
+ Bytes .toStringBinary (startKey ) + " > "
144
157
+ Bytes .toStringBinary (endKey ));
145
158
}
146
- List <HRegionLocation > regionList = new ArrayList <HRegionLocation >();
159
+
160
+ HRegionLocation lastRegion = null ;
161
+ byte [] lastFoundKey = null ;
147
162
byte [] currentKey = startKey ;
163
+
148
164
do {
149
- RegionLocations rl = RpcRetryingCallerWithReadReplicas . getRegionLocations (! reload , id ,
150
- getConnection (), getTableName (), currentKey );
151
- HRegionLocation regionLocation = id < rl . size () ? rl . getRegionLocation ( id ) : null ;
152
- if ( regionLocation != null && regionLocation . getRegionInfo (). containsRow ( currentKey )) {
153
- regionList . add ( regionLocation ) ;
165
+ RegionLocations rl = getRegionLocationsForPrepare ( currentKey );
166
+ HRegionLocation regionLocation = getLocationForReplica ( rl );
167
+ if ( regionLocation . getRegionInfo (). containsRow ( currentKey )) {
168
+ lastFoundKey = currentKey ;
169
+ lastRegion = regionLocation ;
154
170
} else {
155
171
// FindBugs: NP_NULL_ON_SOME_PATH Complaining about regionLocation
156
172
throw new DoNotRetryIOException ("Does hbase:meta exist hole? Locating row "
@@ -160,7 +176,8 @@ private List<HRegionLocation> locateRegionsInRange(byte[] startKey,
160
176
currentKey = regionLocation .getRegionInfo ().getEndKey ();
161
177
} while (!Bytes .equals (currentKey , HConstants .EMPTY_END_ROW )
162
178
&& (endKeyIsEndOfTable || Bytes .compareTo (currentKey , endKey ) < 0 ));
163
- return regionList ;
179
+
180
+ return new Pair <>(lastRegion , lastFoundKey );
164
181
}
165
182
166
183
@ Override
0 commit comments