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

qunar.tc.qmq.delay.store.log.AbstractDelaySegmentContainer Maven / Gradle / Ivy

There is a newer version: 1.1.43
Show newest version
/*
 * 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