1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.replication.regionserver;
20
21 import java.io.EOFException;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.BlockingQueue;
28 import java.util.concurrent.LinkedBlockingQueue;
29 import java.util.concurrent.PriorityBlockingQueue;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.conf.Configuration;
34 import org.apache.hadoop.fs.FileSystem;
35 import org.apache.hadoop.fs.Path;
36 import org.apache.hadoop.hbase.Cell;
37 import org.apache.hadoop.hbase.CellUtil;
38 import org.apache.hadoop.hbase.classification.InterfaceAudience;
39 import org.apache.hadoop.hbase.classification.InterfaceStability;
40 import org.apache.hadoop.hbase.protobuf.generated.WALProtos.BulkLoadDescriptor;
41 import org.apache.hadoop.hbase.protobuf.generated.WALProtos.StoreDescriptor;
42 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
43 import org.apache.hadoop.hbase.replication.ReplicationQueueInfo;
44 import org.apache.hadoop.hbase.replication.WALEntryFilter;
45 import org.apache.hadoop.hbase.replication.regionserver.WALEntryStream.WALEntryStreamRuntimeException;
46 import org.apache.hadoop.hbase.util.Pair;
47 import org.apache.hadoop.hbase.util.Threads;
48 import org.apache.hadoop.hbase.wal.WAL.Entry;
49
50
51
52
53
54 @InterfaceAudience.Private
55 @InterfaceStability.Evolving
56 public class ReplicationSourceWALReaderThread extends Thread {
57 private static final Log LOG = LogFactory.getLog(ReplicationSourceWALReaderThread.class);
58
59 private PriorityBlockingQueue<Path> logQueue;
60 private FileSystem fs;
61 private Configuration conf;
62 private BlockingQueue<WALEntryBatch> entryBatchQueue;
63
64 private long replicationBatchSizeCapacity;
65
66 private int replicationBatchCountCapacity;
67
68 private long currentPosition;
69 private WALEntryFilter filter;
70 private long sleepForRetries;
71
72 private boolean isReaderRunning = true;
73 private ReplicationQueueInfo replicationQueueInfo;
74 private int maxRetriesMultiplier;
75 private MetricsSource metrics;
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public ReplicationSourceWALReaderThread(ReplicationSourceManager manager,
90 ReplicationQueueInfo replicationQueueInfo, PriorityBlockingQueue<Path> logQueue,
91 long startPosition,
92 FileSystem fs, Configuration conf, WALEntryFilter filter, MetricsSource metrics) {
93 this.replicationQueueInfo = replicationQueueInfo;
94 this.logQueue = logQueue;
95 this.currentPosition = startPosition;
96 this.fs = fs;
97 this.conf = conf;
98 this.filter = filter;
99 this.replicationBatchSizeCapacity =
100 this.conf.getLong("replication.source.size.capacity", 1024 * 1024 * 64);
101 this.replicationBatchCountCapacity = this.conf.getInt("replication.source.nb.capacity", 25000);
102
103
104 int batchCount = conf.getInt("replication.source.nb.batches", 1);
105 this.sleepForRetries =
106 this.conf.getLong("replication.source.sleepforretries", 1000);
107 this.maxRetriesMultiplier =
108 this.conf.getInt("replication.source.maxretriesmultiplier", 300);
109 this.metrics = metrics;
110 this.entryBatchQueue = new LinkedBlockingQueue<>(batchCount);
111 LOG.info("peerClusterZnode=" + replicationQueueInfo.getPeerClusterZnode()
112 + ", ReplicationSourceWALReaderThread : " + replicationQueueInfo.getPeerId()
113 + " inited, replicationBatchSizeCapacity=" + replicationBatchSizeCapacity
114 + ", replicationBatchCountCapacity=" + replicationBatchCountCapacity
115 + ", replicationBatchQueueCapacity=" + batchCount);
116 }
117
118 @Override
119 public void run() {
120 int sleepMultiplier = 1;
121 while (isReaderRunning()) {
122 try (WALEntryStream entryStream =
123 new WALEntryStream(logQueue, fs, conf, currentPosition, metrics)) {
124 while (isReaderRunning()) {
125 WALEntryBatch batch = null;
126 while (entryStream.hasNext()) {
127 if (batch == null) {
128 batch = new WALEntryBatch(replicationBatchCountCapacity, entryStream.getCurrentPath());
129 }
130 Entry entry = entryStream.next();
131 entry = filterEntry(entry);
132 if (entry != null) {
133 WALEdit edit = entry.getEdit();
134 if (edit != null && !edit.isEmpty()) {
135 long entrySize = getEntrySize(entry);
136 batch.addEntry(entry);
137 updateBatchStats(batch, entry, entryStream.getPosition(), entrySize);
138
139 if (batch.getHeapSize() >= replicationBatchSizeCapacity
140 || batch.getNbEntries() >= replicationBatchCountCapacity) {
141 break;
142 }
143 }
144 }
145 }
146 if (batch != null && (!batch.getLastSeqIds().isEmpty() || batch.getNbEntries() > 0)) {
147 if (LOG.isTraceEnabled()) {
148 LOG.trace(String.format("Read %s WAL entries eligible for replication",
149 batch.getNbEntries()));
150 }
151 entryBatchQueue.put(batch);
152 sleepMultiplier = 1;
153 } else {
154 LOG.trace("Didn't read any new entries from WAL");
155 if (replicationQueueInfo.isQueueRecovered()) {
156
157 setReaderRunning(false);
158
159 entryBatchQueue.put(batch != null ? batch
160 : new WALEntryBatch(replicationBatchCountCapacity, entryStream.getCurrentPath()));
161 } else {
162 Thread.sleep(sleepForRetries);
163 }
164 }
165 currentPosition = entryStream.getPosition();
166 entryStream.reset();
167 }
168 } catch (IOException | WALEntryStreamRuntimeException e) {
169 if (sleepMultiplier < maxRetriesMultiplier) {
170 LOG.debug("Failed to read stream of replication entries: " + e);
171 sleepMultiplier++;
172 } else {
173 LOG.error("Failed to read stream of replication entries", e);
174 handleEofException(e);
175 }
176 Threads.sleep(sleepForRetries * sleepMultiplier);
177 } catch (InterruptedException e) {
178 LOG.trace("Interrupted while sleeping between WAL reads");
179 Thread.currentThread().interrupt();
180 }
181 }
182 }
183
184
185
186
187 private void handleEofException(Exception e) {
188 if (e.getCause() instanceof EOFException && logQueue.size() > 1
189 && conf.getBoolean("replication.source.eof.autorecovery", false)) {
190 try {
191 if (fs.getFileStatus(logQueue.peek()).getLen() == 0) {
192 LOG.warn("Forcing removal of 0 length log in queue: " + logQueue.peek());
193 logQueue.remove();
194 currentPosition = 0;
195 }
196 } catch (IOException ioe) {
197 LOG.warn("Couldn't get file length information about log " + logQueue.peek());
198 }
199 }
200 }
201
202 public Path getCurrentPath() {
203
204 WALEntryBatch batchQueueHead = entryBatchQueue.peek();
205 if (batchQueueHead != null) {
206 return batchQueueHead.lastWalPath;
207 }
208
209 return logQueue.peek();
210 }
211
212 private Entry filterEntry(Entry entry) {
213 Entry filtered = filter.filter(entry);
214 if (entry != null && filtered == null) {
215 metrics.incrLogEditsFiltered();
216 }
217 return filtered;
218 }
219
220
221
222
223
224
225
226 public WALEntryBatch take() throws InterruptedException {
227 return entryBatchQueue.take();
228 }
229
230 private long getEntrySize(Entry entry) {
231 WALEdit edit = entry.getEdit();
232 return edit.heapSize() + calculateTotalSizeOfStoreFiles(edit);
233 }
234
235 private void updateBatchStats(WALEntryBatch batch, Entry entry, long entryPosition, long entrySize) {
236 WALEdit edit = entry.getEdit();
237 if (edit != null && !edit.isEmpty()) {
238 batch.incrementHeapSize(entrySize);
239 Pair<Integer, Integer> nbRowsAndHFiles = countDistinctRowKeysAndHFiles(edit);
240 batch.incrementNbRowKeys(nbRowsAndHFiles.getFirst());
241 batch.incrementNbHFiles(nbRowsAndHFiles.getSecond());
242 }
243 batch.lastWalPosition = entryPosition;
244 }
245
246
247
248
249
250
251
252 private Pair<Integer, Integer> countDistinctRowKeysAndHFiles(WALEdit edit) {
253 List<Cell> cells = edit.getCells();
254 int distinctRowKeys = 1;
255 int totalHFileEntries = 0;
256 Cell lastCell = cells.get(0);
257
258 int totalCells = edit.size();
259 for (int i = 0; i < totalCells; i++) {
260
261 if (CellUtil.matchingQualifier(cells.get(i), WALEdit.BULK_LOAD)) {
262 try {
263 BulkLoadDescriptor bld = WALEdit.getBulkLoadDescriptor(cells.get(i));
264 List<StoreDescriptor> stores = bld.getStoresList();
265 int totalStores = stores.size();
266 for (int j = 0; j < totalStores; j++) {
267 totalHFileEntries += stores.get(j).getStoreFileList().size();
268 }
269 } catch (IOException e) {
270 LOG.error("Failed to deserialize bulk load entry from wal edit. "
271 + "Then its hfiles count will not be added into metric.");
272 }
273 }
274
275 if (!CellUtil.matchingRow(cells.get(i), lastCell)) {
276 distinctRowKeys++;
277 }
278 lastCell = cells.get(i);
279 }
280
281 Pair<Integer, Integer> result = new Pair<>(distinctRowKeys, totalHFileEntries);
282 return result;
283 }
284
285
286
287
288
289
290 private int calculateTotalSizeOfStoreFiles(WALEdit edit) {
291 List<Cell> cells = edit.getCells();
292 int totalStoreFilesSize = 0;
293
294 int totalCells = edit.size();
295 for (int i = 0; i < totalCells; i++) {
296 if (CellUtil.matchingQualifier(cells.get(i), WALEdit.BULK_LOAD)) {
297 try {
298 BulkLoadDescriptor bld = WALEdit.getBulkLoadDescriptor(cells.get(i));
299 List<StoreDescriptor> stores = bld.getStoresList();
300 int totalStores = stores.size();
301 for (int j = 0; j < totalStores; j++) {
302 totalStoreFilesSize += stores.get(j).getStoreFileSizeBytes();
303 }
304 } catch (IOException e) {
305 LOG.error("Failed to deserialize bulk load entry from wal edit. "
306 + "Size of HFiles part of cell will not be considered in replication "
307 + "request size calculation.",
308 e);
309 }
310 }
311 }
312 return totalStoreFilesSize;
313 }
314
315
316
317
318 public boolean isReaderRunning() {
319 return isReaderRunning && !isInterrupted();
320 }
321
322
323
324
325 public void setReaderRunning(boolean readerRunning) {
326 this.isReaderRunning = readerRunning;
327 }
328
329
330
331
332
333 static class WALEntryBatch {
334 private List<Entry> walEntries;
335
336 private Path lastWalPath;
337
338 private long lastWalPosition = 0;
339
340 private int nbRowKeys = 0;
341
342 private int nbHFiles = 0;
343
344 private long heapSize = 0;
345
346 private Map<String, Long> lastSeqIds = new HashMap<>();
347
348
349
350
351
352
353 private WALEntryBatch(int maxNbEntries, Path lastWalPath) {
354 this.walEntries = new ArrayList<>(maxNbEntries);
355 this.lastWalPath = lastWalPath;
356 }
357
358 public void addEntry(Entry entry) {
359 walEntries.add(entry);
360 }
361
362
363
364
365 public List<Entry> getWalEntries() {
366 return walEntries;
367 }
368
369
370
371
372 public Path getLastWalPath() {
373 return lastWalPath;
374 }
375
376
377
378
379 public long getLastWalPosition() {
380 return lastWalPosition;
381 }
382
383 public int getNbEntries() {
384 return walEntries.size();
385 }
386
387
388
389
390 public int getNbRowKeys() {
391 return nbRowKeys;
392 }
393
394
395
396
397 public int getNbHFiles() {
398 return nbHFiles;
399 }
400
401
402
403
404 public int getNbOperations() {
405 return getNbRowKeys() + getNbHFiles();
406 }
407
408
409
410
411 public long getHeapSize() {
412 return heapSize;
413 }
414
415
416
417
418 public Map<String, Long> getLastSeqIds() {
419 return lastSeqIds;
420 }
421
422 private void incrementNbRowKeys(int increment) {
423 nbRowKeys += increment;
424 }
425
426 private void incrementNbHFiles(int increment) {
427 nbHFiles += increment;
428 }
429
430 private void incrementHeapSize(long increment) {
431 heapSize += increment;
432 }
433
434 private void setLastPosition(String region, Long sequenceId) {
435 getLastSeqIds().put(region, sequenceId);
436 }
437 }
438 }