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

com.staros.service.ServiceManager Maven / Gradle / Ivy

There is a newer version: 3.4-rc2
Show newest version
// 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.service;

import com.google.common.base.Preconditions;
import com.google.protobuf.ByteString;
import com.google.protobuf.util.JsonFormat;
import com.staros.exception.ExceptionCode;
import com.staros.exception.NotExistStarException;
import com.staros.exception.StarException;
import com.staros.filestore.FileStoreMgr;
import com.staros.journal.DummyJournalSystem;
import com.staros.journal.Journal;
import com.staros.journal.JournalSystem;
import com.staros.journal.StarMgrJournal;
import com.staros.proto.SectionType;
import com.staros.proto.ServiceInfo;
import com.staros.proto.ServiceManagerFileStoreMgrHeader;
import com.staros.proto.ServiceManagerImageData;
import com.staros.proto.ServiceManagerImageMetaFooter;
import com.staros.proto.ServiceManagerImageMetaHeader;
import com.staros.proto.ServiceManagerShardManagerHeader;
import com.staros.proto.ServiceState;
import com.staros.proto.ServiceTemplateInfo;
import com.staros.schedule.Scheduler;
import com.staros.section.Section;
import com.staros.section.SectionReader;
import com.staros.section.SectionWriter;
import com.staros.shard.ShardManager;
import com.staros.util.Constant;
import com.staros.util.IdGenerator;
import com.staros.util.LockCloseable;
import com.staros.util.LogUtils;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Service Manager handles all operations that are related to Service, including
 * service registration, service bootstrap, etc.
 */
public class ServiceManager {
    private static final Logger LOG = LogManager.getLogger(ServiceManager.class);

    private final Map serviceTemplates;
    private final Map services;
    private final ReentrantReadWriteLock lock;

    private final JournalSystem journalSystem;
    private final IdGenerator idGenerator;

    private Scheduler shardScheduler;

    // FOR TEST
    public ServiceManager() {
        this(new DummyJournalSystem(), new IdGenerator(null));
    }

    public ServiceManager(JournalSystem journalSystem, IdGenerator idGenerator) {
        this.serviceTemplates = new HashMap<>();
        this.services = new HashMap<>();
        this.lock = new ReentrantReadWriteLock();

        this.journalSystem = journalSystem;
        this.idGenerator = idGenerator;
    }

    /**
     * Register a service template
     *
     * @param name
     *            service template name
     * @param serviceComponents
     *            service component that describes this service template
     *
     * @throws StarException
     *             if service template already exists
     */
    public void registerService(String name, List serviceComponents) throws StarException {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            if (serviceTemplates.containsKey(name)) {
                throw new StarException(ExceptionCode.ALREADY_EXIST,
                        String.format("service template %s already exist.", name));
            }

            ServiceTemplate serviceTemplate = new ServiceTemplate(name, serviceComponents);

            Journal journal = StarMgrJournal.logRegisterService(serviceTemplate);
            journalSystem.write(journal);

            serviceTemplates.put(name, serviceTemplate);

            LOG.info("service {} registered.", name);
        }
    }

    /**
     * Deregister a service template
     *
     * @param name
     *            service template name
     *
     * @throws StarException
     *             if service template does not exist, or
     *             this service template has running service
     */
    public void deregisterService(String name) throws StarException {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            ServiceTemplate serviceTemplate = serviceTemplates.get(name);
            if (serviceTemplate == null) {
                throw new StarException(ExceptionCode.NOT_EXIST,
                        String.format("service template %s not exist.", name));
            }

            for (Service service : services.values()) {
                if (service.getServiceTemplateName().equals(name)) {
                    throw new StarException(ExceptionCode.NOT_ALLOWED,
                            String.format("service template %s has running service.", name));
                }
            }

            Journal journal = StarMgrJournal.logDeregisterService(name);
            journalSystem.write(journal);

            serviceTemplates.remove(name);

            LOG.info("service {} deregistered.", name);
        }
    }

    /**
     * Bootstrap a service
     *
     * @param serviceTemplateName
     *            service template name
     * @param serviceName
     *            service name
     *
     * @throws StarException
     *             if service template does not exist, or
     *             this service is already bootstraped
     */
    public String bootstrapService(String serviceTemplateName, String serviceName) throws StarException {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            ServiceTemplate serviceTemplate = serviceTemplates.get(serviceTemplateName);
            if (serviceTemplate == null) {
                throw new StarException(ExceptionCode.NOT_EXIST,
                        String.format("service template %s not exist.", serviceTemplateName));
            }

            for (Service service : services.values()) {
                if (service.getServiceName().equals(serviceName)) {
                    throw new StarException(ExceptionCode.ALREADY_EXIST,
                            String.format("service %s already exist.", serviceName));
                }
            }

            String serviceId = UUID.randomUUID().toString();
            Service service = new Service(serviceTemplateName, serviceName, serviceId);

            Journal journal = StarMgrJournal.logBootstrapService(service);
            journalSystem.write(journal);

            addService(service);

            LOG.info("service {} bootstrapped.", serviceName);
            return serviceId;
        }
    }

    /**
     * Shutdown a service
     *
     * @param serviceId
     *            service id
     *
     * @throws StarException
     *             if service does not exist
     */
    public void shutdownService(String serviceId) throws StarException {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            Service service = services.get(serviceId);
            if (service == null) {
                throw new StarException(ExceptionCode.NOT_EXIST,
                        String.format("service %s not exist.", serviceId));
            }

            // TODO: deal with journal write failure, it's ok for now
            if (service.setState(ServiceState.SHUTDOWN)) {
                Journal journal = StarMgrJournal.logShutdownService(service);
                journalSystem.write(journal);
            }

            LOG.info("service {} shutdown.", serviceId);
        }
    }

    /**
     * Get a service info
     *
     * @param serviceId
     *            service id
     *
     * @throws StarException
     *             if service does not exist
     */
    public ServiceInfo getServiceInfoById(String serviceId) throws StarException {
        try (LockCloseable lock = new LockCloseable(readLock())) {
            Service service = services.get(serviceId);
            if (service == null) {
                throw new StarException(ExceptionCode.NOT_EXIST,
                        String.format("service %s not exist.", serviceId));
            }

            return service.toProtobuf();
        }
    }

    /**
     * Get a service info
     *
     * @param serviceName
     *            service name
     *
     * @throws StarException
     *             if service does not exist
     */
    public ServiceInfo getServiceInfoByName(String serviceName) throws StarException {
        try (LockCloseable lock = new LockCloseable(readLock())) {
            for (Service service : services.values()) {
                if (service.getServiceName().equals(serviceName)) {
                    return service.toProtobuf();
                }
            }

            throw new StarException(ExceptionCode.NOT_EXIST,
                    String.format("service %s not exist.", serviceName));
        }
    }

    public int getServiceTemplateCount() {
        try (LockCloseable lock = new LockCloseable(readLock())) {
            return serviceTemplates.size();
        }
    }

    public int getServiceCount() {
        try (LockCloseable lock = new LockCloseable(readLock())) {
            return services.size();
        }
    }

    // lock must be held
    public boolean existService(String serviceId) {
        return services.get(serviceId) != null;
    }

    public Lock readLock() {
        return lock.readLock();
    }

    public Lock writeLock() {
        return lock.writeLock();
    }

    public void setShardScheduler(Scheduler shardScheduler) {
        this.shardScheduler = shardScheduler;
    }

    // add service to map and allocate a new shard manager if service not exist already
    private void addService(Service service) {
        assert lock.isWriteLocked() == true;
        String serviceId = service.getServiceId();
        if (services.put(serviceId, service) == null) {
            ShardManager shardManager = new ShardManager(serviceId, journalSystem, idGenerator, shardScheduler);
            service.setShardManager(shardManager);
            FileStoreMgr fileStoreMgr = new FileStoreMgr(serviceId, journalSystem);
            service.setFileStoreMgr(fileStoreMgr);
        }
    }

    // TODO: check it when support delete service
    public ShardManager getShardManager(String serviceId) {
        // NOTE: the read lock here is not necessary, because upper level
        //       has already acquired this, but just make things consistent,
        //       for example, unit test
        try (LockCloseable lock = new LockCloseable(readLock())) {
            Service service = services.get(serviceId);
            if (service == null) {
                return null;
            }
            return service.getShardManager();
        }
    }

    public void replayRegisterService(ServiceTemplate serviceTemplate) {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            if (serviceTemplates.containsKey(serviceTemplate.getServiceTemplateName())) {
                LogUtils.fatal(LOG, "service template {} already exist when replay register service, should not happen!",
                        serviceTemplate.getServiceTemplateName());
            }

            serviceTemplates.put(serviceTemplate.getServiceTemplateName(), serviceTemplate);

            // TODO: state machine
        }
    }

    public void replayDeregisterService(String name) {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            if (!serviceTemplates.containsKey(name)) {
                LOG.warn("service template {} not exist when replay deregister service, just ignore.", name);
                return;
            }

            for (Service service : services.values()) {
                if (service.getServiceTemplateName().equals(name)) {
                    LogUtils.fatal(LOG,
                            "service template {} has running service when replay deregister service, should not happen!",
                            name);
                }
            }

            serviceTemplates.remove(name);

            // TODO: state machine
        }
    }

    public void replayBootstrapService(Service service) {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            if (!serviceTemplates.containsKey(service.getServiceTemplateName())) {
                LogUtils.fatal(LOG, "service template {} not exist when replay bootstrap service, should not happen!",
                        service.getServiceTemplateName());
            }

            if (services.containsKey(service.getServiceId())) {
                LogUtils.fatal(LOG, "service {} already exist when replay bootstrap service, should not happen!",
                        service.getServiceId());
            }

            addService(service);

            // TODO: state machine
        }
    }

    public void replayShutdownService(Service service) {
        try (LockCloseable lock = new LockCloseable(writeLock())) {
            if (!serviceTemplates.containsKey(service.getServiceTemplateName())) {
                LogUtils.fatal(LOG, "service template {} not exist when replay shutdown service, should not happen!",
                        service.getServiceTemplateName());
            }

            if (!services.containsKey(service.getServiceId())) {
                LogUtils.fatal(LOG, "service {} not exist when replay shutdown service, should not happen!",
                        service.getServiceId());
            }

            addService(service);

            // TODO: state machine
        }
    }

    /**
     * Dump ServiceManager info into checkpoint image
     * @param out Output Stream
     * @throws IOException I/O exception
     * 
     *  +----------------------+
     *  |  SERVICE_MGR_HEADER  | (number of service template, number of services)
     *  +----------------------+
     *  | SERVICE_MGR_SVC_TMPLS|
     *  +--------------------- +
     *  |       Templates      |
     *  +----------------------+
     *  |   SERVICE_MGR_SVCS   |
     *  +----------------------+
     *  |        Services      |
     *  +----------------------+
     *  | SERVICE_MGR_SHARD_MGR|
     *  +----------------------+
     *  |     SERVICE_ID_1     |
     *  |    SHARDMANAGER_1    |
     *  |     SERVICE_ID_2     |
     *  |    SHARDMANAGER_2    |
     *  |        ...           |
     *  |     SERVICE_ID_N     |
     *  |    SHARDMANAGER_N    |
     *  +----------------------+
     * 
*/ public void dumpMeta(OutputStream out) throws IOException { try (LockCloseable lk = new LockCloseable(readLock())) { LOG.debug("start dump service manager meta data ..."); // write header ServiceManagerImageMetaHeader header = ServiceManagerImageMetaHeader.newBuilder() .setDigestAlgorithm(Constant.DEFAULT_DIGEST_ALGORITHM) .setNumServiceTemplate(serviceTemplates.size()) .setNumService(services.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_SERVICEMGR_TEMPLATE)) { dumpServiceTemplates(stream); } try (OutputStream stream = writer.appendSection(SectionType.SECTION_SERVICEMGR_SERVICE)) { dumpServices(stream); } for (String serviceId : services.keySet()) { // each shardManager takes a separate section try (OutputStream stream = writer.appendSection(SectionType.SECTION_SERVICEMGR_SHARDMGR)) { dumpShardManager(stream, serviceId); } // each shardManager takes a separate section try (OutputStream stream = writer.appendSection(SectionType.SECTION_SERVICEMGR_FILESTOREMGR)) { dumpFileStoreMgr(stream, serviceId); } } } // 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 ServiceManagerImageMetaFooter.Builder footerBuilder = ServiceManagerImageMetaFooter.newBuilder(); if (mdStream.getMessageDigest() != null) { footerBuilder.setChecksum(ByteString.copyFrom(mdStream.getMessageDigest().digest())); } footerBuilder.build().writeDelimitedTo(out); LOG.debug("end dump service manager meta data."); } } public void loadMeta(InputStream in) throws IOException { try (LockCloseable lk = new LockCloseable(writeLock())) { LOG.debug("start load service manager meta data ..."); ServiceManagerImageMetaHeader header = ServiceManagerImageMetaHeader.parseDelimitedFrom(in); if (header == null) { throw new EOFException(); } DigestInputStream mdStream = Utils.getDigestInputStream(in, header.getDigestAlgorithm()); try (SectionReader reader = new SectionReader(mdStream)) { reader.forEach(x -> loadMetaFromSection(x, header)); } LOG.debug("end load service manager meta data."); } } private void loadMetaFromSection(Section section, ServiceManagerImageMetaHeader header) throws IOException { switch (section.getHeader().getSectionType()) { case SECTION_SERVICEMGR_TEMPLATE: loadServiceTemplates(section.getStream(), header.getNumServiceTemplate()); break; case SECTION_SERVICEMGR_SERVICE: loadServices(section.getStream(), header.getNumService()); break; case SECTION_SERVICEMGR_SHARDMGR: loadShardManager(section.getStream()); break; case SECTION_SERVICEMGR_FILESTOREMGR: loadFileStoreMgr(section.getStream()); default: LOG.warn("Unknown section type:{} when loadMeta in ServiceManager, ignore it!", section.getHeader().getSectionType()); break; } } private void dumpServiceTemplates(OutputStream stream) throws IOException { ServiceManagerImageData.Builder builder = ServiceManagerImageData.newBuilder(); serviceTemplates.values().forEach(x -> builder.addData(x.toProtobuf().toByteString())); builder.build().writeDelimitedTo(stream); } private void loadServiceTemplates(InputStream stream, int numTemplates) throws IOException { ServiceManagerImageData templates = ServiceManagerImageData.parseDelimitedFrom(stream); if (templates == null) { throw new EOFException(); } Preconditions.checkState(numTemplates == templates.getDataCount()); for (ByteString bs : templates.getDataList()) { ServiceTemplate tmpl = ServiceTemplate.fromProtobuf(ServiceTemplateInfo.parseFrom(bs)); serviceTemplates.put(tmpl.getServiceTemplateName(), tmpl); } } private void dumpServices(OutputStream stream) throws IOException { ServiceManagerImageData.Builder builder = ServiceManagerImageData.newBuilder(); services.values().forEach(x -> builder.addData(x.toProtobuf().toByteString())); builder.build().writeDelimitedTo(stream); } private void loadServices(InputStream stream, int numServices) throws IOException { ServiceManagerImageData svcList = ServiceManagerImageData.parseDelimitedFrom(stream); if (svcList == null) { throw new EOFException(); } Preconditions.checkState(numServices == svcList.getDataCount()); for (ByteString bs : svcList.getDataList()) { Service svc = Service.fromProtobuf(ServiceInfo.parseFrom(bs)); addService(svc); } } private void dumpShardManager(OutputStream stream, String serviceId) throws IOException { // write service id ServiceManagerShardManagerHeader.newBuilder().setServiceId(serviceId).build().writeDelimitedTo(stream); // write shardManager ShardManager manager = getShardManager(serviceId); manager.dumpMeta(stream); } private void loadShardManager(InputStream stream) throws IOException { ServiceManagerShardManagerHeader header = ServiceManagerShardManagerHeader.parseDelimitedFrom(stream); if (header == null) { throw new EOFException(); } ShardManager manager = getShardManager(header.getServiceId()); manager.loadMeta(stream); } public Set getServiceIdSet() { try (LockCloseable lk = new LockCloseable(readLock())) { return new HashSet<>(services.keySet()); } } public void dump(DataOutputStream out) throws IOException { try (LockCloseable lk = new LockCloseable(readLock())) { for (ServiceTemplate template : serviceTemplates.values()) { String s = JsonFormat.printer().print(template.toProtobuf()) + "\n"; out.writeBytes(s); } for (Service service : services.values()) { String s = JsonFormat.printer().print(service.toProtobuf()) + "\n"; out.writeBytes(s); service.getShardManager().dump(out); service.getFileStoreMgr().dump(out); } } } public FileStoreMgr getFileStoreMgr(String serviceId) throws StarException { try (LockCloseable lock = new LockCloseable(readLock())) { if (!services.containsKey(serviceId)) { throw new NotExistStarException(String.format("service %s not exist.", serviceId)); } Service service = services.get(serviceId); return service.getFileStoreMgr(); } } private void dumpFileStoreMgr(OutputStream stream, String serviceId) throws IOException { // write service id ServiceManagerFileStoreMgrHeader.newBuilder().setServiceId(serviceId).build().writeDelimitedTo(stream); // write fileStoreMgr FileStoreMgr fsMgr = getFileStoreMgr(serviceId); fsMgr.dumpMeta(stream); } private void loadFileStoreMgr(InputStream stream) throws IOException { ServiceManagerFileStoreMgrHeader header = ServiceManagerFileStoreMgrHeader.parseDelimitedFrom(stream); if (header == null) { throw new EOFException(); } FileStoreMgr fsMgr = getFileStoreMgr(header.getServiceId()); fsMgr.loadMeta(stream); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy