@Test public void testEntrySink() throws Exception { Configuration conf = new Configuration(); RecoveryMode mode = (conf.getBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false) ? RecoveryMode.LOG_REPLAY : RecoveryMode.LOG_SPLITTING); EntryBuffers sink = new EntryBuffers(new PipelineController(), 1 * 1024 * 1024); for (int i = 0; i < 1000; i++) { WAL.Entry entry = createTestLogEntry(i); sink.appendEntry(entry); } assertTrue(sink.totalBuffered > 0); long amountInChunk = sink.totalBuffered; // Get a chunk RegionEntryBuffer chunk = sink.getChunkToWrite(); assertEquals(chunk.heapSize(), amountInChunk); // Make sure it got marked that a thread is "working on this" assertTrue(sink.isRegionCurrentlyWriting(TEST_REGION)); // Insert some more entries for (int i = 0; i < 500; i++) { WAL.Entry entry = createTestLogEntry(i); sink.appendEntry(entry); } // Asking for another chunk shouldn't work since the first one // is still writing assertNull(sink.getChunkToWrite()); // If we say we're done writing the first chunk, then we should be able // to get the second sink.doneWriting(chunk); RegionEntryBuffer chunk2 = sink.getChunkToWrite(); assertNotNull(chunk2); assertNotSame(chunk, chunk2); long amountInChunk2 = sink.totalBuffered; // The second chunk had fewer rows than the first assertTrue(amountInChunk2 < amountInChunk); sink.doneWriting(chunk2); assertEquals(0, sink.totalBuffered); }
@Override public boolean replicate(ReplicateContext replicateContext) { /* A note on batching in RegionReplicaReplicationEndpoint (RRRE): * * RRRE relies on batching from two different mechanisms. The first is the batching from * ReplicationSource since RRRE is a ReplicationEndpoint driven by RS. RS reads from a single * WAL file filling up a buffer of heap size "replication.source.size.capacity"(64MB) or at most * "replication.source.nb.capacity" entries or until it sees the end of file (in live tailing). * Then RS passes all the buffered edits in this replicate() call context. RRRE puts the edits * to the WALSplitter.EntryBuffers which is a blocking buffer space of up to * "hbase.region.replica.replication.buffersize" (128MB) in size. This buffer splits the edits * based on regions. * * There are "hbase.region.replica.replication.writer.threads"(default 3) writer threads which * pick largest per-region buffer and send it to the SinkWriter (see RegionReplicaOutputSink). * The SinkWriter in this case will send the wal edits to all secondary region replicas in * parallel via a retrying rpc call. EntryBuffers guarantees that while a buffer is * being written to the sink, another buffer for the same region will not be made available to * writers ensuring regions edits are not replayed out of order. * * The replicate() call won't return until all the buffers are sent and ack'd by the sinks so * that the replication can assume all edits are persisted. We may be able to do a better * pipelining between the replication thread and output sinks later if it becomes a bottleneck. */ while (this.isRunning()) { try { for (Entry entry : replicateContext.getEntries()) { entryBuffers.appendEntry(entry); } outputSink.flush(); // make sure everything is flushed ctx.getMetrics().incrLogEditsFiltered(outputSink.getSkippedEditsCounter().getAndSet(0)); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } catch (IOException e) { LOG.warn( "Received IOException while trying to replicate" + StringUtils.stringifyException(e)); } } return false; }