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

com.staros.filestore.FileStoreMgr Maven / Gradle / Ivy

// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// 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
//
//     https://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 com.staros.filestore;

import com.google.protobuf.ByteString;
import com.google.protobuf.util.JsonFormat;
import com.staros.credential.AwsCredential;
import com.staros.credential.AwsCredentialMgr;
import com.staros.credential.AzBlobCredential;
import com.staros.exception.AlreadyExistsStarException;
import com.staros.exception.ExceptionCode;
import com.staros.exception.InvalidArgumentStarException;
import com.staros.exception.NotExistStarException;
import com.staros.exception.StarException;
import com.staros.journal.DummyJournalSystem;
import com.staros.journal.Journal;
import com.staros.journal.JournalSystem;
import com.staros.journal.StarMgrJournal;
import com.staros.proto.AzBlobCredentialInfo;
import com.staros.proto.AzBlobFileStoreInfo;
import com.staros.proto.FileStoreInfo;
import com.staros.proto.FileStoreMgrImageMetaFooter;
import com.staros.proto.FileStoreMgrImageMetaHeader;
import com.staros.proto.FileStoreType;
import com.staros.proto.HDFSFileStoreInfo;
import com.staros.proto.S3FileStoreInfo;
import com.staros.proto.SectionType;
import com.staros.section.Section;
import com.staros.section.SectionReader;
import com.staros.section.SectionWriter;
import com.staros.util.Config;
import com.staros.util.Constant;
import com.staros.util.LockCloseable;
import com.staros.util.Utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

public class FileStoreMgr {
    private static final Logger LOG = LogManager.getLogger(FileStoreMgr.class);

    private String serviceId;

    // File store unit key to FileStoreUnit object
    private Map fileStores;

    private ReentrantReadWriteLock lock;

    private JournalSystem journalSystem;

    // FOR TEST
    public static FileStoreMgr createFileStoreMgrForTest(String serviceId) {
        return new FileStoreMgr(serviceId, new DummyJournalSystem());
    }

    public FileStoreMgr(String serviceId, JournalSystem journalSystem) {
        this.serviceId = serviceId;
        this.fileStores = new HashMap();
        this.lock = new ReentrantReadWriteLock();
        this.journalSystem = journalSystem;
        addFileStoresFromConfig();
    }

    public FileStore getFileStore(String fsKey) {
        try (LockCloseable lockCloseable = new LockCloseable(lock.readLock())) {
            FileStore fileStore = fileStores.get(fsKey);
            return fileStore;
        }
    }

    public void addFileStore(FileStore fs) throws StarException {
        addFileStore(fs, false);
    }

    private void addFileStore(FileStore fs, boolean isReplay) throws StarException {
        if (!fs.isValid()) {
            throw new InvalidArgumentStarException("Invalid file store, please check");
        }

        try (LockCloseable lockCloseable = new LockCloseable(lock.writeLock())) {
            if (fileStores.containsKey(fs.key())) {
                if (fs.key().equals(Constant.S3_FSKEY_FOR_CONFIG) ||
                        fs.key().equals(Constant.HDFS_FSKEY_FOR_CONFIG) ||
                        fs.key().equals(Constant.AZURE_BLOB_FSKEY_FOR_CONFIG)) {
                    return;
                }
                throw new AlreadyExistsStarException("File store with key {} already exist", fs.key());
            }

            if (!isReplay) {
                Journal journal = StarMgrJournal.logAddFileStore(serviceId, fs.toProtobuf());
                journalSystem.write(journal);
            }

            fileStores.put(fs.key(), fs);

            LOG.info("Add file store {}", fs);
        }
    }

    public void removeFileStore(String fsKey) throws StarException {
        removeFileStore(fsKey, false);
    }

    private void removeFileStore(String fsKey, boolean isReplay) throws StarException {
        try (LockCloseable lockCloseable = new LockCloseable(lock.writeLock())) {
            if (!fileStores.containsKey(fsKey)) {
                throw new NotExistStarException("File store with key {} not exist", fsKey);
            }

            if (!isReplay) {
                Journal journal = StarMgrJournal.logRemoveFileStore(serviceId, fsKey);
                journalSystem.write(journal);
            }

            FileStore fs = fileStores.remove(fsKey);

            LOG.info("Remove file store {}", fs);
        }
    }

