18
18
package com .alipay .oceanbase .hbase .util ;
19
19
20
20
import com .alipay .oceanbase .hbase .OHTable ;
21
- import com .alipay .oceanbase .rpc .ObTableClient ;
22
- import com .alipay .oceanbase .rpc .protocol .payload .impl .execute .*;
21
+ import com .google .common .annotations .VisibleForTesting ;
23
22
import org .apache .hadoop .classification .InterfaceAudience ;
24
23
import org .apache .hadoop .conf .Configuration ;
25
- import org .apache .hadoop .hbase .KeyValue ;
26
24
import org .apache .hadoop .hbase .TableName ;
27
25
import org .apache .hadoop .hbase .client .*;
28
- import org .apache .hadoop .hbase .util .Bytes ;
29
26
import org .slf4j .Logger ;
30
27
31
28
import java .io .IOException ;
34
31
import java .util .concurrent .ExecutorService ;
35
32
import java .util .concurrent .TimeUnit ;
36
33
import java .util .concurrent .atomic .AtomicLong ;
37
- import java .util .concurrent .atomic .AtomicReference ;
38
34
import static com .alipay .oceanbase .rpc .util .TableClientLoggerFactory .LCD ;
39
35
40
36
@ InterfaceAudience .Private
41
37
public class OHBufferedMutatorImpl implements BufferedMutator {
42
- private static final Logger LOGGER = TableHBaseLoggerFactory
43
- .getLogger (OHBufferedMutatorImpl .class );
38
+ private static final Logger LOGGER = TableHBaseLoggerFactory
39
+ .getLogger (OHBufferedMutatorImpl .class );
44
40
45
- private final ExceptionListener listener ;
41
+ private final ExceptionListener listener ;
46
42
47
- protected final ObTableClient obTableClient ;
48
- private final TableName tableName ;
49
- private volatile Configuration conf ;
50
- private final OHConnectionConfiguration connectionConfig ;
43
+ private final OHTable ohTable ;
44
+ private final TableName tableName ;
45
+ private volatile Configuration conf ;
51
46
52
- final ConcurrentLinkedQueue <Mutation > asyncWriteBuffer = new ConcurrentLinkedQueue <Mutation >();
53
- AtomicLong currentAsyncBufferSize = new AtomicLong (0 );
47
+ @ VisibleForTesting
48
+ final ConcurrentLinkedQueue <Mutation > asyncWriteBuffer = new ConcurrentLinkedQueue <Mutation >();
49
+ @ VisibleForTesting
50
+ AtomicLong currentAsyncBufferSize = new AtomicLong (0 );
54
51
55
- private AtomicReference < Class <?>> type = new AtomicReference <>( null ) ;
56
- private final long writeBufferSize ;
57
- private final int maxKeyValueSize ;
58
- private boolean closed = false ;
59
- private final ExecutorService pool ;
60
- private final int rpcTimeout ;
52
+ private long writeBufferSize ;
53
+ private final int maxKeyValueSize ;
54
+ private boolean closed = false ;
55
+ private final ExecutorService pool ;
56
+ private int rpcTimeout ;
57
+ private int operationTimeout ;
61
58
62
59
public OHBufferedMutatorImpl (OHConnectionImpl ohConnection , BufferedMutatorParams params )
63
60
throws IOException {
64
61
if (ohConnection == null || ohConnection .isClosed ()) {
65
62
throw new IllegalArgumentException ("Connection is null or closed." );
66
63
}
67
- // create a ObTableClient to do rpc operations
68
- this .obTableClient = ObTableClientManager .getOrCreateObTableClient (ohConnection
69
- .getOHConnectionConfiguration ());
70
-
71
64
// init params in OHBufferedMutatorImpl
72
65
this .tableName = params .getTableName ();
73
66
this .conf = ohConnection .getConfiguration ();
74
- this .connectionConfig = ohConnection .getOHConnectionConfiguration ();
75
67
this .listener = params .getListener ();
68
+
69
+ OHConnectionConfiguration connectionConfig = ohConnection .getOHConnectionConfiguration ();
76
70
this .pool = params .getPool ();
77
- this .obTableClient .setRuntimeBatchExecutor (pool );
71
+ this .rpcTimeout = connectionConfig .getRpcTimeout ();
72
+ this .operationTimeout = connectionConfig .getOperationTimeout ();
78
73
79
74
this .writeBufferSize = params .getWriteBufferSize () != OHConnectionImpl .BUFFERED_PARAM_UNSET ? params
80
75
.getWriteBufferSize () : connectionConfig .getWriteBufferSize ();
81
76
this .maxKeyValueSize = params .getMaxKeyValueSize () != OHConnectionImpl .BUFFERED_PARAM_UNSET ? params
82
77
.getMaxKeyValueSize () : connectionConfig .getMaxKeyValueSize ();
83
- this .rpcTimeout = connectionConfig .getRpcTimeout ();
84
- this .obTableClient .setRpcExecuteTimeout (rpcTimeout );
78
+
79
+ // create an OHTable object to do batch work
80
+ this .ohTable = new OHTable (tableName , ohConnection , connectionConfig , pool );
85
81
}
86
82
87
83
@ Override
@@ -119,38 +115,37 @@ public void mutate(List<? extends Mutation> mutations) throws IOException {
119
115
}
120
116
121
117
long toAddSize = 0 ;
122
- // check if every mutation's family is the same
123
- // check if mutations are the same type
124
118
for (Mutation m : mutations ) {
125
- OHTable .checkFamilyViolation (m .getFamilyMap ().keySet (), true );
126
- validateInsUpAndDelete (m );
127
- Class <?> curType = m .getClass ();
128
- // set the type of this BufferedMutator
129
- if (type .get () == null ) {
130
- type .compareAndSet (null , mutations .get (0 ).getClass ());
131
- }
132
- if (!type .get ().equals (curType )) {
133
- throw new IllegalArgumentException ("Not support different type in one batch." );
134
- }
119
+ validateOperation (m );
135
120
toAddSize += m .heapSize ();
136
121
}
137
122
138
123
currentAsyncBufferSize .addAndGet (toAddSize );
139
124
asyncWriteBuffer .addAll (mutations );
140
125
141
- asyncExecute (false );
126
+ if (currentAsyncBufferSize .get () > writeBufferSize ) {
127
+ execute (false );
128
+ }
129
+
142
130
}
143
131
144
132
/**
145
133
* Check whether the mutation is Put or Delete in 1.x
146
134
* @param mt - mutation operation
147
135
*/
148
- private void validateInsUpAndDelete (Mutation mt ) throws IllegalArgumentException {
136
+ private void validateOperation (Mutation mt ) throws IllegalArgumentException {
137
+ if (mt == null ) {
138
+ throw new IllegalArgumentException ("Mutation operation cannot be null" );
139
+ }
149
140
if (!(mt instanceof Put ) && !(mt instanceof Delete )) {
150
141
throw new IllegalArgumentException ("Only support for Put and Delete for now." );
151
142
}
152
143
if (mt instanceof Put ) {
144
+ // family empty check is in validatePut
153
145
HTable .validatePut ((Put ) mt , maxKeyValueSize );
146
+ OHTable .checkFamilyViolation (mt .getFamilyMap ().keySet (), true );
147
+ } else {
148
+ OHTable .checkFamilyViolation (mt .getFamilyMap ().keySet (), false );
154
149
}
155
150
}
156
151
@@ -161,91 +156,49 @@ private void validateInsUpAndDelete(Mutation mt) throws IllegalArgumentException
161
156
* @param flushAll - if true, sends all the writes and wait for all of them to finish before
162
157
* returning.
163
158
*/
164
- private void asyncExecute (boolean flushAll ) throws IOException {
159
+ private void execute (boolean flushAll ) throws IOException {
165
160
LinkedList <Mutation > execBuffer = new LinkedList <>();
166
- ObTableBatchOperationRequest request = null ;
167
- // namespace n1, n1:table_name
168
- // namespace default, table_name
169
- String tableNameString = tableName .getNameAsString ();
161
+ long dequeuedSize = 0L ;
170
162
try {
171
- while (true ) {
172
- try {
173
- if (!flushAll || asyncWriteBuffer .isEmpty ()) {
174
- if (currentAsyncBufferSize .get () <= writeBufferSize ) {
175
- break ;
176
- }
177
- }
178
- Mutation m ;
179
- while ((m = asyncWriteBuffer .poll ()) != null ) {
180
- execBuffer .add (m );
181
- long size = m .heapSize ();
182
- currentAsyncBufferSize .addAndGet (-size );
183
- }
184
- // in concurrent situation, asyncWriteBuffer may be empty here
185
- // for other threads flush all buffer
186
- if (execBuffer .isEmpty ()) {
187
- break ;
188
- }
189
- // for now, operations' family is the same
190
- byte [] family = execBuffer .getFirst ().getFamilyMap ().firstKey ();
191
- ObTableBatchOperation batch = buildObTableBatchOperation (execBuffer );
192
- // table_name$cf_name
193
- String targetTableName = OHTable .getTargetTableName (tableNameString , Bytes .toString (family ), conf );
194
- request = OHTable .buildObTableBatchOperationRequest (batch , targetTableName );
195
- } catch (Exception ex ) {
196
- LOGGER .error ("Errors occur before mutation operation" , ex );
197
- throw new IllegalArgumentException ("Errors occur before mutation operation" , ex );
198
- }
199
- try {
200
- ObTableBatchOperationResult result = (ObTableBatchOperationResult ) obTableClient .execute (request );
201
- } catch (Exception ex ) {
202
- LOGGER .debug ("Errors occur during mutation operation" , ex );
203
- Mutation m = null ;
204
- try {
205
- // retry every single operation
206
- while (!execBuffer .isEmpty ()) {
207
- // poll elements from execBuffer to recollect remaining operations
208
- m = execBuffer .poll ();
209
- byte [] family = m .getFamilyMap ().firstKey ();
210
- ObTableBatchOperation batch = buildObTableBatchOperation (Collections .singletonList (m ));
211
- String targetTableName = OHTable .getTargetTableName (tableNameString , Bytes .toString (family ), conf );
212
- request = OHTable .buildObTableBatchOperationRequest (batch , targetTableName );
213
- ObTableBatchOperationResult result = (ObTableBatchOperationResult ) obTableClient .execute (request );
214
- }
215
- } catch (Exception newEx ) {
216
- if (m != null ) {
217
- execBuffer .addFirst (m );
218
- }
219
- // if retry fails, only recollect remaining operations
220
- while (!execBuffer .isEmpty ()) {
221
- m = execBuffer .poll ();
222
- long size = m .heapSize ();
223
- asyncWriteBuffer .add (m );
224
- currentAsyncBufferSize .addAndGet (size );
225
- }
226
- throw newEx ;
227
- }
228
- }
163
+ Mutation m ;
164
+ while ((writeBufferSize <= 0 || dequeuedSize < (writeBufferSize * 2 ) || flushAll )
165
+ && (m = asyncWriteBuffer .poll ()) != null ) {
166
+ execBuffer .add (m );
167
+ long size = m .heapSize ();
168
+ currentAsyncBufferSize .addAndGet (-size );
169
+ dequeuedSize += size ;
170
+ }
171
+
172
+ if (execBuffer .isEmpty ()) {
173
+ return ;
229
174
}
175
+ ohTable .batch (execBuffer );
176
+ // if commit all successfully, clean execBuffer
177
+ execBuffer .clear ();
230
178
} catch (Exception ex ) {
231
179
LOGGER .error (LCD .convert ("01-00026" ), ex );
232
- // if the cause is illegal argument, directly throw to user
233
- if (ex instanceof IllegalArgumentException ) {
234
- throw (IllegalArgumentException ) ex ;
235
- }
236
- // TODO: need to collect error information and actions during batch operations
237
- // TODO: maybe keep in ObTableBatchOperationResult
238
- List <Throwable > throwables = new ArrayList <Throwable >();
239
- List <Row > actions = new ArrayList <Row >();
240
- List <String > addresses = new ArrayList <String >();
241
- throwables .add (ex );
242
- RetriesExhaustedWithDetailsException error = new RetriesExhaustedWithDetailsException (
243
- new ArrayList <Throwable >(throwables ),
244
- new ArrayList <Row >(actions ), new ArrayList <String >(addresses ));
245
- if (listener == null ) {
246
- throw error ;
180
+ if (ex .getCause () instanceof RetriesExhaustedWithDetailsException ) {
181
+ LOGGER .error (tableName + ": One or more of the operations have failed after retries." );
182
+ RetriesExhaustedWithDetailsException retryException = (RetriesExhaustedWithDetailsException ) ex .getCause ();
183
+ // recollect mutations
184
+ execBuffer .clear ();
185
+ for (int i = 0 ; i < retryException .getNumExceptions (); ++i ) {
186
+ execBuffer .add ((Mutation ) retryException .getRow (i ));
187
+ }
188
+ if (listener != null ) {
189
+ listener .onException (retryException , this );
190
+ } else {
191
+ throw retryException ;
192
+ }
247
193
} else {
248
- listener .onException (error , this );
194
+ LOGGER .error ("Errors unrelated to operations occur during mutation operation" , ex );
195
+ throw ex ;
196
+ }
197
+ } finally {
198
+ for (Mutation mutation : execBuffer ) {
199
+ long size = mutation .heapSize ();
200
+ currentAsyncBufferSize .addAndGet (size );
201
+ asyncWriteBuffer .add (mutation );
249
202
}
250
203
}
251
204
}
@@ -256,7 +209,7 @@ public void close() throws IOException {
256
209
return ;
257
210
}
258
211
try {
259
- asyncExecute (true );
212
+ execute (true );
260
213
} finally {
261
214
// the pool in ObTableClient will be shut down too
262
215
this .pool .shutdown ();
@@ -273,27 +226,40 @@ public void close() throws IOException {
273
226
}
274
227
}
275
228
229
+ @ Deprecated
230
+ public void setWriteBufferSize (long writeBufferSize ) throws IOException {
231
+ this .writeBufferSize = writeBufferSize ;
232
+ if (currentAsyncBufferSize .get () > writeBufferSize ) {
233
+ flush ();
234
+ }
235
+ }
236
+
276
237
/**
277
238
* Force to commit all operations
278
239
* do not care whether the pool is shut down or this BufferedMutator is closed
279
240
*/
280
241
@ Override
281
242
public void flush () throws IOException {
282
- asyncExecute (true );
243
+ execute (true );
283
244
}
284
245
285
246
@ Override
286
247
public long getWriteBufferSize () {
287
248
return this .writeBufferSize ;
288
249
}
289
250
290
- private ObTableBatchOperation buildObTableBatchOperation (List <? extends Mutation > execBuffer ) {
291
- List <KeyValue > keyValueList = new LinkedList <>();
292
- for (Mutation mutation : execBuffer ) {
293
- for (Map .Entry <byte [], List <KeyValue >> entry : mutation .getFamilyMap ().entrySet ()) {
294
- keyValueList .addAll (entry .getValue ());
295
- }
296
- }
297
- return OHTable .buildObTableBatchOperation (keyValueList , false , null );
251
+ public void setRpcTimeout (int rpcTimeout ) {
252
+ this .rpcTimeout = rpcTimeout ;
253
+ this .ohTable .setRpcTimeout (rpcTimeout );
254
+ }
255
+
256
+ public void setOperationTimeout (int operationTimeout ) {
257
+ this .operationTimeout = operationTimeout ;
258
+ this .ohTable .setOperationTimeout (operationTimeout );
259
+ }
260
+
261
+ @ Deprecated
262
+ public List <Row > getWriteBuffer () {
263
+ return Arrays .asList (asyncWriteBuffer .toArray (new Row [0 ]));
298
264
}
299
265
}
0 commit comments