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.client; 019 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import org.apache.hadoop.hbase.CellScannable; 026import org.apache.hadoop.hbase.CellUtil; 027import org.apache.hadoop.hbase.DoNotRetryIOException; 028import org.apache.hadoop.hbase.HRegionInfo; 029import org.apache.hadoop.hbase.HRegionLocation; 030import org.apache.hadoop.hbase.ServerName; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.yetus.audience.InterfaceAudience; 033 034import org.apache.hbase.thirdparty.com.google.protobuf.RpcController; 035 036import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; 037import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter; 038import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos; 039import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiRequest; 040import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MutationProto; 041import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionAction; 042 043/** 044 * Callable that handles the <code>multi</code> method call going against a single regionserver; 045 * i.e. A RegionServerCallable for the multi call (It is NOT a RegionServerCallable that goes 046 * against multiple regions). 047 */ 048@InterfaceAudience.Private 049class MultiServerCallable extends CancellableRegionServerCallable<MultiResponse> { 050 private MultiAction multiAction; 051 private boolean cellBlock; 052 053 MultiServerCallable(final ClusterConnection connection, final TableName tableName, 054 final ServerName location, final MultiAction multi, RpcController rpcController, int rpcTimeout, 055 RetryingTimeTracker tracker, int priority, Map<String, byte[]> requestAttributes) { 056 super(connection, tableName, null, rpcController, rpcTimeout, tracker, priority, 057 requestAttributes); 058 this.multiAction = multi; 059 // RegionServerCallable has HRegionLocation field, but this is a multi-region request. 060 // Using region info from parent HRegionLocation would be a mistake for this class; so 061 // we will store the server here, and throw if someone tries to obtain location/regioninfo. 062 this.location = new HRegionLocation(null, location); 063 this.cellBlock = isCellBlock(); 064 } 065 066 public void reset(ServerName location, MultiAction multiAction) { 067 this.location = new HRegionLocation(null, location); 068 this.multiAction = multiAction; 069 this.cellBlock = isCellBlock(); 070 } 071 072 @Override 073 protected HRegionLocation getLocation() { 074 throw new RuntimeException("Cannot get region location for multi-region request"); 075 } 076 077 @Override 078 public HRegionInfo getHRegionInfo() { 079 throw new RuntimeException("Cannot get region info for multi-region request"); 080 } 081 082 MultiAction getMulti() { 083 return this.multiAction; 084 } 085 086 @Override 087 protected MultiResponse rpcCall() throws Exception { 088 int countOfActions = this.multiAction.size(); 089 if (countOfActions <= 0) throw new DoNotRetryIOException("No Actions"); 090 MultiRequest.Builder multiRequestBuilder = MultiRequest.newBuilder(); 091 RegionAction.Builder regionActionBuilder = RegionAction.newBuilder(); 092 ClientProtos.Action.Builder actionBuilder = ClientProtos.Action.newBuilder(); 093 MutationProto.Builder mutationBuilder = MutationProto.newBuilder(); 094 095 // Pre-size. Presume at least a KV per Action. There are likely more. 096 List<CellScannable> cells = 097 (this.cellBlock ? new ArrayList<CellScannable>(countOfActions) : null); 098 099 long nonceGroup = multiAction.getNonceGroup(); 100 101 // Map from a created RegionAction to the original index for a RowMutations/CheckAndMutate 102 // within the original list of actions. This will be used to process the results when there 103 // is RowMutations/CheckAndMutate in the action list. 104 Map<Integer, Integer> indexMap = new HashMap<>(); 105 // The multi object is a list of Actions by region. Iterate by region. 106 for (Map.Entry<byte[], List<Action>> e : this.multiAction.actions.entrySet()) { 107 final byte[] regionName = e.getKey(); 108 final List<Action> actions = e.getValue(); 109 if (this.cellBlock) { 110 // Send data in cellblocks. 111 // multiRequestBuilder will be populated with region actions. 112 // indexMap will be non-empty after the call if there is RowMutations/CheckAndMutate in 113 // the action list. 114 RequestConverter.buildNoDataRegionActions(regionName, actions, cells, multiRequestBuilder, 115 regionActionBuilder, actionBuilder, mutationBuilder, nonceGroup, indexMap); 116 } else { 117 // multiRequestBuilder will be populated with region actions. 118 // indexMap will be non-empty after the call if there is RowMutations/CheckAndMutate in 119 // the action list. 120 RequestConverter.buildRegionActions(regionName, actions, multiRequestBuilder, 121 regionActionBuilder, actionBuilder, mutationBuilder, nonceGroup, indexMap); 122 } 123 } 124 125 if (cells != null) { 126 setRpcControllerCellScanner(CellUtil.createCellScanner(cells)); 127 } 128 ClientProtos.MultiRequest requestProto = multiRequestBuilder.build(); 129 ClientProtos.MultiResponse responseProto = getStub().multi(getRpcController(), requestProto); 130 if (responseProto == null) return null; // Occurs on cancel 131 return ResponseConverter.getResults(requestProto, indexMap, responseProto, 132 getRpcControllerCellScanner()); 133 } 134 135 /** 136 * @return True if we should send data in cellblocks. This is an expensive call. Cache the result 137 * if you can rather than call each time. 138 */ 139 private boolean isCellBlock() { 140 // This is not exact -- the configuration could have changed on us after connection was set up 141 // but it will do for now. 142 ClusterConnection conn = getConnection(); 143 return conn.hasCellBlockSupport(); 144 } 145 146 @Override 147 public void prepare(boolean reload) throws IOException { 148 // Use the location we were given in the constructor rather than go look it up. 149 setStub(getConnection().getClient(this.location.getServerName())); 150 } 151 152 ServerName getServerName() { 153 return location.getServerName(); 154 } 155}