    public void removeFileStoreByName(String fsName) throws StarException {
        removeFileStoreByName(fsName, false);
    }

    private void removeFileStoreByName(String fsName, boolean isReplay) throws StarException {
        try (LockCloseable lockCloseable = new LockCloseable(lock.writeLock())) {
            FileStore fs = getFileStoreByName(fsName);
            if (fs != null) {
                if (!isReplay) {
                    Journal journal = StarMgrJournal.logRemoveFileStore(serviceId, fsName);
                    journalSystem.write(journal);
                }

                fileStores.remove(fs.key());
                LOG.info("Remove file store {}", fs);
            } else {
                throw new NotExistStarException("File store with name {} not exist", fsName);
            }
        }
    }

    public void updateFileStore(FileStore fs) throws StarException {
        updateFileStore(fs, false);
    }

    private void updateFileStore(FileStore fs, boolean isReplay) throws StarException {
        try (LockCloseable lockCloseable = new LockCloseable(lock.writeLock())) {
            if (!fileStores.containsKey(fs.key())) {
                throw new NotExistStarException("File store with key {} not exist", fs.key());
            }

            if (!isReplay) {
                Journal journal = StarMgrJournal.logUpdateFileStore(serviceId, fs.toProtobuf());
                journalSystem.write(journal);
            }

            fileStores.get(fs.key()).mergeFrom(fs);

            LOG.info("Update file store {}", fs);
        }
    }

    public List listFileStore(FileStoreType fsType) throws StarException {
        try (LockCloseable lockCloseable = new LockCloseable(lock.readLock())) {
            if (fsType == Constant.FS_NOT_SET) {
                return new ArrayList(fileStores.values());
            }
            return fileStores.values().stream().filter(fileStore -> fileStore.type() == fsType)
                    .collect(Collectors.toList());
        }
    }

    // Allocate a file store to hold shard data
    public FileStore allocFileStore(String fsKey) throws StarException {
        try (LockCloseable lockCloseable = new LockCloseable(lock.readLock())) {
            if (fileStores.isEmpty()) {
                throw new NotExistStarException("No any file store exist");
            }

            if (fsKey.isEmpty()) {
                // choose the first one now, will add some policy in future
                // TODO: use the default file store first.
                Iterator enabledFileStores = fileStores.values()
                        .stream().filter(fileStore -> fileStore.getEnabled()).iterator();
                if (!enabledFileStores.hasNext()) {
                    throw new NotExistStarException("No any enabled file store exist");
                }
                return enabledFileStores.next();
            }

            if (!fileStores.containsKey(fsKey)) {
                throw new NotExistStarException("file store {} not exists", fsKey);
            }
            FileStore fs = fileStores.get(fsKey);
            if (!fs.getEnabled()) {
                throw new InvalidArgumentStarException("file store {} is disabled", fsKey);
            }
            return fs;
        }
    }

    public FileStore newFsFromProtobuf(FileStoreInfo fsInfo) {
        switch (fsInfo.getFsType()) {
            case S3:
                return newS3FsFromProtobuf(fsInfo);
            case HDFS:
                return newHDFSFsFromProtoBuf(fsInfo);
            case AZBLOB:
                return newAzBlobFromProtoBuf(fsInfo);
            default:
                return null;
        }
    }

    private FileStore newAzBlobFromProtoBuf(FileStoreInfo fsInfo) {
        AzBlobFileStoreInfo azblobInfo = fsInfo.getAzblobFsInfo();
        AzBlobCredentialInfo azblobCredentialInfo = azblobInfo.getCredential();
        AzBlobCredential azblobCredential = new AzBlobCredential(azblobCredentialInfo.getSharedKey(),
                azblobCredentialInfo.getSasToken(), azblobCredentialInfo.getTenantId(),
                azblobCredentialInfo.getClientId(), azblobCredentialInfo.getClientSecret(),
                azblobCredentialInfo.getClientCertificatePath(), azblobCredentialInfo.getAuthorityHost());
        return new AzBlobFileStore(fsInfo, azblobInfo.getEndpoint(),
                azblobInfo.getPath(), azblobCredential);
    }

    private FileStore newHDFSFsFromProtoBuf(FileStoreInfo fsInfo) {
        HDFSFileStoreInfo hdfsInfo = fsInfo.getHdfsFsInfo();
        HDFSFileStore hdfs = new HDFSFileStore(fsInfo, hdfsInfo.getUrl());
        return hdfs;
    }

    private FileStore newS3FsFromProtobuf(FileStoreInfo fsInfo) {
        S3FileStoreInfo s3fsInfo = fsInfo.getS3FsInfo();
        AwsCredential credential = AwsCredentialMgr.fromProtobuf(s3fsInfo.getCredential());
        S3FileStore s3fs = new S3FileStore(fsInfo,
                s3fsInfo.getBucket(), s3fsInfo.getRegion(), s3fsInfo.getEndpoint(), credential, s3fsInfo.getPathPrefix());
        return s3fs;
    }

    public void addFileStoresFromConfig() {
        List fileStores = new ArrayList<>();
        fileStores.add(loadS3FileStoreFromConfig());
        fileStores.add(loadHDFSFileStoreFromConfig());
        fileStores.add(loadAzBlobFileStoreFromConfig());

        for (FileStore fileStore : fileStores) {
            if (fileStore != null) {
                try {
                    addFileStore(fileStore);
                    LOG.debug("Add hdfs file store with key {} from config", fileStore.key());
                } catch (StarException e) {
                    if (e.getExceptionCode() == ExceptionCode.ALREADY_EXIST) {
                        updateFileStore(fileStore);
                    } else {
                        LOG.info("no default file store configured");
                    }
                }
            }
        }
    }

    public FileStore loadAzBlobFileStoreFromConfig() {
        if (Config.AZURE_BLOB_ENDPOINT.isEmpty()) {
            LOG.info("Empty AZURE_BLOB_ENDPOINT configured, skip load");
            return null;
        }

        if (Config.AZURE_BLOB_PATH.isEmpty()) {
            LOG.info("Empty AZURE_BLOB_PATH configured, skip load");
            return null;
        }

        AzBlobCredential credential = new AzBlobCredential(Config.AZURE_BLOB_SHARED_KEY, Config.AZURE_BLOB_SAS_TOKEN,
                Config.AZURE_BLOB_TENANT_ID, Config.AZURE_BLOB_CLIENT_ID, Config.AZURE_BLOB_CLIENT_SECRET,
                Config.AZURE_BLOB_CLIENT_CERTIFICATE_PATH, Config.AZURE_BLOB_AUTHORITY_HOST);

        AzBlobFileStore azblob = new AzBlobFileStore(Constant.AZURE_BLOB_FSKEY_FOR_CONFIG,
                Constant.AZURE_BLOB_FSNAME_FOR_CONFIG, Config.AZURE_BLOB_ENDPOINT, Config.AZURE_BLOB_PATH, credential);

        return azblob;
    }

    public FileStore loadHDFSFileStoreFromConfig() {
        HDFSFileStore hdfs = new HDFSFileStore(Constant.HDFS_FSKEY_FOR_CONFIG, Constant.HDFS_FSNAME_FOR_CONFIG,
                Config.HDFS_URL);
        return hdfs;
    }

    public FileStore loadS3FileStoreFromConfig() {
        AwsCredential credential = AwsCredentialMgr.getCredentialFromConfig();
        if (credential == null) {
            LOG.warn("get credential from config error");
            return null;
        }

        if (Config.S3_BUCKET.isEmpty()) {
            LOG.info("Empty S3_BUCKET configured, skip load");
            return null;
        }
        if (Config.S3_BUCKET.contains("/")) {
            LOG.warn("Invalid S3_BUCKET configuration:{}", Config.S3_BUCKET);
            throw new InvalidArgumentStarException("Invalid S3_BUCKET configuration:{}", Config.S3_BUCKET);
        }

        S3FileStore s3fs = new S3FileStore(Constant.S3_FSKEY_FOR_CONFIG, Constant.S3_FSNAME_FOR_CONFIG, Config.S3_BUCKET,
                Config.S3_REGION, Config.S3_ENDPOINT, credential, Config.S3_PATH_PREFIX);
        return s3fs;
    }

    public FileStore getFileStoreByName(String fsName) {
        try (LockCloseable lockCloseable = new LockCloseable(lock.readLock())) {
            for (FileStore fileStore : fileStores.values()) {
                if (fileStore.name().equals(fsName)) {
                    return fileStore;
                }
            }
        }
        return null;
    }

    public void replayAddFileStore(FileStoreInfo fsInfo) {
        addFileStore(newFsFromProtobuf(fsInfo), true);
    }

    public void replayRemoveFileStore(String fsKey) {
        removeFileStore(fsKey, true);
    }

    public void replayRemoveFileStoreByName(String fsName) {
        removeFileStoreByName(fsName, true);
    }

    public void replayUpdateFileStore(FileStoreInfo fsInfo) {
        updateFileStore(newFsFromProtobuf(fsInfo), true);
    }

    public void dumpMeta(OutputStream out) throws IOException {
        try (LockCloseable ignored = new LockCloseable(lock.readLock())) {
            LOG.debug("start dump filestore manager meta data ...");

            // write header
            FileStoreMgrImageMetaHeader header = FileStoreMgrImageMetaHeader.newBuilder()
                    .setDigestAlgorithm(Constant.DEFAULT_DIGEST_ALGORITHM)
                    .setNumFileStore(fileStores.size())
                    .build();
            header.writeDelimitedTo(out);

            DigestOutputStream mdStream = Utils.getDigestOutputStream(out, Constant.DEFAULT_DIGEST_ALGORITHM);
            try (SectionWriter writer = new SectionWriter(mdStream)) {
                try (OutputStream stream = writer.appendSection(SectionType.SECTION_FILESTOREMGR_FILESTORE)) {
                    dumpFileStores(stream);
                }
            }
            // flush the mdStream because we are going to write to `out` directly.
            mdStream.flush();

            // write MetaFooter, DO NOT directly write new data here, change the protobuf definition
            FileStoreMgrImageMetaFooter.Builder footerBuilder = FileStoreMgrImageMetaFooter.newBuilder();
            if (mdStream.getMessageDigest() != null) {
                footerBuilder.setChecksum(ByteString.copyFrom(mdStream.getMessageDigest().digest()));
            }
            footerBuilder.build().writeDelimitedTo(out);
            LOG.debug("end dump filestore manager meta data.");
        }
    }

    private void dumpFileStores(OutputStream stream) throws IOException {
        for (FileStore fileStore : fileStores.values()) {
            fileStore.toProtobuf().writeDelimitedTo(stream);
        }
    }

    public void loadMeta(InputStream in) throws IOException {
        try (LockCloseable ignored = new LockCloseable(lock.writeLock())) {
            LOG.debug("start load file store manager meta data ...");

            // read header
            FileStoreMgrImageMetaHeader header = FileStoreMgrImageMetaHeader.parseDelimitedFrom(in);
            if (header == null) {
                throw new EOFException();
            }
            DigestInputStream digestInput = Utils.getDigestInputStream(in, header.getDigestAlgorithm());

            try (SectionReader reader = new SectionReader(digestInput)) {
                reader.forEach(x -> loadFileStoreMgrSection(x, header));
            }

            // read MetaFooter
            FileStoreMgrImageMetaFooter footer = FileStoreMgrImageMetaFooter.parseDelimitedFrom(in);
            if (footer == null) {
                throw new EOFException();
            }
            Utils.validateChecksum(digestInput.getMessageDigest(), footer.getChecksum());
            LOG.debug("end load file store manager meta data.");
        }
    }

    private void loadFileStoreMgrSection(Section section, FileStoreMgrImageMetaHeader header) throws IOException {
        switch (section.getHeader().getSectionType()) {
            case SECTION_FILESTOREMGR_FILESTORE:
                loadFileStores(section.getStream(), header.getNumFileStore());
                break;
            default:
                LOG.warn("Unknown section type:{} when loadMeta in FileStoreMgr, ignore it!",
                        section.getHeader().getSectionType());
                break;
        }
    }

    private void loadFileStores(InputStream stream, int numFileStore) throws IOException {
        for (int i = 0; i < numFileStore; ++i) {
            FileStoreInfo fsInfo = FileStoreInfo.parseDelimitedFrom(stream);
            if (fsInfo == null) {
                throw new EOFException();
            }
            FileStore fs = newFsFromProtobuf(fsInfo);
            addFileStore(fs, true);
        }
    }

    public void dump(DataOutputStream out) throws IOException {
        try (LockCloseable ignored = new LockCloseable(lock.readLock())) {
            for (FileStore fileStore : fileStores.values()) {
                String s = JsonFormat.printer().print(fileStore.toProtobuf()) + "\n";
                out.writeBytes(s);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy