1515 * See the License for the specific language governing permissions and
1616 * limitations under the License.
1717 */
18- package org .apache .hadoop .hbase ;
18+ package org .apache .hadoop .hbase .client ;
19+
20+ import static org .apache .hadoop .hbase .client .MetricsConnection .CLIENT_SIDE_METRICS_ENABLED_KEY ;
1921
2022import java .io .IOException ;
2123import java .net .SocketTimeoutException ;
2224import java .util .ArrayList ;
2325import java .util .List ;
2426import org .apache .hadoop .conf .Configuration ;
25- import org .apache .hadoop .hbase .client .ColumnFamilyDescriptorBuilder ;
26- import org .apache .hadoop .hbase .client .Connection ;
27- import org .apache .hadoop .hbase .client .ConnectionFactory ;
28- import org .apache .hadoop .hbase .client .Get ;
29- import org .apache .hadoop .hbase .client .Put ;
30- import org .apache .hadoop .hbase .client .ResultScanner ;
31- import org .apache .hadoop .hbase .client .RetriesExhaustedException ;
32- import org .apache .hadoop .hbase .client .Scan ;
33- import org .apache .hadoop .hbase .client .Table ;
34- import org .apache .hadoop .hbase .client .TableDescriptorBuilder ;
27+ import org .apache .hadoop .hbase .HBaseClassTestRule ;
28+ import org .apache .hadoop .hbase .HBaseTestingUtility ;
29+ import org .apache .hadoop .hbase .HConstants ;
30+ import org .apache .hadoop .hbase .MiniHBaseCluster ;
31+ import org .apache .hadoop .hbase .StartMiniClusterOption ;
32+ import org .apache .hadoop .hbase .TableName ;
3533import org .apache .hadoop .hbase .ipc .CallTimeoutException ;
3634import org .apache .hadoop .hbase .regionserver .HRegionServer ;
3735import org .apache .hadoop .hbase .regionserver .RSRpcServices ;
@@ -78,7 +76,8 @@ public class TestClientOperationTimeout {
7876 private static int DELAY_GET ;
7977 private static int DELAY_SCAN ;
8078 private static int DELAY_MUTATE ;
81- private static int DELAY_BATCH_MUTATE ;
79+ private static int DELAY_BATCH ;
80+ private static int DELAY_META_SCAN ;
8281
8382 private static final TableName TABLE_NAME = TableName .valueOf ("Timeout" );
8483 private static final byte [] FAMILY = Bytes .toBytes ("family" );
@@ -112,7 +111,8 @@ public void setUp() throws Exception {
112111 DELAY_GET = 0 ;
113112 DELAY_SCAN = 0 ;
114113 DELAY_MUTATE = 0 ;
115- DELAY_BATCH_MUTATE = 0 ;
114+ DELAY_BATCH = 0 ;
115+ DELAY_META_SCAN = 0 ;
116116 }
117117
118118 @ AfterClass
@@ -157,12 +157,12 @@ public void testPutTimeout() {
157157 }
158158
159159 /**
160- * Tests that a batch mutate on a table throws {@link SocketTimeoutException} when the operation
161- * takes longer than 'hbase.client.operation.timeout'.
160+ * Tests that a batch mutate and batch get on a table throws {@link SocketTimeoutException} when
161+ * the operation takes longer than 'hbase.client.operation.timeout'.
162162 */
163163 @ Test
164- public void testMultiPutsTimeout () {
165- DELAY_BATCH_MUTATE = 600 ;
164+ public void testMultiTimeout () {
165+ DELAY_BATCH = 600 ;
166166 Put put1 = new Put (ROW );
167167 put1 .addColumn (FAMILY , QUALIFIER , VALUE );
168168 Put put2 = new Put (ROW );
@@ -176,6 +176,72 @@ public void testMultiPutsTimeout() {
176176 } catch (Exception e ) {
177177 Assert .assertTrue (e instanceof SocketTimeoutException );
178178 }
179+
180+ Get get1 = new Get (ROW );
181+ get1 .addColumn (FAMILY , QUALIFIER );
182+ Get get2 = new Get (ROW );
183+ get2 .addColumn (FAMILY , QUALIFIER );
184+
185+ List <Get > gets = new ArrayList <>();
186+ gets .add (get1 );
187+ gets .add (get2 );
188+ try {
189+ TABLE .batch (gets , new Object [2 ]);
190+ Assert .fail ("should not reach here" );
191+ } catch (Exception e ) {
192+ Assert .assertTrue (e instanceof SocketTimeoutException );
193+ }
194+ }
195+
196+ /**
197+ * Tests that a batch get on a table throws
198+ * {@link org.apache.hadoop.hbase.client.OperationTimeoutExceededException} when the region lookup
199+ * takes longer than the 'hbase.client.operation.timeout'
200+ */
201+ @ Test
202+ public void testMultiGetMetaTimeout () throws IOException {
203+
204+ Configuration conf = new Configuration (UTIL .getConfiguration ());
205+
206+ // the operation timeout must be lower than the delay from a meta scan to etch region locations
207+ // of the get requests. Simply increasing the meta scan timeout to greater than the
208+ // HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD will result in SocketTimeoutException on the scans thus
209+ // avoiding the simulation of load on meta. See: HBASE-27487
210+ conf .setLong (HConstants .HBASE_CLIENT_OPERATION_TIMEOUT , 400 );
211+ conf .setBoolean (CLIENT_SIDE_METRICS_ENABLED_KEY , true );
212+ try (Connection specialConnection = ConnectionFactory .createConnection (conf );
213+ Table specialTable = specialConnection .getTable (TABLE_NAME )) {
214+
215+ MetricsConnection metrics =
216+ ((ConnectionImplementation ) specialConnection ).getConnectionMetrics ();
217+ long metaCacheNumClearServerPreFailure = metrics .metaCacheNumClearServer .getCount ();
218+
219+ DELAY_META_SCAN = 400 ;
220+ List <Get > gets = new ArrayList <>();
221+ // we need to ensure the region look-ups eat up more time than the operation timeout without
222+ // exceeding the scan timeout.
223+ for (int i = 0 ; i < 100 ; i ++) {
224+ gets .add (new Get (Bytes .toBytes (i )).addColumn (FAMILY , QUALIFIER ));
225+ }
226+ try {
227+ specialTable .get (gets );
228+ Assert .fail ("should not reach here" );
229+ } catch (Exception e ) {
230+ RetriesExhaustedWithDetailsException expected = (RetriesExhaustedWithDetailsException ) e ;
231+ Assert .assertEquals (100 , expected .getNumExceptions ());
232+
233+ // verify we do not clear the cache in this situation otherwise we will create pathological
234+ // feedback loop with multigets See: HBASE-27487
235+ long metaCacheNumClearServerPostFailure = metrics .metaCacheNumClearServer .getCount ();
236+ Assert .assertEquals (metaCacheNumClearServerPreFailure , metaCacheNumClearServerPostFailure );
237+
238+ for (Throwable cause : expected .getCauses ()) {
239+ Assert .assertTrue (cause instanceof OperationTimeoutExceededException );
240+ }
241+
242+ }
243+ }
244+
179245 }
180246
181247 /**
@@ -240,7 +306,12 @@ public ClientProtos.MutateResponse mutate(RpcController rpcc,
240306 public ClientProtos .ScanResponse scan (RpcController controller ,
241307 ClientProtos .ScanRequest request ) throws ServiceException {
242308 try {
243- Thread .sleep (DELAY_SCAN );
309+ String regionName = Bytes .toString (request .getRegion ().getValue ().toByteArray ());
310+ if (regionName .contains (TableName .META_TABLE_NAME .getNameAsString ())) {
311+ Thread .sleep (DELAY_META_SCAN );
312+ } else {
313+ Thread .sleep (DELAY_SCAN );
314+ }
244315 } catch (InterruptedException e ) {
245316 LOG .error ("Sleep interrupted during scan operation" , e );
246317 }
@@ -251,7 +322,7 @@ public ClientProtos.ScanResponse scan(RpcController controller,
251322 public ClientProtos .MultiResponse multi (RpcController rpcc , ClientProtos .MultiRequest request )
252323 throws ServiceException {
253324 try {
254- Thread .sleep (DELAY_BATCH_MUTATE );
325+ Thread .sleep (DELAY_BATCH );
255326 } catch (InterruptedException e ) {
256327 LOG .error ("Sleep interrupted during multi operation" , e );
257328 }
0 commit comments