1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.coprocessor.example;
19
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Set;
25 import java.util.TreeSet;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.Coprocessor;
30 import org.apache.hadoop.hbase.CoprocessorEnvironment;
31 import org.apache.hadoop.hbase.HConstants;
32 import org.apache.hadoop.hbase.KeyValue;
33 import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
34 import org.apache.hadoop.hbase.client.Delete;
35 import org.apache.hadoop.hbase.client.Mutation;
36 import org.apache.hadoop.hbase.client.Scan;
37 import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
38 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
39 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
40 import org.apache.hadoop.hbase.coprocessor.example.generated.BulkDeleteProtos.BulkDeleteRequest;
41 import org.apache.hadoop.hbase.coprocessor.example.generated.BulkDeleteProtos.BulkDeleteResponse;
42 import org.apache.hadoop.hbase.coprocessor.example.generated.BulkDeleteProtos.BulkDeleteService;
43 import org.apache.hadoop.hbase.coprocessor.example.generated.BulkDeleteProtos.BulkDeleteRequest.DeleteType;
44 import org.apache.hadoop.hbase.coprocessor.example.generated.BulkDeleteProtos.BulkDeleteResponse.Builder;
45 import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
46 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
47 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
48 import org.apache.hadoop.hbase.regionserver.HRegion;
49 import org.apache.hadoop.hbase.regionserver.OperationStatus;
50 import org.apache.hadoop.hbase.regionserver.RegionScanner;
51 import org.apache.hadoop.hbase.util.Bytes;
52
53 import com.google.protobuf.RpcCallback;
54 import com.google.protobuf.RpcController;
55 import com.google.protobuf.Service;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public class BulkDeleteEndpoint extends BulkDeleteService implements CoprocessorService,
98 Coprocessor {
99 private static final String NO_OF_VERSIONS_TO_DELETE = "noOfVersionsToDelete";
100 private static final Log LOG = LogFactory.getLog(BulkDeleteEndpoint.class);
101
102 private RegionCoprocessorEnvironment env;
103
104 @Override
105 public Service getService() {
106 return this;
107 }
108
109 @Override
110 public void delete(RpcController controller, BulkDeleteRequest request,
111 RpcCallback<BulkDeleteResponse> done) {
112 long totalRowsDeleted = 0L;
113 long totalVersionsDeleted = 0L;
114 HRegion region = env.getRegion();
115 int rowBatchSize = request.getRowBatchSize();
116 Long timestamp = null;
117 if (request.hasTimestamp()) {
118 timestamp = request.getTimestamp();
119 }
120 DeleteType deleteType = request.getDeleteType();
121 boolean hasMore = true;
122 RegionScanner scanner = null;
123 try {
124 Scan scan = ProtobufUtil.toScan(request.getScan());
125 if (scan.getFilter() == null && deleteType == DeleteType.ROW) {
126
127
128
129 scan.setFilter(new FirstKeyOnlyFilter());
130 }
131
132
133 scanner = region.getScanner(scan);
134 while (hasMore) {
135 List<List<KeyValue>> deleteRows = new ArrayList<List<KeyValue>>(rowBatchSize);
136 for (int i = 0; i < rowBatchSize; i++) {
137 List<KeyValue> results = new ArrayList<KeyValue>();
138 hasMore = scanner.next(results);
139 if (results.size() > 0) {
140 deleteRows.add(results);
141 }
142 if (!hasMore) {
143
144 break;
145 }
146 }
147 if (deleteRows.size() > 0) {
148 Mutation[] deleteArr = new Mutation[deleteRows.size()];
149 int i = 0;
150 for (List<KeyValue> deleteRow : deleteRows) {
151 deleteArr[i++] = createDeleteMutation(deleteRow, deleteType, timestamp);
152 }
153 OperationStatus[] opStatus = region.batchMutate(deleteArr);
154 for (i = 0; i < opStatus.length; i++) {
155 if (opStatus[i].getOperationStatusCode() != OperationStatusCode.SUCCESS) {
156 break;
157 }
158 totalRowsDeleted++;
159 if (deleteType == DeleteType.VERSION) {
160 byte[] versionsDeleted = deleteArr[i].getAttribute(
161 NO_OF_VERSIONS_TO_DELETE);
162 if (versionsDeleted != null) {
163 totalVersionsDeleted += Bytes.toInt(versionsDeleted);
164 }
165 }
166 }
167 }
168 }
169 } catch (IOException ioe) {
170 LOG.error(ioe);
171
172 ResponseConverter.setControllerException(controller, ioe);
173 } finally {
174 if (scanner != null) {
175 try {
176 scanner.close();
177 } catch (IOException ioe) {
178 LOG.error(ioe);
179 }
180 }
181 }
182 Builder responseBuilder = BulkDeleteResponse.newBuilder();
183 responseBuilder.setRowsDeleted(totalRowsDeleted);
184 if (deleteType == DeleteType.VERSION) {
185 responseBuilder.setVersionsDeleted(totalVersionsDeleted);
186 }
187 BulkDeleteResponse result = responseBuilder.build();
188 done.run(result);
189 }
190
191 private Delete createDeleteMutation(List<KeyValue> deleteRow, DeleteType deleteType,
192 Long timestamp) {
193 long ts;
194 if (timestamp == null) {
195 ts = HConstants.LATEST_TIMESTAMP;
196 } else {
197 ts = timestamp;
198 }
199
200 byte[] row = deleteRow.get(0).getRow();
201 Delete delete = new Delete(row, ts);
202 if (deleteType == DeleteType.FAMILY) {
203 Set<byte[]> families = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
204 for (KeyValue kv : deleteRow) {
205 if (families.add(kv.getFamily())) {
206 delete.deleteFamily(kv.getFamily(), ts);
207 }
208 }
209 } else if (deleteType == DeleteType.COLUMN) {
210 Set<Column> columns = new HashSet<Column>();
211 for (KeyValue kv : deleteRow) {
212 Column column = new Column(kv.getFamily(), kv.getQualifier());
213 if (columns.add(column)) {
214
215
216
217 delete.deleteColumns(column.family, column.qualifier, ts);
218 }
219 }
220 } else if (deleteType == DeleteType.VERSION) {
221
222
223
224
225 int noOfVersionsToDelete = 0;
226 if (timestamp == null) {
227 for (KeyValue kv : deleteRow) {
228 delete.deleteColumn(kv.getFamily(), kv.getQualifier(), kv.getTimestamp());
229 noOfVersionsToDelete++;
230 }
231 } else {
232 Set<Column> columns = new HashSet<Column>();
233 for (KeyValue kv : deleteRow) {
234 Column column = new Column(kv.getFamily(), kv.getQualifier());
235
236 if (columns.add(column)) {
237 delete.deleteColumn(column.family, column.qualifier, ts);
238 noOfVersionsToDelete++;
239 }
240 }
241 }
242 delete.setAttribute(NO_OF_VERSIONS_TO_DELETE, Bytes.toBytes(noOfVersionsToDelete));
243 }
244 return delete;
245 }
246
247 private static class Column {
248 private byte[] family;
249 private byte[] qualifier;
250
251 public Column(byte[] family, byte[] qualifier) {
252 this.family = family;
253 this.qualifier = qualifier;
254 }
255
256 @Override
257 public boolean equals(Object other) {
258 if (!(other instanceof Column)) {
259 return false;
260 }
261 Column column = (Column) other;
262 return Bytes.equals(this.family, column.family)
263 && Bytes.equals(this.qualifier, column.qualifier);
264 }
265
266 @Override
267 public int hashCode() {
268 int h = 31;
269 h = h + 13 * Bytes.hashCode(this.family);
270 h = h + 13 * Bytes.hashCode(this.qualifier);
271 return h;
272 }
273 }
274
275 @Override
276 public void start(CoprocessorEnvironment env) throws IOException {
277 if (env instanceof RegionCoprocessorEnvironment) {
278 this.env = (RegionCoprocessorEnvironment) env;
279 } else {
280 throw new CoprocessorException("Must be loaded on a table region!");
281 }
282 }
283
284 @Override
285 public void stop(CoprocessorEnvironment env) throws IOException {
286
287 }
288 }