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

io.camunda.zeebe.journal.file.SegmentDescriptor Maven / Gradle / Ivy

/*
 * Copyright 2017-present Open Networking Foundation
 * Copyright © 2020 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.camunda.zeebe.journal.file;

import static com.google.common.base.Preconditions.checkArgument;

import io.camunda.zeebe.journal.util.ChecksumGenerator;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.agrona.MutableDirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The segment descriptor stores the metadata of a single segment {@link Segment} of a {@link
 * SegmentedJournal}. The descriptor is stored in the first bytes of the segment. The number of
 * bytes requires for the descriptor is dependent on the encoding used. The first byte of the
 * segment contains the version of the descriptor. The subsequent bytes contains the following
 * fields encoded using the SBE schema.
 *
 * 

{@code id} (64-bit signed integer) - A unique segment identifier. This is a monotonically * increasing number within each journal. Segments with in-sequence identifiers should contain * in-sequence indices. * *

<{@code index} (64-bit signed integer) - The effective first index of the segment. This * indicates the index at which the first entry should be written to the segment. Indices are * monotonically increasing thereafter. * *

{@code maxSegmentSize} (32-bit unsigned integer) - The maximum number of bytes allowed in the * segment. */ final class SegmentDescriptor { // current descriptor version containing: header, metadata, header and descriptor. descriptor // contains lastIndex and lastPosition. Version 2 with sbeSchemaVersion 1 does not contain // lastIndex and lastPosition. static final byte CUR_VERSION = 2; // First version containing: header and descriptor. We remove support for VERSION 1 as this was // introduced long ago. static final byte META_VERSION = 2; static final int VERSION_LENGTH = Byte.BYTES; private static final Logger LOG = LoggerFactory.getLogger(SegmentDescriptor.class); private final DescriptorMetadataEncoder metadataEncoder = new DescriptorMetadataEncoder(); private final SegmentDescriptorEncoder segmentDescriptorEncoder = new SegmentDescriptorEncoder(); private final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder(); private final ChecksumGenerator checksumGen = new ChecksumGenerator(); // version in the header. Increment this version if there is non-backward compatible changes in // the serialization format. private final byte version; // version of sbe schema. The version will be incremented if fields are added or removed from the // sbe schema of descriptor. As long as these changes are backward compatible, there is no need to // increment `CUR_VERSION` private final int actingSchemaVersion; private final long id; private final long index; private final int maxSegmentSize; private final int encodedLength; // index of the last entry in this segment. Can be 0 if not set, even if an entry exists. private long lastIndex; // position of the last entry in this segment. Can be 0 if not set, even if an entry exists. private int lastPosition; SegmentDescriptor( final byte version, final int actingSchemaVersion, final long id, final long index, final int maxSegmentSize, final long lastIndex, final int lastPosition, final int encodedLength) { this.version = version; this.actingSchemaVersion = actingSchemaVersion; this.id = id; this.index = index; this.maxSegmentSize = maxSegmentSize; this.lastIndex = lastIndex; this.lastPosition = lastPosition; this.encodedLength = encodedLength; } /** * The number of bytes taken by the descriptor in the segment is dependent on the encoding used. * The length represents this number of bytes. * * @return the number of bytes taken by this descriptor in the segment. */ int length() { return encodedLength; } /** * The number of bytes required to write a descriptor to the segment. * * @return the encoding length */ static int getEncodingLength() { return VERSION_LENGTH + MessageHeaderEncoder.ENCODED_LENGTH * 2 + DescriptorMetadataEncoder.BLOCK_LENGTH + SegmentDescriptorEncoder.BLOCK_LENGTH; } /** * Returns a descriptor builder. * * @return The descriptor builder. */ static Builder builder() { return new Builder(); } /** * Returns the segment identifier. * *

The segment ID is a monotonically increasing number within each log. Segments with * in-sequence identifiers should contain in-sequence indexes. * * @return The segment identifier. */ long id() { return id; } /** * Returns the segment index. * *

The index indicates the index at which the first entry should be written to the segment. * Indexes are monotonically increasing thereafter. * * @return The segment index. */ long index() { return index; } /** * Returns the maximum allowed number of bytes in the segment. * * @return The maximum allowed number of bytes in the segment. */ int maxSegmentSize() { return maxSegmentSize; } /** * Copies the descriptor to a new buffer. The number of bytes written will be equal to {@link * SegmentDescriptor#getEncodingLength()} */ SegmentDescriptor copyTo(final ByteBuffer buffer) { final MutableDirectBuffer directBuffer = new UnsafeBuffer(); directBuffer.wrap(buffer); directBuffer.putByte(0, CUR_VERSION); // descriptor header final int descHeaderOffset = VERSION_LENGTH + MessageHeaderEncoder.ENCODED_LENGTH + DescriptorMetadataEncoder.BLOCK_LENGTH; segmentDescriptorEncoder .wrapAndApplyHeader(directBuffer, descHeaderOffset, headerEncoder) .id(id) .index(index) .maxSegmentSize(maxSegmentSize) .lastIndex(lastIndex) .lastPosition(lastPosition); final long checksum = checksumGen.compute( buffer, descHeaderOffset, headerEncoder.encodedLength() + segmentDescriptorEncoder.encodedLength()); metadataEncoder .wrapAndApplyHeader(directBuffer, VERSION_LENGTH, headerEncoder) .checksum(checksum); return this; } @Override public int hashCode() { return Objects.hash(id, index, maxSegmentSize); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final SegmentDescriptor that = (SegmentDescriptor) o; return id == that.id && index == that.index && maxSegmentSize == that.maxSegmentSize; } @Override public String toString() { return "SegmentDescriptor{" + "id=" + id + ", index=" + index + ", maxSegmentSize=" + maxSegmentSize + ", lastIndex=" + lastIndex + ", lastPosition=" + lastPosition + '}'; } int lastPosition() { return lastPosition; } void setLastPosition(final int lastPosition) { this.lastPosition = lastPosition; } void setLastIndex(final long lastIndex) { this.lastIndex = lastIndex; } void updateIfCurrentVersion(final ByteBuffer buffer) { if (version >= CUR_VERSION && actingSchemaVersion == segmentDescriptorEncoder.sbeSchemaVersion()) { copyTo(buffer); } else { // Do not overwrite the descriptor for older versions. The new version has a higher length and // will overwrite the first entry. LOG.trace( "Segment descriptor version is {}, and sbe schema version is {}, which is different from current version {}, and current sbe schema version {}." + "Skipping update to the descriptor.", version, actingSchemaVersion, CUR_VERSION, segmentDescriptorEncoder.sbeSchemaVersion()); } } long lastIndex() { return lastIndex; } /** Segment descriptor builder. */ static final class Builder { private long id; private long index; private int maxSegmentSize; /** * Sets the segment identifier. * * @param id The segment identifier. * @return The segment descriptor builder. */ Builder withId(final long id) { checkArgument(id > 0, "id must be positive"); this.id = id; return this; } /** * Sets the segment index. * * @param index The segment starting index. * @return The segment descriptor builder. */ Builder withIndex(final long index) { checkArgument(index > 0, "index must be positive"); this.index = index; return this; } /** * Sets maximum number of bytes of the segment. * * @param maxSegmentSize The maximum count of the segment. * @return The segment descriptor builder. */ Builder withMaxSegmentSize(final int maxSegmentSize) { checkArgument(maxSegmentSize > 0, "maxSegmentSize must be positive"); this.maxSegmentSize = maxSegmentSize; return this; } /** * Builds the segment descriptor. * * @return The built segment descriptor. */ SegmentDescriptor build() { return new SegmentDescriptor( CUR_VERSION, SegmentDescriptorEncoder.SCHEMA_VERSION, id, index, maxSegmentSize, 0, 0, getEncodingLength()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy