org.apache.kafka.controller.ControllerPurgatory Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.kafka.controller;
import org.apache.kafka.common.utils.LogContext;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.OptionalLong;
import java.util.TreeMap;
/**
* The purgatory which holds events that have been started, but not yet completed.
* We wait for the high water mark of the metadata log to advance before completing
* them.
*/
class ControllerPurgatory {
private final Logger log;
/**
* A map from log offsets to events. Each event will be completed once the log
* advances past its offset.
*/
private final TreeMap> pending = new TreeMap<>();
public ControllerPurgatory(LogContext logContext) {
this.log = logContext.logger(ControllerPurgatory.class);
}
/**
* Complete some purgatory entries.
*
* @param offset The offset which the high water mark has advanced to.
*/
void completeUpTo(long offset) {
Iterator>> iter = pending.entrySet().iterator();
int numCompleted = 0;
while (iter.hasNext()) {
Entry> entry = iter.next();
if (entry.getKey() > offset) {
break;
}
for (DeferredEvent event : entry.getValue()) {
log.debug("completeUpTo({}): successfully completing {}", offset, event);
event.complete(null);
numCompleted++;
}
iter.remove();
}
if (log.isTraceEnabled()) {
log.trace("completeUpTo({}): successfully completed {} deferred entries",
offset, numCompleted);
}
}
/**
* Fail all the pending purgatory entries.
*
* @param exception The exception to fail the entries with.
*/
void failAll(Exception exception) {
Iterator>> iter = pending.entrySet().iterator();
while (iter.hasNext()) {
Entry> entry = iter.next();
for (DeferredEvent event : entry.getValue()) {
log.info("failAll({}): failing {}.", exception.getClass().getSimpleName(), event);
event.complete(exception);
}
iter.remove();
}
}
/**
* Add a new purgatory event.
*
* @param offset The offset to add the new event at.
* @param event The new event.
*/
void add(long offset, DeferredEvent event) {
if (!pending.isEmpty()) {
long lastKey = pending.lastKey();
if (offset < lastKey) {
throw new RuntimeException("There is already a purgatory event with " +
"offset " + lastKey + ". We should not add one with an offset of " +
offset + " which " + "is lower than that.");
}
}
List events = pending.get(offset);
if (events == null) {
events = new ArrayList<>();
pending.put(offset, events);
}
events.add(event);
if (log.isTraceEnabled()) {
log.trace("Adding deferred event {} at offset {}", event, offset);
}
}
/**
* Get the offset of the highest pending event, or empty if there are no pending
* events.
*/
OptionalLong highestPendingOffset() {
if (pending.isEmpty()) {
return OptionalLong.empty();
} else {
return OptionalLong.of(pending.lastKey());
}
}
}