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

io.zeebe.logstreams.impl.service.LogStreamService 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.impl.service;

import static io.zeebe.logstreams.impl.service.LogStreamServiceNames.logStorageAppenderRootService;
import static io.zeebe.logstreams.impl.service.LogStreamServiceNames.logStorageAppenderServiceName;
import static io.zeebe.logstreams.impl.service.LogStreamServiceNames.logStreamRootServiceName;
import static io.zeebe.logstreams.impl.service.LogStreamServiceNames.logWriteBufferServiceName;
import static io.zeebe.logstreams.impl.service.LogStreamServiceNames.logWriteBufferSubscriptionServiceName;
import static io.zeebe.logstreams.log.LogStreamUtil.INVALID_ADDRESS;
import static io.zeebe.logstreams.log.LogStreamUtil.getAddressForPosition;

import io.zeebe.dispatcher.Dispatcher;
import io.zeebe.dispatcher.DispatcherBuilder;
import io.zeebe.dispatcher.Dispatchers;
import io.zeebe.dispatcher.Subscription;
import io.zeebe.logstreams.impl.LogBlockIndexWriter;
import io.zeebe.logstreams.impl.LogStorageAppender;
import io.zeebe.logstreams.impl.LogStreamBuilder;
import io.zeebe.logstreams.impl.log.index.LogBlockIndex;
import io.zeebe.logstreams.log.LogStream;
import io.zeebe.logstreams.spi.LogStorage;
import io.zeebe.servicecontainer.CompositeServiceBuilder;
import io.zeebe.servicecontainer.Injector;
import io.zeebe.servicecontainer.Service;
import io.zeebe.servicecontainer.ServiceContainer;
import io.zeebe.servicecontainer.ServiceName;
import io.zeebe.servicecontainer.ServiceStartContext;
import io.zeebe.servicecontainer.ServiceStopContext;
import io.zeebe.util.ByteValue;
import io.zeebe.util.sched.ActorCondition;
import io.zeebe.util.sched.channel.ActorConditions;
import io.zeebe.util.sched.future.ActorFuture;
import org.agrona.concurrent.status.Position;

public class LogStreamService implements LogStream, Service {
  private static final String APPENDER_SUBSCRIPTION_NAME = "appender";

  private final Injector logStorageInjector = new Injector<>();
  private final Injector logBlockIndexInjector = new Injector<>();
  private final Injector logBockIndexWriterInjector = new Injector<>();

  private final ServiceContainer serviceContainer;

  private final ActorConditions onLogStorageAppendedConditions = new ActorConditions();
  private final ActorConditions onCommitPositionUpdatedConditions;

  private final String logName;
  private final int partitionId;

  private final ByteValue writeBufferSize;
  private final int maxAppendBlockSize;

  private final Position commitPosition;
  private volatile int term = 0;

  private ServiceStartContext serviceContext;

  private LogStorage logStorage;
  private LogBlockIndex logBlockIndex;
  private LogBlockIndexWriter logBlockIndexWriter;

  private ActorFuture writeBufferFuture;
  private ActorFuture appenderFuture;
  private Dispatcher writeBuffer;
  private LogStorageAppender appender;

  public LogStreamService(final LogStreamBuilder builder) {
    this.logName = builder.getLogName();
    this.partitionId = builder.getPartitionId();
    this.serviceContainer = builder.getServiceContainer();
    this.onCommitPositionUpdatedConditions = builder.getOnCommitPositionUpdatedConditions();
    this.commitPosition = builder.getCommitPosition();
    this.writeBufferSize = ByteValue.ofBytes(builder.getWriteBufferSize());
    this.maxAppendBlockSize = builder.getMaxAppendBlockSize();
  }

  @Override
  public void start(final ServiceStartContext startContext) {
    commitPosition.setVolatile(INVALID_ADDRESS);

    serviceContext = startContext;
    logStorage = logStorageInjector.getValue();
    logBlockIndex = logBlockIndexInjector.getValue();
    logBlockIndexWriter = logBockIndexWriterInjector.getValue();
  }

  @Override
  public ActorFuture openAppender() {
    final ServiceName logStorageAppenderRootService = logStorageAppenderRootService(logName);
    final ServiceName logWriteBufferServiceName = logWriteBufferServiceName(logName);
    final ServiceName appenderSubscriptionServiceName =
        logWriteBufferSubscriptionServiceName(logName, APPENDER_SUBSCRIPTION_NAME);
    final ServiceName logStorageAppenderServiceName =
        logStorageAppenderServiceName(logName);

    final DispatcherBuilder writeBufferBuilder =
        Dispatchers.create(logWriteBufferServiceName.getName()).bufferSize(writeBufferSize);

    final CompositeServiceBuilder installOperation =
        serviceContext.createComposite(logStorageAppenderRootService);

    final LogWriteBufferService writeBufferService = new LogWriteBufferService(writeBufferBuilder);
    writeBufferFuture =
        installOperation
            .createService(logWriteBufferServiceName, writeBufferService)
            .dependency(
                logStorageInjector.getInjectedServiceName(),
                writeBufferService.getLogStorageInjector())
            .dependency(
                logBlockIndexInjector.getInjectedServiceName(),
                writeBufferService.getLogBlockIndexInjector())
            .install();

    final LogWriteBufferSubscriptionService subscriptionService =
        new LogWriteBufferSubscriptionService(APPENDER_SUBSCRIPTION_NAME);
    installOperation
        .createService(appenderSubscriptionServiceName, subscriptionService)
        .dependency(logWriteBufferServiceName, subscriptionService.getWritebufferInjector())
        .install();

    final LogStorageAppenderService appenderService =
        new LogStorageAppenderService(onLogStorageAppendedConditions, maxAppendBlockSize);
    appenderFuture =
        installOperation
            .createService(logStorageAppenderServiceName, appenderService)
            .dependency(
                appenderSubscriptionServiceName, appenderService.getAppenderSubscriptionInjector())
            .dependency(
                logStorageInjector.getInjectedServiceName(),
                appenderService.getLogStorageInjector())
            .install();

    return installOperation.installAndReturn(logStorageAppenderServiceName);
  }

  @Override
  public ActorFuture closeAppender() {
    appenderFuture = null;
    writeBufferFuture = null;
    appender = null;
    writeBuffer = null;

    return serviceContext.removeService(logStorageAppenderRootService(logName));
  }

  @Override
  public void stop(final ServiceStopContext stopContext) {
    // nothing to do
  }

  @Override
  public LogStream get() {
    return this;
  }

  @Override
  public int getPartitionId() {
    return partitionId;
  }

  @Override
  public String getLogName() {
    return logName;
  }

  @Override
  public void close() {
    closeAsync().join();
  }

  @Override
  public ActorFuture closeAsync() {
    return serviceContainer.removeService(logStreamRootServiceName(logName));
  }

  @Override
  public LogStorage getLogStorage() {
    return logStorage;
  }

  @Override
  public LogBlockIndex getLogBlockIndex() {
    return logBlockIndex;
  }

  @Override
  public LogBlockIndexWriter getLogBlockIndexWriter() {
    return logBlockIndexWriter;
  }

  @Override
  public Dispatcher getWriteBuffer() {
    if (writeBuffer == null && writeBufferFuture != null) {
      writeBuffer = writeBufferFuture.join();
    }
    return writeBuffer;
  }

  @Override
  public LogStorageAppender getLogStorageAppender() {
    if (appender == null && appenderFuture != null) {
      appender = appenderFuture.join();
    }
    return appender;
  }

  @Override
  public long getCommitPosition() {
    return commitPosition.get();
  }

  @Override
  public void truncate(final long position) {
    if (position <= getCommitPosition()) {
      throw new IllegalArgumentException("Can't truncate position which is already committed");
    }

    final long truncateAddress = getAddressForPosition(this, position);
    if (truncateAddress != INVALID_ADDRESS) {
      logStorage.truncate(truncateAddress);
    } else {
      throw new IllegalArgumentException(
          String.format("Truncation failed! Position %d was not found.", position));
    }
  }

  @Override
  public void setCommitPosition(final long commitPosition) {
    this.commitPosition.setOrdered(commitPosition);

    onCommitPositionUpdatedConditions.signalConsumers();
  }

  @Override
  public void registerOnCommitPositionUpdatedCondition(final ActorCondition condition) {
    onCommitPositionUpdatedConditions.registerConsumer(condition);
  }

  @Override
  public void removeOnCommitPositionUpdatedCondition(final ActorCondition condition) {
    onCommitPositionUpdatedConditions.removeConsumer(condition);
  }

  @Override
  public void registerOnAppendCondition(final ActorCondition condition) {
    onLogStorageAppendedConditions.registerConsumer(condition);
  }

  @Override
  public void removeOnAppendCondition(final ActorCondition condition) {
    onLogStorageAppendedConditions.removeConsumer(condition);
  }

  @Override
  public int getTerm() {
    return term;
  }

  @Override
  public void setTerm(final int term) {
    this.term = term;
  }

  public Injector getLogBlockIndexInjector() {
    return logBlockIndexInjector;
  }

  public Injector getLogBockIndexWriterInjector() {
    return logBockIndexWriterInjector;
  }

  public Injector getLogStorageInjector() {
    return logStorageInjector;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy