1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.hadoop.hbase.client;
17
18 import com.google.common.annotations.VisibleForTesting;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.hadoop.conf.Configuration;
22 import org.apache.hadoop.hbase.HConstants;
23 import org.apache.hadoop.hbase.TableName;
24 import org.apache.hadoop.hbase.classification.InterfaceAudience;
25 import org.apache.hadoop.hbase.classification.InterfaceStability;
26 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
27
28 import java.io.IOException;
29 import java.io.InterruptedIOException;
30 import java.util.Arrays;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.concurrent.ConcurrentLinkedQueue;
34 import java.util.concurrent.ExecutorService;
35 import java.util.concurrent.TimeUnit;
36 import java.util.concurrent.atomic.AtomicLong;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 @InterfaceAudience.Private
56 @InterfaceStability.Evolving
57 public class BufferedMutatorImpl implements BufferedMutator {
58
59 private static final Log LOG = LogFactory.getLog(BufferedMutatorImpl.class);
60
61 private final ExceptionListener listener;
62
63 protected ClusterConnection connection;
64 private final TableName tableName;
65 private volatile Configuration conf;
66 @VisibleForTesting
67 final ConcurrentLinkedQueue<Mutation> writeAsyncBuffer = new ConcurrentLinkedQueue<Mutation>();
68 @VisibleForTesting
69 AtomicLong currentWriteBufferSize = new AtomicLong(0);
70
71 private long writeBufferSize;
72 private final int maxKeyValueSize;
73 private boolean closed = false;
74 private final ExecutorService pool;
75 private int rpcTimeout;
76 private int operationTimeout;
77
78 @VisibleForTesting
79 protected AsyncProcess ap;
80
81 BufferedMutatorImpl(ClusterConnection conn, RpcRetryingCallerFactory rpcCallerFactory,
82 RpcControllerFactory rpcFactory, BufferedMutatorParams params) {
83 if (conn == null || conn.isClosed()) {
84 throw new IllegalArgumentException("Connection is null or closed.");
85 }
86
87 this.tableName = params.getTableName();
88 this.connection = conn;
89 this.conf = connection.getConfiguration();
90 this.pool = params.getPool();
91 this.listener = params.getListener();
92
93 ConnectionConfiguration tableConf = new ConnectionConfiguration(conf);
94 this.writeBufferSize = params.getWriteBufferSize() != BufferedMutatorParams.UNSET ?
95 params.getWriteBufferSize() : tableConf.getWriteBufferSize();
96 this.maxKeyValueSize = params.getMaxKeyValueSize() != BufferedMutatorParams.UNSET ?
97 params.getMaxKeyValueSize() : tableConf.getMaxKeyValueSize();
98 this.rpcTimeout = conn.getConfiguration().getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
99 HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
100 this.operationTimeout = conn.getConfiguration().getInt(
101 HConstants.HBASE_CLIENT_OPERATION_TIMEOUT,
102 HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT);
103
104 ap = new AsyncProcess(connection, conf, pool, rpcCallerFactory, true, rpcFactory, rpcTimeout);
105 }
106
107 @Override
108 public TableName getName() {
109 return tableName;
110 }
111
112 @Override
113 public Configuration getConfiguration() {
114 return conf;
115 }
116
117 @Override
118 public void mutate(Mutation m) throws InterruptedIOException,
119 RetriesExhaustedWithDetailsException {
120 mutate(Arrays.asList(m));
121 }
122
123 @Override
124 public void mutate(List<? extends Mutation> ms) throws InterruptedIOException,
125 RetriesExhaustedWithDetailsException {
126
127 if (closed) {
128 throw new IllegalStateException("Cannot put when the BufferedMutator is closed.");
129 }
130
131 long toAddSize = 0;
132 for (Mutation m : ms) {
133 if (m instanceof Put) {
134 validatePut((Put) m);
135 }
136 toAddSize += m.heapSize();
137 }
138
139
140
141
142 if (ap.hasError()) {
143 currentWriteBufferSize.addAndGet(toAddSize);
144 writeAsyncBuffer.addAll(ms);
145 backgroundFlushCommits(true);
146 } else {
147 currentWriteBufferSize.addAndGet(toAddSize);
148 writeAsyncBuffer.addAll(ms);
149 }
150
151
152 while (currentWriteBufferSize.get() > writeBufferSize) {
153 backgroundFlushCommits(false);
154 }
155 }
156
157
158 public void validatePut(final Put put) throws IllegalArgumentException {
159 HTable.validatePut(put, maxKeyValueSize);
160 }
161
162 @Override
163 public synchronized void close() throws IOException {
164 try {
165 if (this.closed) {
166 return;
167 }
168
169
170 backgroundFlushCommits(true);
171 this.pool.shutdown();
172 boolean terminated;
173 int loopCnt = 0;
174 do {
175
176 terminated = this.pool.awaitTermination(60, TimeUnit.SECONDS);
177 loopCnt += 1;
178 if (loopCnt >= 10) {
179 LOG.warn("close() failed to terminate pool after 10 minutes. Abandoning pool.");
180 break;
181 }
182 } while (!terminated);
183
184 } catch (InterruptedException e) {
185 LOG.warn("waitForTermination interrupted");
186
187 } finally {
188 this.closed = true;
189 }
190 }
191
192 @Override
193 public synchronized void flush() throws InterruptedIOException,
194 RetriesExhaustedWithDetailsException {
195
196
197 backgroundFlushCommits(true);
198 }
199
200
201
202
203
204
205
206
207
208 private void backgroundFlushCommits(boolean synchronous) throws
209 InterruptedIOException,
210 RetriesExhaustedWithDetailsException {
211
212 LinkedList<Mutation> buffer = new LinkedList<>();
213
214 long dequeuedSize = 0;
215
216 try {
217
218 Mutation m;
219
220
221
222
223 while (
224 (writeBufferSize <= 0 || dequeuedSize < (writeBufferSize * 2) || synchronous)
225 && (m = writeAsyncBuffer.poll()) != null) {
226 buffer.add(m);
227 long size = m.heapSize();
228 dequeuedSize += size;
229 currentWriteBufferSize.addAndGet(-size);
230 }
231
232 if (!synchronous && dequeuedSize == 0) {
233 return;
234 }
235
236 if (!synchronous) {
237 ap.submit(tableName, buffer, true, null, false);
238 if (ap.hasError()) {
239 LOG.debug(tableName + ": One or more of the operations have failed -"
240 + " waiting for all operation in progress to finish (successfully or not)");
241 }
242 }
243 if (synchronous || ap.hasError()) {
244 while (!buffer.isEmpty()) {
245 ap.submit(tableName, buffer, true, null, false);
246 }
247 RetriesExhaustedWithDetailsException error =
248 ap.waitForAllPreviousOpsAndReset(null, tableName.getNameAsString());
249 if (error != null) {
250 if (listener == null) {
251 throw error;
252 } else {
253 this.listener.onException(error, this);
254 }
255 }
256 }
257 } finally {
258 for (Mutation mut : buffer) {
259 long size = mut.heapSize();
260 currentWriteBufferSize.addAndGet(size);
261 dequeuedSize -= size;
262 writeAsyncBuffer.add(mut);
263 }
264 }
265 }
266
267
268
269
270
271
272 @Deprecated
273 public void setWriteBufferSize(long writeBufferSize) throws RetriesExhaustedWithDetailsException,
274 InterruptedIOException {
275 this.writeBufferSize = writeBufferSize;
276 if (currentWriteBufferSize.get() > writeBufferSize) {
277 flush();
278 }
279 }
280
281
282
283
284 @Override
285 public long getWriteBufferSize() {
286 return this.writeBufferSize;
287 }
288
289 public void setRpcTimeout(int rpcTimeout) {
290 this.rpcTimeout = rpcTimeout;
291 this.ap.setRpcTimeout(rpcTimeout);
292 }
293
294 public void setOperationTimeout(int operationTimeout) {
295 this.operationTimeout = operationTimeout;
296 this.ap.setOperationTimeout(operationTimeout);
297 }
298
299
300
301
302
303
304 @Deprecated
305 public List<Row> getWriteBuffer() {
306 return Arrays.asList(writeAsyncBuffer.toArray(new Row[0]));
307 }
308 }