io.zeebe.logstreams.impl.LogStorageAppender Maven / Gradle / Ivy
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Zeebe Community License 1.0. You may not use this file
* except in compliance with the Zeebe Community License 1.0.
*/
package io.zeebe.logstreams.impl;
import io.zeebe.dispatcher.BlockPeek;
import io.zeebe.dispatcher.Subscription;
import io.zeebe.distributedlog.impl.DistributedLogstreamPartition;
import io.zeebe.util.sched.Actor;
import io.zeebe.util.sched.future.ActorFuture;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import org.agrona.DirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.slf4j.Logger;
/** Consume the write buffer and append the blocks to the distributedlog. */
public class LogStorageAppender extends Actor {
public static final Logger LOG = Loggers.LOGSTREAMS_LOGGER;
private final AtomicBoolean isFailed = new AtomicBoolean(false);
private final BlockPeek blockPeek = new BlockPeek();
private final String name;
private final Subscription writeBufferSubscription;
private final int maxAppendBlockSize;
private final DistributedLogstreamPartition distributedLog;
private byte[] bytesToAppend;
private long commitPosition;
private final Runnable peekedBlockHandler = this::appendBlock;
public LogStorageAppender(
String name,
DistributedLogstreamPartition distributedLog,
Subscription writeBufferSubscription,
int maxBlockSize) {
this.name = name;
this.distributedLog = distributedLog;
this.writeBufferSubscription = writeBufferSubscription;
this.maxAppendBlockSize = maxBlockSize;
}
@Override
public String getName() {
return name;
}
@Override
protected void onActorStarting() {
actor.consume(writeBufferSubscription, this::peekBlock);
}
private void peekBlock() {
if (writeBufferSubscription.peekBlock(blockPeek, maxAppendBlockSize, true) > 0) {
peekedBlockHandler.run();
} else {
actor.yield();
}
}
private void appendBlock() {
final ByteBuffer rawBuffer = blockPeek.getRawBuffer();
bytesToAppend = new byte[rawBuffer.remaining()];
rawBuffer.get(bytesToAppend);
// Commit position is the position of the last event. DistributedLogstream uses this position
// to identify duplicate append requests during recovery.
commitPosition = getLastEventPosition(bytesToAppend);
actor.runUntilDone(this::tryWrite);
}
private void tryWrite() {
distributedLog.asyncAppend(bytesToAppend, commitPosition);
blockPeek.markCompleted();
actor.done();
/*// TODO: Handle error codes
if (res >= 0) {
blockPeek.markCompleted();
actor.done();
} else {
// retry
LOG.debug("Append failed, retrying");
actor.yield();
}*/
}
/* Iterate over the events in buffer and find the position of the last event */
private long getLastEventPosition(byte[] buffer) {
int bufferOffset = 0;
final DirectBuffer directBuffer = new UnsafeBuffer(0, 0);
directBuffer.wrap(buffer);
long lastEventPosition = -1;
final LoggedEventImpl nextEvent = new LoggedEventImpl();
int remaining = buffer.length - bufferOffset;
while (remaining > 0) {
nextEvent.wrap(directBuffer, bufferOffset);
bufferOffset += nextEvent.getFragmentLength();
lastEventPosition = nextEvent.getPosition();
remaining = buffer.length - bufferOffset;
}
return lastEventPosition;
}
public ActorFuture close() {
return actor.close();
}
public boolean isFailed() {
return isFailed.get();
}
public long getCurrentAppenderPosition() {
return writeBufferSubscription.getPosition();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy