/*
* Copyright 2014 NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.navercorp.pinpoint.profiler.sender.planer;
import com.navercorp.pinpoint.profiler.sender.PartitionedByteBufferLocator;
import com.navercorp.pinpoint.profiler.sender.SpanStreamSendData;
import com.navercorp.pinpoint.profiler.sender.SpanStreamSendDataFactory;
import com.navercorp.pinpoint.profiler.sender.SpanStreamSendDataMode;
import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author Taejin Koo
*/
public abstract class AbstractSpanStreamSendDataPlaner implements SendDataPlaner {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
protected PartitionedByteBufferLocator partitionedByteBufferLocator;
private final SpanStreamSendDataFactory spanStreamSendDataFactory;
public AbstractSpanStreamSendDataPlaner(PartitionedByteBufferLocator partitionedByteBufferLocator, SpanStreamSendDataFactory spanStreamSendDataFactory) {
this.partitionedByteBufferLocator = partitionedByteBufferLocator;
this.spanStreamSendDataFactory = spanStreamSendDataFactory;
}
@Override
public Iterator<SpanStreamSendData> getSendDataIterator() throws IOException {
return getSendDataIterator(spanStreamSendDataFactory.create());
}
@Override
public Iterator<SpanStreamSendData> getSendDataIterator(SpanStreamSendData spanStreamSendData) throws IOException {
return getSendDataIterator(spanStreamSendData, null);
}
@Override
public Iterator<SpanStreamSendData> getSendDataIterator(SpanStreamSendData spanStreamSendData, HeaderTBaseSerializer serializer) throws IOException {
logger.info("process");
List<SpanStreamSendData> spanStreamSendDataList = new ArrayList<SpanStreamSendData>();
SpanStreamSendData currentSpanStreamSendData = spanStreamSendData;
FlushMode mode = plan(spanStreamSendData);
if (mode == FlushMode.FLUSH_FIRST) {
spanStreamSendDataList.add(currentSpanStreamSendData);
currentSpanStreamSendData.setFlushMode(SpanStreamSendDataMode.WAIT_BUFFER_AND_FLUSH);
currentSpanStreamSendData = spanStreamSendDataFactory.create();
}
int byteBufferCapacity = partitionedByteBufferLocator.getTotalByteBufferCapacity();
if (currentSpanStreamSendData.isAvailableBufferCapacity(byteBufferCapacity)) {
if (currentSpanStreamSendData.getAvailableGatheringComponentsCount() < 2) {
currentSpanStreamSendData.setFlushMode();
}
currentSpanStreamSendData.addBuffer(partitionedByteBufferLocator.getByteBuffer(), serializer);
spanStreamSendDataList.add(currentSpanStreamSendData);
} else {
int markFromPartitionIndex = 0;
int markToPartitionIndex = -1;
int flushBufferCapacity = 0;
for (int i = 0; i < partitionedByteBufferLocator.getPartitionedCount(); i++) {
int appendBufferSize = 0;
if (!partitionedByteBufferLocator.isLastPartitionIndex(i)) {
appendBufferSize = getSpanChunkLength();
}
flushBufferCapacity += partitionedByteBufferLocator.getByteBufferCapacity(i);
if (needFlush(currentSpanStreamSendData, flushBufferCapacity, appendBufferSize)) {
ByteBuffer addBuffer = getByteBuffer(partitionedByteBufferLocator, markFromPartitionIndex, markToPartitionIndex);
if (addBuffer != null) {
ByteBuffer[] byteBufferArray = new ByteBuffer[2];
byteBufferArray[0] = addBuffer;
byteBufferArray[1] = getSpanChunkBuffer();
currentSpanStreamSendData.addBuffer(byteBufferArray);
}
currentSpanStreamSendData.setFlushMode();
spanStreamSendDataList.add(currentSpanStreamSendData);
currentSpanStreamSendData = spanStreamSendDataFactory.create();
markFromPartitionIndex = i;
flushBufferCapacity = partitionedByteBufferLocator.getByteBufferCapacity(i);
}
markToPartitionIndex = i;
if (partitionedByteBufferLocator.isLastPartitionIndex(i)) {
ByteBuffer addBuffer = getByteBuffer(partitionedByteBufferLocator, markFromPartitionIndex, markToPartitionIndex);
currentSpanStreamSendData.addBuffer(addBuffer, serializer);
spanStreamSendDataList.add(currentSpanStreamSendData);
}
}
}
return spanStreamSendDataList.iterator();
}
private FlushMode plan(SpanStreamSendData spanStreamSendData) {
int byteBufferCapacity = partitionedByteBufferLocator.getTotalByteBufferCapacity();
if (byteBufferCapacity < spanStreamSendData.getAvailableBufferCapacity()) {
return FlushMode.NORMAL;
} else if (byteBufferCapacity < spanStreamSendData.getMaxBufferCapacity()) {
return FlushMode.FLUSH_FIRST;
} else {
ByteBuffer spanChunkBuffer = getSpanChunkBuffer();
for (int i = 0; i < partitionedByteBufferLocator.getPartitionedCount(); i++) {
int currentBufferCapacity = partitionedByteBufferLocator.getByteBufferCapacity(i);
if (partitionedByteBufferLocator.isLastPartitionIndex(i)) {
if (currentBufferCapacity > spanStreamSendData.getMaxBufferCapacity()) {
throw new IllegalStateException("BufferList has over size buffer. buffer length:" + currentBufferCapacity);
}
} else {
if (currentBufferCapacity + spanChunkBuffer.remaining() > spanStreamSendData.getMaxBufferCapacity()) {
throw new IllegalStateException("BufferList has over size buffer. buffer length:" + currentBufferCapacity + ", maxCapacity:" + spanStreamSendData.getMaxBufferCapacity());
}
}
}
return plan0(spanStreamSendData);
}
}
private FlushMode plan0(SpanStreamSendData spanStreamSendData) {
int normalModeChunkSize = calculateWithUsingCurrentSendData(spanStreamSendData);
int flushFirstModeChunkSize = calculateWithoutUsingCurrentSendData(spanStreamSendData);
if (normalModeChunkSize > flushFirstModeChunkSize) {
return FlushMode.FLUSH_FIRST;
} else {
return FlushMode.NORMAL;
}
}
private int calculateWithUsingCurrentSendData(SpanStreamSendData spanStreamSendData) {
int chunkCount = 0;
int availableBufferCapacity = spanStreamSendData.getAvailableBufferCapacity();
return chunkCount + getNeedsChunkCount(spanStreamSendData, availableBufferCapacity);
}
private int calculateWithoutUsingCurrentSendData(SpanStreamSendData spanStreamSendData) {
int chunkCount = 1;
int availableBufferCapacity = spanStreamSendData.getMaxBufferCapacity();
return chunkCount + getNeedsChunkCount(spanStreamSendData, availableBufferCapacity);
}
private int getNeedsChunkCount(SpanStreamSendData spanStreamSendData, int availableCurrentBufferCapacity) {
int chunkCount = 1;
int availableBufferCapacity = availableCurrentBufferCapacity;
for (int i = 0; i < partitionedByteBufferLocator.getPartitionedCount(); i++) {
int partitionByteBufferCapacity = partitionedByteBufferLocator.getByteBufferCapacity(i);
if (partitionedByteBufferLocator.isLastPartitionIndex(i)) {
if (partitionByteBufferCapacity > availableBufferCapacity) {
chunkCount++;
}
} else {
if (partitionByteBufferCapacity + getSpanChunkLength() < availableBufferCapacity) {
availableBufferCapacity -= partitionByteBufferCapacity;
} else {
chunkCount++;
availableBufferCapacity = spanStreamSendData.getMaxBufferCapacity();
availableBufferCapacity -= partitionByteBufferCapacity;
}
}
}
return chunkCount;
}
private boolean needFlush(SpanStreamSendData spanStreamSendData, int length, int delemeterBufferSize) {
if (!spanStreamSendData.isAvailableBufferCapacity(length + delemeterBufferSize)) {
return true;
}
int availableComponentsCount = 1;
if (delemeterBufferSize > 0) {
availableComponentsCount++;
}
if (!spanStreamSendData.isAvailableComponentsCount(availableComponentsCount)) {
return true;
}
return false;
}
private ByteBuffer getByteBuffer(PartitionedByteBufferLocator partitionedByteBufferLocator, int fromPartitionIndex, int toPartitionIndex) {
if (toPartitionIndex == -1) {
return null;
}
return partitionedByteBufferLocator.getByteBuffer(fromPartitionIndex, toPartitionIndex);
}
abstract protected int getSpanChunkLength();
abstract protected ByteBuffer getSpanChunkBuffer();
private enum FlushMode {
FLUSH_FIRST, NORMAL
}
}