001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.shaded.protobuf; 019 020import edu.umd.cs.findbugs.annotations.Nullable; 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import org.apache.hadoop.hbase.Cell; 027import org.apache.hadoop.hbase.CellScanner; 028import org.apache.hadoop.hbase.DoNotRetryIOException; 029import org.apache.hadoop.hbase.ServerName; 030import org.apache.hadoop.hbase.client.CheckAndMutateResult; 031import org.apache.hadoop.hbase.client.QueryMetrics; 032import org.apache.hadoop.hbase.client.RegionInfo; 033import org.apache.hadoop.hbase.client.Result; 034import org.apache.hadoop.hbase.client.SingleResponse; 035import org.apache.hadoop.hbase.ipc.ServerRpcController; 036import org.apache.hadoop.util.StringUtils; 037import org.apache.yetus.audience.InterfaceAudience; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 042import org.apache.hbase.thirdparty.com.google.protobuf.RpcController; 043 044import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos.HasPermissionResponse; 045import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; 046import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionResponse; 047import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetOnlineRegionResponse; 048import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoResponse; 049import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ServerInfo; 050import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos; 051import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiRequest; 052import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiResponse; 053import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionAction; 054import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionActionResult; 055import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ResultOrException; 056import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse; 057import org.apache.hadoop.hbase.shaded.protobuf.generated.ClusterStatusProtos.RegionStoreSequenceIds; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameBytesPair; 060import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameInt64Pair; 061import org.apache.hadoop.hbase.shaded.protobuf.generated.MapReduceProtos.ScanMetrics; 062import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.EnableCatalogJanitorResponse; 063import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunCatalogScanResponse; 064import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunCleanerChoreResponse; 065import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.GetLastFlushedSequenceIdResponse; 066 067/** 068 * Helper utility to build protocol buffer responses, or retrieve data from protocol buffer 069 * responses. 070 */ 071@InterfaceAudience.Private 072public final class ResponseConverter { 073 private static final Logger LOG = LoggerFactory.getLogger(ResponseConverter.class); 074 075 private ResponseConverter() { 076 } 077 078 // Start utilities for Client 079 public static SingleResponse getResult(final ClientProtos.MutateRequest request, 080 final ClientProtos.MutateResponse response, final CellScanner cells) throws IOException { 081 SingleResponse singleResponse = new SingleResponse(); 082 SingleResponse.Entry entry = new SingleResponse.Entry(); 083 entry.setResult(ProtobufUtil.toResult(response.getResult(), cells)); 084 entry.setProcessed(response.getProcessed()); 085 singleResponse.setEntry(entry); 086 return singleResponse; 087 } 088 089 /** 090 * Get the results from a protocol buffer MultiResponse 091 * @param request the original protocol buffer MultiRequest 092 * @param response the protocol buffer MultiResponse to convert 093 * @param cells Cells to go with the passed in <code>proto</code>. Can be null. 094 * @return the results that were in the MultiResponse (a Result or an Exception). 095 */ 096 public static org.apache.hadoop.hbase.client.MultiResponse getResults(final MultiRequest request, 097 final MultiResponse response, final CellScanner cells) throws IOException { 098 return getResults(request, null, response, cells); 099 } 100 101 /** 102 * Get the results from a protocol buffer MultiResponse 103 * @param request the original protocol buffer MultiRequest 104 * @param indexMap Used to support RowMutations/CheckAndMutate in batch 105 * @param response the protocol buffer MultiResponse to convert 106 * @param cells Cells to go with the passed in <code>proto</code>. Can be null. 107 * @return the results that were in the MultiResponse (a Result or an Exception). 108 */ 109 public static org.apache.hadoop.hbase.client.MultiResponse getResults(final MultiRequest request, 110 final Map<Integer, Integer> indexMap, final MultiResponse response, final CellScanner cells) 111 throws IOException { 112 int requestRegionActionCount = request.getRegionActionCount(); 113 int responseRegionActionResultCount = response.getRegionActionResultCount(); 114 if (requestRegionActionCount != responseRegionActionResultCount) { 115 throw new IllegalStateException("Request mutation count=" + requestRegionActionCount 116 + " does not match response mutation result count=" + responseRegionActionResultCount); 117 } 118 119 org.apache.hadoop.hbase.client.MultiResponse results = 120 new org.apache.hadoop.hbase.client.MultiResponse(); 121 122 for (int i = 0; i < responseRegionActionResultCount; i++) { 123 RegionAction actions = request.getRegionAction(i); 124 RegionActionResult actionResult = response.getRegionActionResult(i); 125 HBaseProtos.RegionSpecifier rs = actions.getRegion(); 126 if ( 127 rs.hasType() 128 && (rs.getType() != HBaseProtos.RegionSpecifier.RegionSpecifierType.REGION_NAME) 129 ) { 130 throw new IllegalArgumentException( 131 "We support only encoded types for protobuf multi response."); 132 } 133 byte[] regionName = rs.getValue().toByteArray(); 134 135 if (actionResult.hasException()) { 136 Throwable regionException = ProtobufUtil.toException(actionResult.getException()); 137 results.addException(regionName, regionException); 138 continue; 139 } 140 141 if (actions.getActionCount() != actionResult.getResultOrExceptionCount()) { 142 throw new IllegalStateException("actions.getActionCount=" + actions.getActionCount() 143 + ", actionResult.getResultOrExceptionCount=" + actionResult.getResultOrExceptionCount() 144 + " for region " + actions.getRegion()); 145 } 146 147 // For RowMutations/CheckAndMutate action, if there is an exception, the exception is set 148 // at the RegionActionResult level and the ResultOrException is null at the original index 149 Integer index = (indexMap == null ? null : indexMap.get(i)); 150 if (index != null) { 151 // This RegionAction is from a RowMutations/CheckAndMutate in a batch. 152 // If there is an exception from the server, the exception is set at 153 // the RegionActionResult level, which has been handled above. 154 if (actions.hasCondition()) { 155 results.add(regionName, index, getCheckAndMutateResult(actionResult, cells)); 156 } else { 157 results.add(regionName, index, getMutateRowResult(actionResult, cells)); 158 } 159 continue; 160 } 161 162 if (actions.hasCondition()) { 163 results.add(regionName, 0, getCheckAndMutateResult(actionResult, cells)); 164 } else { 165 if (actionResult.hasProcessed()) { 166 results.add(regionName, 0, getMutateRowResult(actionResult, cells)); 167 continue; 168 } 169 for (ResultOrException roe : actionResult.getResultOrExceptionList()) { 170 Object responseValue; 171 if (roe.hasException()) { 172 responseValue = ProtobufUtil.toException(roe.getException()); 173 } else if (roe.hasResult()) { 174 responseValue = ProtobufUtil.toResult(roe.getResult(), cells); 175 } else if (roe.hasServiceResult()) { 176 responseValue = roe.getServiceResult(); 177 } else { 178 responseValue = ProtobufUtil.EMPTY_RESULT_EXISTS_TRUE; 179 } 180 results.add(regionName, roe.getIndex(), responseValue); 181 } 182 } 183 } 184 185 if (response.hasRegionStatistics()) { 186 ClientProtos.MultiRegionLoadStats stats = response.getRegionStatistics(); 187 for (int i = 0; i < stats.getRegionCount(); i++) { 188 results.addStatistic(stats.getRegion(i).getValue().toByteArray(), stats.getStat(i)); 189 } 190 } 191 192 return results; 193 } 194 195 private static CheckAndMutateResult getCheckAndMutateResult(RegionActionResult actionResult, 196 CellScanner cells) throws IOException { 197 Result result = null; 198 QueryMetrics metrics = null; 199 if (actionResult.getResultOrExceptionCount() > 0) { 200 // Get the result of the Increment/Append operations from the first element of the 201 // ResultOrException list 202 ResultOrException roe = actionResult.getResultOrException(0); 203 if (roe.hasResult()) { 204 Result r = ProtobufUtil.toResult(roe.getResult(), cells); 205 if (!r.isEmpty()) { 206 result = r; 207 } 208 } 209 210 if (roe.hasMetrics()) { 211 metrics = ProtobufUtil.toQueryMetrics(roe.getMetrics()); 212 } 213 } 214 return new CheckAndMutateResult(actionResult.getProcessed(), result).setMetrics(metrics); 215 } 216 217 private static Result getMutateRowResult(RegionActionResult actionResult, CellScanner cells) 218 throws IOException { 219 if (actionResult.getProcessed()) { 220 Result result = null; 221 if (actionResult.getResultOrExceptionCount() > 0) { 222 // Get the result of the Increment/Append operations from the first element of the 223 // ResultOrException list 224 ResultOrException roe = actionResult.getResultOrException(0); 225 Result r = ProtobufUtil.toResult(roe.getResult(), cells); 226 if (!r.isEmpty()) { 227 r.setExists(true); 228 result = r; 229 } 230 } 231 if (result != null) { 232 return result; 233 } else { 234 return ProtobufUtil.EMPTY_RESULT_EXISTS_TRUE; 235 } 236 } else { 237 return ProtobufUtil.EMPTY_RESULT_EXISTS_FALSE; 238 } 239 } 240 241 /** 242 * Create a CheckAndMutateResult object from a protocol buffer MutateResponse 243 * @return a CheckAndMutateResult object 244 */ 245 public static CheckAndMutateResult getCheckAndMutateResult( 246 ClientProtos.MutateResponse mutateResponse, CellScanner cells) throws IOException { 247 boolean success = mutateResponse.getProcessed(); 248 Result result = null; 249 QueryMetrics metrics = null; 250 if (mutateResponse.hasResult()) { 251 result = ProtobufUtil.toResult(mutateResponse.getResult(), cells); 252 } 253 254 if (mutateResponse.hasMetrics()) { 255 metrics = ProtobufUtil.toQueryMetrics(mutateResponse.getMetrics()); 256 } 257 258 return new CheckAndMutateResult(success, result).setMetrics(metrics); 259 } 260 261 /** 262 * Wrap a throwable to an action result. 263 * @return an action result builder 264 */ 265 public static ResultOrException.Builder buildActionResult(final Throwable t) { 266 ResultOrException.Builder builder = ResultOrException.newBuilder(); 267 if (t != null) builder.setException(buildException(t)); 268 return builder; 269 } 270 271 /** 272 * Wrap a throwable to an action result. 273 * @return an action result builder 274 */ 275 public static ResultOrException.Builder buildActionResult(final ClientProtos.Result r) { 276 ResultOrException.Builder builder = ResultOrException.newBuilder(); 277 if (r != null) builder.setResult(r); 278 return builder; 279 } 280 281 /** Returns NameValuePair of the exception name to stringified version os exception. */ 282 public static NameBytesPair buildException(final Throwable t) { 283 NameBytesPair.Builder parameterBuilder = NameBytesPair.newBuilder(); 284 parameterBuilder.setName(t.getClass().getName()); 285 parameterBuilder.setValue(ByteString.copyFromUtf8(StringUtils.stringifyException(t))); 286 return parameterBuilder.build(); 287 } 288 289 /** 290 * Builds a protocol buffer HasPermissionResponse 291 */ 292 public static HasPermissionResponse buildHasPermissionResponse(boolean hasPermission) { 293 HasPermissionResponse.Builder builder = HasPermissionResponse.newBuilder(); 294 builder.setHasPermission(hasPermission); 295 return builder.build(); 296 } 297 298 // End utilities for Client 299 // Start utilities for Admin 300 301 /** 302 * Get the list of region info from a GetOnlineRegionResponse 303 * @param proto the GetOnlineRegionResponse 304 * @return the list of region info 305 */ 306 public static List<RegionInfo> getRegionInfos(final GetOnlineRegionResponse proto) { 307 if (proto == null || proto.getRegionInfoCount() == 0) return null; 308 return ProtobufUtil.getRegionInfos(proto); 309 } 310 311 /** 312 * Check if the region is closed from a CloseRegionResponse 313 * @param proto the CloseRegionResponse 314 * @return the region close state 315 */ 316 public static boolean isClosed(final CloseRegionResponse proto) { 317 if (proto == null || !proto.hasClosed()) return false; 318 return proto.getClosed(); 319 } 320 321 /** 322 * A utility to build a GetServerInfoResponse. 323 * @return the response 324 */ 325 public static GetServerInfoResponse buildGetServerInfoResponse(final ServerName serverName, 326 final int webuiPort) { 327 GetServerInfoResponse.Builder builder = GetServerInfoResponse.newBuilder(); 328 ServerInfo.Builder serverInfoBuilder = ServerInfo.newBuilder(); 329 serverInfoBuilder.setServerName(ProtobufUtil.toServerName(serverName)); 330 if (webuiPort >= 0) { 331 serverInfoBuilder.setWebuiPort(webuiPort); 332 } 333 builder.setServerInfo(serverInfoBuilder.build()); 334 return builder.build(); 335 } 336 337 /** 338 * A utility to build a GetOnlineRegionResponse. 339 * @return the response 340 */ 341 public static GetOnlineRegionResponse 342 buildGetOnlineRegionResponse(final List<RegionInfo> regions) { 343 GetOnlineRegionResponse.Builder builder = GetOnlineRegionResponse.newBuilder(); 344 for (RegionInfo region : regions) { 345 builder.addRegionInfo(ProtobufUtil.toRegionInfo(region)); 346 } 347 return builder.build(); 348 } 349 350 /** 351 * Creates a response for the catalog scan request 352 * @return A RunCatalogScanResponse 353 */ 354 public static RunCatalogScanResponse buildRunCatalogScanResponse(int numCleaned) { 355 return RunCatalogScanResponse.newBuilder().setScanResult(numCleaned).build(); 356 } 357 358 /** 359 * Creates a response for the catalog scan request 360 * @return A EnableCatalogJanitorResponse 361 */ 362 public static EnableCatalogJanitorResponse buildEnableCatalogJanitorResponse(boolean prevValue) { 363 return EnableCatalogJanitorResponse.newBuilder().setPrevValue(prevValue).build(); 364 } 365 366 /** 367 * Creates a response for the cleaner chore request 368 * @return A RunCleanerChoreResponse 369 */ 370 public static RunCleanerChoreResponse buildRunCleanerChoreResponse(boolean ran) { 371 return RunCleanerChoreResponse.newBuilder().setCleanerChoreRan(ran).build(); 372 } 373 374 // End utilities for Admin 375 376 /** 377 * Creates a response for the last flushed sequence Id request 378 * @return A GetLastFlushedSequenceIdResponse 379 */ 380 public static GetLastFlushedSequenceIdResponse 381 buildGetLastFlushedSequenceIdResponse(RegionStoreSequenceIds ids) { 382 return GetLastFlushedSequenceIdResponse.newBuilder() 383 .setLastFlushedSequenceId(ids.getLastFlushedSequenceId()) 384 .addAllStoreLastFlushedSequenceId(ids.getStoreSequenceIdList()).build(); 385 } 386 387 /** 388 * Stores an exception encountered during RPC invocation so it can be passed back through to the 389 * client. 390 * @param controller the controller instance provided by the client when calling the service 391 * @param ioe the exception encountered 392 */ 393 public static void setControllerException(RpcController controller, IOException ioe) { 394 if (controller != null) { 395 if (controller instanceof ServerRpcController) { 396 ((ServerRpcController) controller).setFailedOn(ioe); 397 } else { 398 controller.setFailed(StringUtils.stringifyException(ioe)); 399 } 400 } 401 } 402 403 /** 404 * Retreivies exception stored during RPC invocation. 405 * @param controller the controller instance provided by the client when calling the service 406 * @return exception if any, or null; Will return DoNotRetryIOException for string represented 407 * failure causes in controller. 408 */ 409 @Nullable 410 public static IOException getControllerException(RpcController controller) throws IOException { 411 if (controller != null && controller.failed()) { 412 if (controller instanceof ServerRpcController) { 413 return ((ServerRpcController) controller).getFailedOn(); 414 } else { 415 return new DoNotRetryIOException(controller.errorText()); 416 } 417 } 418 return null; 419 } 420 421 /** 422 * Create Results from the cells using the cells meta data. 423 */ 424 public static Result[] getResults(CellScanner cellScanner, ScanResponse response) 425 throws IOException { 426 if (response == null) return null; 427 // If cellscanner, then the number of Results to return is the count of elements in the 428 // cellsPerResult list. Otherwise, it is how many results are embedded inside the response. 429 int noOfResults = 430 cellScanner != null ? response.getCellsPerResultCount() : response.getResultsCount(); 431 Result[] results = new Result[noOfResults]; 432 List<ClientProtos.QueryMetrics> queryMetrics = response.getQueryMetricsList(); 433 for (int i = 0; i < noOfResults; i++) { 434 if (cellScanner != null) { 435 // Cells are out in cellblocks. Group them up again as Results. How many to read at a 436 // time will be found in getCellsLength -- length here is how many Cells in the i'th Result 437 int noOfCells = response.getCellsPerResult(i); 438 boolean isPartial = 439 response.getPartialFlagPerResultCount() > i ? response.getPartialFlagPerResult(i) : false; 440 List<Cell> cells = new ArrayList<>(noOfCells); 441 for (int j = 0; j < noOfCells; j++) { 442 try { 443 if (cellScanner.advance() == false) { 444 // We are not able to retrieve the exact number of cells which ResultCellMeta says us. 445 // We have to scan for the same results again. Throwing DNRIOE as a client retry on 446 // the 447 // same scanner will result in OutOfOrderScannerNextException 448 String msg = "Results sent from server=" + noOfResults + ". But only got " + i 449 + " results completely at client. Resetting the scanner to scan again."; 450 LOG.error(msg); 451 throw new DoNotRetryIOException(msg); 452 } 453 } catch (IOException ioe) { 454 // We are getting IOE while retrieving the cells for Results. 455 // We have to scan for the same results again. Throwing DNRIOE as a client retry on the 456 // same scanner will result in OutOfOrderScannerNextException 457 LOG.error( 458 "Exception while reading cells from result." + "Resetting the scanner to scan again.", 459 ioe); 460 throw new DoNotRetryIOException("Resetting the scanner.", ioe); 461 } 462 cells.add(cellScanner.current()); 463 } 464 results[i] = Result.create(cells, null, response.getStale(), isPartial); 465 } else { 466 // Result is pure pb. 467 results[i] = ProtobufUtil.toResult(response.getResults(i)); 468 } 469 470 // Populate result metrics if they exist 471 if (queryMetrics.size() > i) { 472 QueryMetrics metrics = ProtobufUtil.toQueryMetrics(queryMetrics.get(i)); 473 results[i].setMetrics(metrics); 474 } 475 } 476 return results; 477 } 478 479 public static Map<String, Long> getScanMetrics(ScanResponse response) { 480 Map<String, Long> metricMap = new HashMap<>(); 481 if (response == null || !response.hasScanMetrics()) { 482 return metricMap; 483 } 484 485 ScanMetrics metrics = response.getScanMetrics(); 486 int numberOfMetrics = metrics.getMetricsCount(); 487 for (int i = 0; i < numberOfMetrics; i++) { 488 NameInt64Pair metricPair = metrics.getMetrics(i); 489 if (metricPair != null) { 490 String name = metricPair.getName(); 491 Long value = metricPair.getValue(); 492 if (name != null && value != null) { 493 metricMap.put(name, value); 494 } 495 } 496 } 497 498 return metricMap; 499 } 500 501 /** 502 * Creates a protocol buffer ClearRegionBlockCacheResponse 503 * @return a ClearRegionBlockCacheResponse 504 */ 505 public static AdminProtos.ClearRegionBlockCacheResponse 506 buildClearRegionBlockCacheResponse(final HBaseProtos.CacheEvictionStats cacheEvictionStats) { 507 return AdminProtos.ClearRegionBlockCacheResponse.newBuilder().setStats(cacheEvictionStats) 508 .build(); 509 } 510}