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

io.journalkeeper.core.state.JournalKeeperState Maven / Gradle / Ivy

There is a newer version: 0.1.11
Show newest version
/**
 * 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.
 */
/**
 * 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.journalkeeper.core.state; import io.journalkeeper.base.Replicable; import io.journalkeeper.base.ReplicableIterator; import io.journalkeeper.base.Serializer; import io.journalkeeper.core.api.JournalEntry; import io.journalkeeper.core.api.RaftJournal; import io.journalkeeper.core.api.State; import io.journalkeeper.core.api.StateFactory; import io.journalkeeper.core.api.StateResult; import io.journalkeeper.core.entry.internal.InternalEntriesSerializeSupport; import io.journalkeeper.core.entry.internal.InternalEntryType; import io.journalkeeper.core.entry.internal.ScalePartitionsEntry; import io.journalkeeper.core.entry.internal.SetPreferredLeaderEntry; import io.journalkeeper.core.exception.StateRecoverException; import io.journalkeeper.core.journal.Journal; import io.journalkeeper.core.journal.JournalSnapshot; import io.journalkeeper.persistence.MetadataPersistence; import io.journalkeeper.utils.files.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.Flushable; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URI; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.file.Files; import java.nio.file.Path; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.StampedLock; import static io.journalkeeper.core.api.RaftJournal.INTERNAL_PARTITION; import static io.journalkeeper.core.api.RaftJournal.RESERVED_PARTITIONS_START; /** * * @author LiYue * Date: 2019/11/20 */ public class JournalKeeperState implements Replicable { private static final Logger logger = LoggerFactory.getLogger(JournalKeeperState.class); private static final String USER_STATE_PATH = "user"; private static final String INTERNAL_STATE_PATH = "internal"; private static final String INTERNAL_STATE_FILE = "state"; private static final int MAX_TRUNK_SIZE = 1024 * 1024; private Path path; private State userState; private InternalState internalState; private final Map internalEntryInterceptors = new HashMap<>(); private final List reservedEntryInterceptors = new CopyOnWriteArrayList<>(); private final StateFactory userStateFactory; private final MetadataPersistence metadataPersistence; private Properties properties; /** * State文件读写锁 */ private final ReadWriteLock stateFilesLock = new ReentrantReadWriteLock(); /** * 状态读写锁 * 读取状态是加乐观锁或读锁,变更状态时加写锁。 */ private final StampedLock stateLock = new StampedLock(); public JournalKeeperState(StateFactory userStateFactory, MetadataPersistence metadataPersistence) { this.userStateFactory = userStateFactory; this.metadataPersistence = metadataPersistence; } public void init(Path path, List voters, Set partitions, URI preferredLeader) throws IOException { Files.createDirectories(path.resolve(USER_STATE_PATH)); InternalState internalState = new InternalState(new ConfigState(voters), partitions, preferredLeader); File lockFile = path.getParent().resolve(path.getFileName() + ".lock").toFile(); try (RandomAccessFile raf = new RandomAccessFile(lockFile, "rw"); FileChannel fileChannel = raf.getChannel()){ FileLock lock = fileChannel.tryLock(); if (null == lock) { throw new ConcurrentModificationException( String.format( "Some other thread is operating the state files! State: %s.", path.toString() )); } else { flushInternalState(internalStateFile(path), internalState); lock.release(); } } finally { lockFile.delete(); } } public void flush () throws IOException { try { stateFilesLock.writeLock().lock(); flushInternalState(); flushUserState(); } finally { stateFilesLock.writeLock().unlock(); } } private void flushUserState(State userState) throws IOException { if(userState instanceof Flushable) { ((Flushable) userState).flush(); } } private void flushUserState() throws IOException { flushUserState(userState); } private void flushInternalState() throws IOException { flushInternalState(internalStateFile(path), internalState); } private Path internalStateFile(Path statePath) { return statePath.resolve(INTERNAL_STATE_PATH).resolve(INTERNAL_STATE_FILE); } private void flushInternalState(Path internalStateFile, InternalState internalState ) throws IOException { metadataPersistence.save(internalStateFile, new PersistInternalState().fromInternalState(internalState)); } public void recover(Path path, Properties properties) { this.path = path; this.properties = properties; stateFilesLock.writeLock().lock(); try { Files.createDirectories(path); this.internalState = recoverInternalState(internalStateFile(path)); this.userState = userStateFactory.createState(); Path userStatePath = path.resolve(USER_STATE_PATH); Files.createDirectories(userStatePath); userState.recover(path.resolve(USER_STATE_PATH), properties); } catch (IOException e) { throw new StateRecoverException(e); } finally { stateFilesLock.writeLock().unlock(); } } private InternalState recoverInternalState(Path internalStateFile) throws IOException { return metadataPersistence.load(internalStateFile, PersistInternalState.class).toInternalState(); } public void addInterceptor(InternalEntryType type, ApplyInternalEntryInterceptor internalEntryInterceptor) { this.internalEntryInterceptors.put(type, internalEntryInterceptor); } public void removeInterceptor(InternalEntryType type) { this.internalEntryInterceptors.remove(type); } public void addInterceptor(ApplyReservedEntryInterceptor interceptor) { reservedEntryInterceptors.add(interceptor); } public void removeInterceptor(ApplyReservedEntryInterceptor interceptor) { reservedEntryInterceptors.remove(interceptor); } public Path getPath() { return path; } public long lastIncludedIndex() { return internalState.getLastIncludedIndex(); } public int lastIncludedTerm() { return internalState.getLastIncludedTerm(); } public long lastApplied() { return lastIncludedIndex() + 1; } public StateResult applyEntry(JournalEntry journalEntry, Serializer entrySerializer, RaftJournal journal) { byte [] payloadBytes = journalEntry.getPayload().getBytes(); int partition = journalEntry.getPartition(); int batchSize = journalEntry.getBatchSize(); StateResult result = new StateResult<>(null); long stamp = stateLock.writeLock(); try { if(partition< RESERVED_PARTITIONS_START) { E entry = entrySerializer.parse(payloadBytes); result = userState.execute(entry, partition, lastApplied(), batchSize, journal); } else if (partition == INTERNAL_PARTITION){ applyInternalEntry(journalEntry.getPayload().getBytes()); } else { for (ApplyReservedEntryInterceptor reservedEntryInterceptor : reservedEntryInterceptors) { reservedEntryInterceptor.applyReservedEntry(journalEntry, lastApplied()); } } internalState.setLastIncludedTerm(journalEntry.getTerm()); internalState.next(); result.setLastApplied(lastApplied()); } finally { stateLock.unlockWrite(stamp); } return result; } private void applyInternalEntry(byte [] internalEntry) { InternalEntryType type = InternalEntriesSerializeSupport.parseEntryType(internalEntry); logger.info("Apply internal entry, type: {}", type); switch (type) { case TYPE_SCALE_PARTITIONS: internalState.setPartitions(InternalEntriesSerializeSupport.parse(internalEntry, ScalePartitionsEntry.class).getPartitions()); break; case TYPE_SET_PREFERRED_LEADER: SetPreferredLeaderEntry setPreferredLeaderEntry = InternalEntriesSerializeSupport.parse(internalEntry); URI old = internalState.getPreferredLeader(); internalState.setPreferredLeader(setPreferredLeaderEntry.getPreferredLeader()); logger.info("Set preferred leader from {} to {}.", old, internalState.getPreferredLeader()); break; default: } ApplyInternalEntryInterceptor interceptor = internalEntryInterceptors.get(type); if (null != interceptor) { interceptor.applyInternalEntry(type, internalEntry); } try { flushInternalState(); } catch (IOException e) { logger.warn("Flush internal state exception! Path: {}.", path, e); } } public StateQueryResult query(Q query, RaftJournal journal) { StateQueryResult result; long stamp = stateLock.tryOptimisticRead(); result = new StateQueryResult<>(userState.query(query, journal), lastApplied()); if (!stateLock.validate(stamp)) { stamp = stateLock.readLock(); try { result = new StateQueryResult<>(userState.query(query, journal), lastApplied()); } finally { stateLock.unlockRead(stamp); } } return result; } public void dump(Path destPath) throws IOException { flush(); try { stateFilesLock.readLock().lock(); FileUtils.dump(path, destPath); } finally { stateFilesLock.readLock().unlock(); } } /** * 列出所有复制时需要拷贝的文件。 * @return 所有需要复制的文件的Path */ private List listAllFiles(Path path) throws IOException { return FileUtils.listAllFiles(path); } public List voters() { return internalState.getConfigState().voters(); } @Override public ReplicableIterator iterator() throws IOException { return new FolderTrunkIterator(path, listAllFiles(path), MAX_TRUNK_SIZE, lastIncludedIndex(), lastIncludedTerm()); } public void setConfigState(ConfigState configState) { internalState.setConfigState(configState); } public void close() { userState.close(); } public void clear() throws IOException { FileUtils.deleteFolder(path); } public ConfigState getConfigState() { return internalState.getConfigState(); } public Set getPartitions() { return internalState.getPartitions(); } public URI getPreferredLeader() { return internalState.getPreferredLeader(); } public JournalSnapshot getJournalSnapshot() {return internalState; } public void createSnapshot(Journal journal) throws IOException { internalState.setSnapshotTimestamp(System.currentTimeMillis()); internalState.setMinOffset( journal.maxIndex() == internalState.minIndex() ? journal.maxOffset(): journal.readOffset(internalState.minIndex()) ); Map partitionIndices = journal.calcPartitionIndices(internalState.minOffset()); internalState.setPartitionIndices(partitionIndices); flushInternalState(); } public long timestamp() { return internalState.getSnapshotTimestamp(); } @Override public String toString() { return "JournalKeeperState{" + "internalState=" + internalState + ", path=" + path + '}'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy