All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.zeebe.logstreams.log.LogStreamBatchWriterImpl Maven / Gradle / Ivy

There is a newer version: 0.16.4
Show newest version
/*
 * Copyright © 2017 camunda services GmbH ([email protected])
 *
 * 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 io.zeebe.logstreams.log;

import io.zeebe.dispatcher.ClaimedFragmentBatch;
import io.zeebe.dispatcher.Dispatcher;
import io.zeebe.logstreams.log.LogStreamBatchWriter.LogEntryBuilder;
import io.zeebe.util.buffer.BufferWriter;
import io.zeebe.util.buffer.DirectBufferWriter;
import io.zeebe.util.sched.clock.ActorClock;
import org.agrona.DirectBuffer;
import org.agrona.ExpandableArrayBuffer;
import org.agrona.LangUtil;
import org.agrona.MutableDirectBuffer;

import static io.zeebe.dispatcher.impl.log.DataFrameDescriptor.alignedFramedLength;
import static io.zeebe.dispatcher.impl.log.LogBufferAppender.RESULT_PADDING_AT_END_OF_PARTITION;
import static io.zeebe.logstreams.impl.LogEntryDescriptor.*;
import static io.zeebe.util.EnsureUtil.*;
import static org.agrona.BitUtil.SIZE_OF_INT;
import static org.agrona.BitUtil.SIZE_OF_LONG;

public class LogStreamBatchWriterImpl implements LogStreamBatchWriter, LogEntryBuilder
{
    private static final int INITIAL_BUFFER_CAPACITY = 1024 * 32;

    private static final long POSITION_AS_KEY = -1L;

    private final ClaimedFragmentBatch claimedBatch = new ClaimedFragmentBatch();

    private MutableDirectBuffer eventBuffer = new ExpandableArrayBuffer(INITIAL_BUFFER_CAPACITY);

    private final DirectBufferWriter metadataWriterInstance = new DirectBufferWriter();
    private final DirectBufferWriter bufferWriterInstance = new DirectBufferWriter();

    private int eventBufferOffset;
    private int eventLength;
    private int eventCount;

    private LogStream logStream;
    private Dispatcher logWriteBuffer;
    private int logId;

    private boolean positionAsKey;
    private long key;

    private int producerId;

    private long sourceEventPosition;

    private BufferWriter metadataWriter;
    private BufferWriter valueWriter;

    public LogStreamBatchWriterImpl()
    {
        reset();
    }

    public LogStreamBatchWriterImpl(LogStream logStream)
    {
        wrap(logStream);
    }

    @Override
    public void wrap(LogStream logStream)
    {
        this.logStream = logStream;
        this.logWriteBuffer = logStream.getWriteBuffer();
        this.logId = logStream.getPartitionId();

        reset();
    }

    @Override
    public LogStreamBatchWriter sourceRecordPosition(long position)
    {
        this.sourceEventPosition = position;
        return this;
    }

    @Override
    public LogStreamBatchWriter producerId(int producerId)
    {
        this.producerId = producerId;
        return this;
    }

    @Override
    public LogEntryBuilder event()
    {
        resetEvent();
        return this;
    }

    @Override
    public LogEntryBuilder positionAsKey()
    {
        positionAsKey = true;
        return this;
    }

    @Override
    public LogEntryBuilder key(long key)
    {
        this.key = key;
        return this;
    }

    @Override
    public LogEntryBuilder metadata(DirectBuffer buffer, int offset, int length)
    {
        metadataWriterInstance.wrap(buffer, offset, length);
        return this;
    }

    @Override
    public LogEntryBuilder metadata(DirectBuffer buffer)
    {
        return metadata(buffer, 0, buffer.capacity());
    }

    @Override
    public LogEntryBuilder metadataWriter(BufferWriter writer)
    {
        this.metadataWriter = writer;
        return this;
    }

    @Override
    public LogEntryBuilder value(DirectBuffer value, int valueOffset, int valueLength)
    {
        return valueWriter(bufferWriterInstance.wrap(value, valueOffset, valueLength));
    }

    @Override
    public LogEntryBuilder value(DirectBuffer value)
    {
        return value(value, 0, value.capacity());
    }

    @Override
    public LogEntryBuilder valueWriter(BufferWriter writer)
    {
        this.valueWriter = writer;
        return this;
    }

    @Override
    public LogStreamBatchWriter done()
    {
        // validation
        ensureNotNull("value", valueWriter);

        if (!positionAsKey)
        {
            ensureGreaterThanOrEqual("key", key, 0);
        }

        // copy event to buffer
        final int metadataLength = metadataWriter.getLength();
        final int valueLength = valueWriter.getLength();

        eventBuffer.putLong(eventBufferOffset, positionAsKey ? POSITION_AS_KEY : key);
        eventBufferOffset += SIZE_OF_LONG;

        eventBuffer.putInt(eventBufferOffset, metadataLength);
        eventBufferOffset += SIZE_OF_INT;

        eventBuffer.putInt(eventBufferOffset, valueLength);
        eventBufferOffset += SIZE_OF_INT;

        if (metadataLength > 0)
        {
            metadataWriter.write(eventBuffer, eventBufferOffset);
            eventBufferOffset += metadataLength;
        }

        valueWriter.write(eventBuffer, eventBufferOffset);
        eventBufferOffset += valueLength;

        eventLength += metadataLength + valueLength;
        eventCount += 1;

        return this;
    }

    @Override
    public long tryWrite()
    {
        ensureGreaterThan("event count", eventCount, 0);

        long result = claimBatchForEvents();
        if (result >= 0)
        {
            try
            {
                // return position of last event
                result = writeEventsToBuffer(claimedBatch.getBuffer());

                claimedBatch.commit();
            }
            catch (Exception e)
            {
                claimedBatch.abort();
                LangUtil.rethrowUnchecked(e);
            }
            finally
            {
                reset();
            }
        }
        return result;
    }

    private long claimBatchForEvents()
    {
        final int batchLength = eventLength + (eventCount * HEADER_BLOCK_LENGTH);

        long claimedPosition = -1;
        do
        {
            claimedPosition = logWriteBuffer.claim(claimedBatch, eventCount, batchLength);
        }
        while (claimedPosition == RESULT_PADDING_AT_END_OF_PARTITION);

        return claimedPosition;
    }

    private long writeEventsToBuffer(final MutableDirectBuffer writeBuffer)
    {
        long lastEventPosition = -1L;
        eventBufferOffset = 0;

        for (int i = 0; i < eventCount; i++)
        {
            final long key = eventBuffer.getLong(eventBufferOffset);
            eventBufferOffset += SIZE_OF_LONG;

            final int metadataLength = eventBuffer.getInt(eventBufferOffset);
            eventBufferOffset += SIZE_OF_INT;

            final int valueLength = eventBuffer.getInt(eventBufferOffset);
            eventBufferOffset += SIZE_OF_INT;

            final int fragmentLength = headerLength(metadataLength) + valueLength;

            // allocate fragment for log entry
            final long nextFragmentPosition = claimedBatch.nextFragment(fragmentLength, logId);
            final int bufferOffset = claimedBatch.getFragmentOffset();

            final long position = nextFragmentPosition - alignedFramedLength(fragmentLength);
            final long keyToWrite = key == POSITION_AS_KEY ? position : key;

            // write log entry header
            setPosition(writeBuffer, bufferOffset, position);
            setRaftTerm(writeBuffer, bufferOffset, logStream.getTerm());
            setProducerId(writeBuffer, bufferOffset, producerId);
            setSourceEventPosition(writeBuffer, bufferOffset, sourceEventPosition);
            setKey(writeBuffer, bufferOffset, keyToWrite);
            setTimestamp(writeBuffer, bufferOffset, ActorClock.currentTimeMillis());
            setMetadataLength(writeBuffer, bufferOffset, (short) metadataLength);

            if (metadataLength > 0)
            {
                writeBuffer.putBytes(metadataOffset(bufferOffset), eventBuffer, eventBufferOffset, metadataLength);
                eventBufferOffset += metadataLength;
            }

            // write log entry value
            writeBuffer.putBytes(valueOffset(bufferOffset, metadataLength), eventBuffer, eventBufferOffset, valueLength);
            eventBufferOffset += valueLength;

            lastEventPosition = position;
        }
        return lastEventPosition;
    }

    @Override
    public void reset()
    {
        eventBufferOffset = 0;
        eventLength = 0;
        eventCount = 0;
        sourceEventPosition = -1L;
        producerId = -1;
        resetEvent();
    }

    private void resetEvent()
    {
        positionAsKey = false;
        key = -1L;

        metadataWriter = metadataWriterInstance;
        valueWriter = null;

        bufferWriterInstance.reset();
        metadataWriterInstance.reset();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy