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

io.camunda.zeebe.journal.file.SegmentReader 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 com.google.common.base.Preconditions;
import io.camunda.zeebe.journal.JournalRecord;
import io.camunda.zeebe.journal.record.JournalRecordReaderUtil;
import io.camunda.zeebe.journal.record.SBESerializer;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.NoSuchElementException;

/** Log segment reader. */
final class SegmentReader implements Iterator {

  private final ByteBuffer buffer;
  private final JournalIndex index;
  private final Segment segment;
  private long currentIndex;
  private final JournalRecordReaderUtil recordReader;
  private final int descriptorLength;

  SegmentReader(final ByteBuffer buffer, final Segment segment, final JournalIndex index) {
    this.index = index;
    this.segment = segment;
    descriptorLength = segment.descriptor().length();
    recordReader = new JournalRecordReaderUtil(new SBESerializer());
    this.buffer = buffer;
    reset();
  }

  @Override
  public boolean hasNext() {
    if (!segment.isOpen()) {
      // When the segment has been deleted concurrently, we do not want to allow the readers to
      // continue reading from this segment.
      return false;
    }

    // if the next entry exists the version would be non-zero
    return FrameUtil.hasValidVersion(buffer);
  }

  @Override
  public JournalRecord next() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }

    // Read version so that buffer's position is advanced.
    FrameUtil.readVersion(buffer);

    final var currentEntry = recordReader.read(buffer, getNextIndex());
    // currentEntry should not be null as hasNext returns true
    currentIndex = currentEntry.index();
    return currentEntry;
  }

  void reset() {
    buffer.position(descriptorLength);
    currentIndex = segment.index() - 1;
  }

  void seek(final long index) {
    checkSegmentOpen();
    final long firstIndex = segment.index();
    final long lastIndex = segment.lastIndex();

    reset();

    final var position = this.index.lookup(index - 1);
    if (position != null && position.index() >= firstIndex && position.index() <= lastIndex) {
      buffer.position(position.position());
      currentIndex = position.index() - 1;
    }

    // If the returned index is far away from the seekIndex, it is likely that this segment was not
    // indexed before. So we try to index them during the seek.
    final boolean shouldIndex = !this.index.hasIndexed(index);

    while (getNextIndex() < index && hasNext()) {
      final var nextPosition = buffer.position();
      final var nextEntry = next();
      if (shouldIndex) {
        this.index.index(nextEntry, nextPosition);
      }
    }
  }

  void close() {
    segment.onReaderClosed(this);
  }

  long getNextIndex() {
    return currentIndex + 1;
  }

  private void checkSegmentOpen() {
    Preconditions.checkState(
        segment.isOpen(), "Segment is already closed. Reader must reset to a valid index.");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy