View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.regionserver;
19  
20  import java.io.IOException;
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.hadoop.hbase.classification.InterfaceAudience;
27  import org.apache.hadoop.hbase.Cell;
28  import org.apache.hadoop.hbase.DoNotRetryIOException;
29  import org.apache.hadoop.hbase.KeyValue;
30  import org.apache.hadoop.hbase.KeyValueUtil;
31  import org.apache.hadoop.hbase.client.Delete;
32  import org.apache.hadoop.hbase.client.Durability;
33  import org.apache.hadoop.hbase.client.Mutation;
34  import org.apache.hadoop.hbase.client.Put;
35  import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationProcessorRequest;
36  import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationProcessorResponse;
37  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
38  import org.apache.hadoop.hbase.util.Bytes;
39  
40  /**
41   * A <code>MultiRowProcessor</code> that performs multiple puts and deletes.
42   */
43  @InterfaceAudience.Private
44  class MultiRowMutationProcessor extends BaseRowProcessor<MultiRowMutationProcessorRequest,
45  MultiRowMutationProcessorResponse> {
46    Collection<byte[]> rowsToLock;
47    Collection<Mutation> mutations;
48    MiniBatchOperationInProgress<Mutation> miniBatch;
49  
50    MultiRowMutationProcessor(Collection<Mutation> mutations,
51                              Collection<byte[]> rowsToLock) {
52      this.rowsToLock = rowsToLock;
53      this.mutations = mutations;
54    }
55  
56    @Override
57    public Collection<byte[]> getRowsToLock() {
58      return rowsToLock;
59    }
60  
61    @Override
62    public boolean readOnly() {
63      return false;
64    }
65    
66    @Override
67    public MultiRowMutationProcessorResponse getResult() {
68      return MultiRowMutationProcessorResponse.getDefaultInstance();
69    }
70  
71    @Override
72    public void process(long now,
73                        HRegion region,
74                        List<Mutation> mutationsToApply,
75                        WALEdit walEdit) throws IOException {
76      byte[] byteNow = Bytes.toBytes(now);
77      // Check mutations
78      for (Mutation m : this.mutations) {
79        if (m instanceof Put) {
80          Map<byte[], List<Cell>> familyMap = m.getFamilyCellMap();
81          region.checkFamilies(familyMap.keySet());
82          region.checkTimestamps(familyMap, now);
83          region.updateKVTimestamps(familyMap.values(), byteNow);
84        } else if (m instanceof Delete) {
85          Delete d = (Delete) m;
86          region.prepareDelete(d);
87          region.prepareDeleteTimestamps(d, d.getFamilyCellMap(), byteNow);
88        } else {
89          throw new DoNotRetryIOException("Action must be Put or Delete. But was: "
90              + m.getClass().getName());
91        }
92        mutationsToApply.add(m);
93      }
94      // Apply edits to a single WALEdit
95      for (Mutation m : mutations) {
96        for (List<Cell> cells : m.getFamilyCellMap().values()) {
97          boolean writeToWAL = m.getDurability() != Durability.SKIP_WAL;
98          for (Cell cell : cells) {
99            KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
100           if (writeToWAL) walEdit.add(kv);
101         }
102       }
103     }
104   }
105 
106   @Override
107   public void preProcess(HRegion region, WALEdit walEdit) throws IOException {
108     RegionCoprocessorHost coprocessorHost = region.getCoprocessorHost();
109     if (coprocessorHost != null) {
110       for (Mutation m : mutations) {
111         if (m instanceof Put) {
112           if (coprocessorHost.prePut((Put) m, walEdit, m.getDurability())) {
113             // by pass everything
114             return;
115           }
116         } else if (m instanceof Delete) {
117           Delete d = (Delete) m;
118           region.prepareDelete(d);
119           if (coprocessorHost.preDelete(d, walEdit, d.getDurability())) {
120             // by pass everything
121             return;
122           }
123         }
124       }
125     }
126   }
127 
128   @Override
129   public void preBatchMutate(HRegion region, WALEdit walEdit) throws IOException {
130     // TODO we should return back the status of this hook run to HRegion so that those Mutations
131     // with OperationStatus as SUCCESS or FAILURE should not get applied to memstore.
132     RegionCoprocessorHost coprocessorHost = region.getCoprocessorHost();
133     OperationStatus[] opStatus = new OperationStatus[mutations.size()];
134     Arrays.fill(opStatus, OperationStatus.NOT_RUN);
135     WALEdit[] walEditsFromCP = new WALEdit[mutations.size()];
136     if (coprocessorHost != null) {
137       miniBatch = new MiniBatchOperationInProgress<Mutation>(
138           mutations.toArray(new Mutation[mutations.size()]), opStatus, walEditsFromCP, 0,
139           mutations.size());
140       coprocessorHost.preBatchMutate(miniBatch);
141     }
142     // Apply edits to a single WALEdit
143     for (int i = 0; i < mutations.size(); i++) {
144       if (opStatus[i] == OperationStatus.NOT_RUN) {
145         // Other OperationStatusCode means that Mutation is already succeeded or failed in CP hook
146         // itself. No need to apply again to region
147         if (walEditsFromCP[i] != null) {
148           // Add the WALEdit created by CP hook
149           for (KeyValue walKv : walEditsFromCP[i].getKeyValues()) {
150             walEdit.add(walKv);
151           }
152         }
153       }
154     }
155   }
156 
157   @Override
158   public void postBatchMutate(HRegion region) throws IOException {
159     RegionCoprocessorHost coprocessorHost = region.getCoprocessorHost();
160     if (coprocessorHost != null) {
161       assert miniBatch != null;
162       // Use the same miniBatch state used to call the preBatchMutate()
163       coprocessorHost.postBatchMutate(miniBatch);
164     }
165   }
166 
167   @Override
168   public void postProcess(HRegion region, WALEdit walEdit, boolean success) throws IOException {
169     RegionCoprocessorHost coprocessorHost = region.getCoprocessorHost();
170     if (coprocessorHost != null) {
171       for (Mutation m : mutations) {
172         if (m instanceof Put) {
173           coprocessorHost.postPut((Put) m, walEdit, m.getDurability());
174         } else if (m instanceof Delete) {
175           coprocessorHost.postDelete((Delete) m, walEdit, m.getDurability());
176         }
177       }
178       // At the end call the CP hook postBatchMutateIndispensably
179       if (miniBatch != null) {
180         // Directly calling this hook, with out calling pre/postBatchMutate() when Processor do a
181         // read only process. Then no need to call this batch based CP hook also.
182         coprocessorHost.postBatchMutateIndispensably(miniBatch, success);
183       }
184     }
185   }
186 
187   @Override
188   public MultiRowMutationProcessorRequest getRequestData() {
189     return MultiRowMutationProcessorRequest.getDefaultInstance();
190   }
191 
192   @Override
193   public void initialize(MultiRowMutationProcessorRequest msg) {
194     //nothing
195   }
196 
197   @Override
198   public Durability useDurability() {
199     // return true when at least one mutation requested a WAL flush (default)
200     Durability durability = Durability.USE_DEFAULT;
201     for (Mutation m : mutations) {
202       if (m.getDurability().ordinal() > durability.ordinal()) {
203         durability = m.getDurability();
204       }
205     }
206     return durability;
207   }
208 }