qunar.tc.qmq.delay.store.log.AbstractDelaySegmentContainer Maven / Gradle / Ivy
/*
* Copyright 2018 Qunar, Inc.
*
* 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 qunar.tc.qmq.delay.store.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qunar.tc.qmq.delay.store.DelaySegmentValidator;
import qunar.tc.qmq.delay.store.appender.LogAppender;
import qunar.tc.qmq.delay.store.model.LogRecord;
import qunar.tc.qmq.delay.store.model.NopeRecordResult;
import qunar.tc.qmq.delay.store.model.RecordResult;
import qunar.tc.qmq.store.AppendMessageResult;
import qunar.tc.qmq.store.PutMessageStatus;
import java.io.File;
import java.util.concurrent.ConcurrentSkipListMap;
import static qunar.tc.qmq.delay.store.log.ScheduleOffsetResolver.resolveSegment;
/**
* @author xufeng.deng [email protected]
* @since 2018-07-19 10:36
*/
public abstract class AbstractDelaySegmentContainer implements SegmentContainer, LogRecord> {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDelaySegmentContainer.class);
File logDir;
private final LogAppender appender;
final int segmentScale;
final ConcurrentSkipListMap> segments = new ConcurrentSkipListMap<>();
AbstractDelaySegmentContainer(int scale, File logDir, DelaySegmentValidator validator, LogAppender appender) {
this.segmentScale = scale;
this.logDir = logDir;
this.appender = appender;
createAndValidateLogDir();
loadLogs(validator);
}
protected abstract void loadLogs(DelaySegmentValidator validator);
private void createAndValidateLogDir() {
if (!logDir.exists()) {
LOGGER.info("Log directory {} not found, try create it.", logDir.getAbsoluteFile());
boolean created = logDir.mkdirs();
if (!created) {
throw new RuntimeException("Failed to create log directory " + logDir.getAbsolutePath());
}
}
if (!logDir.isDirectory() || !logDir.canRead() || !logDir.canWrite()) {
throw new RuntimeException(logDir.getAbsolutePath() + " is not a readable log directory");
}
}
@Override
@SuppressWarnings("unchecked")
public RecordResult append(LogRecord record) {
long scheduleTime = record.getScheduleTime();
DelaySegment segment = locateSegment(scheduleTime);
if (null == segment) {
segment = allocNewSegment(scheduleTime);
}
if (null == segment) {
return new NopeRecordResult(PutMessageStatus.CREATE_MAPPED_FILE_FAILED);
}
return retResult(segment.append(record, appender));
}
@Override
public boolean clean(Long key) {
if (segments.isEmpty()) return false;
if (segments.lastKey() < key) return false;
DelaySegment segment = segments.remove(key);
if (null == segment) {
LOGGER.error("clean delay segment log failed,segment:{}", logDir, key);
return false;
}
if (!segment.destroy()) {
LOGGER.warn("remove delay segment failed.segment:{}", segment);
return false;
}
LOGGER.info("remove delay segment success.segment:{}", segment);
return true;
}
@Override
public void flush() {
for (DelaySegment segment : segments.values()) {
segment.flush();
}
}
protected abstract RecordResult retResult(AppendMessageResult result);
DelaySegment locateSegment(long scheduleTime) {
long baseOffset = resolveSegment(scheduleTime, segmentScale);
return segments.get(baseOffset);
}
private DelaySegment allocNewSegment(long offset) {
long baseOffset = resolveSegment(offset, segmentScale);
if (segments.containsKey(baseOffset)) {
return segments.get(baseOffset);
}
return allocSegment(baseOffset);
}
long higherBaseOffset(long low) {
Long next = segments.higherKey(low);
return next == null ? -1 : next;
}
protected abstract DelaySegment allocSegment(long segmentBaseOffset);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy