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.protobuf.util.JsonFormat;
import com.staros.exception.ExceptionCode;
import com.staros.exception.StarException;
import com.staros.journal.DummyJournalSystem;
import com.staros.journal.Journal;
import com.staros.journal.JournalSystem;
import com.staros.proto.ServiceFileMeta;
import com.staros.proto.ServiceInfo;
import com.staros.proto.ServiceState;
import com.staros.proto.ServiceTemplateInfo;
import com.staros.schedule.Scheduler;
import com.staros.shard.ShardManager;
import com.staros.util.IdGenerator;
import com.staros.util.LockCloseable;
import com.staros.util.LogUtils;
import com.staros.util.Text;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
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 Map serviceTemplates;
    private Map services;

    private ReentrantReadWriteLock lock;

    private JournalSystem journalSystem;
    private IdGenerator idGenerator;

    private Scheduler shardScheduler;

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

    public ServiceManager(JournalSystem journalSystem, IdGenerator idGenerator) {
        // TODO: load from storage

        serviceTemplates = new HashMap();
        services = new HashMap();
        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 = Journal.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 = Journal.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 = Journal.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 = Journal.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);
        }
    }

    // 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 List getShardManagers() {
        try (LockCloseable lock = new LockCloseable(readLock())) {
            List shardManagers = new ArrayList<>();
            for (Service service : services.values()) {
                shardManagers.add(service.getShardManager());
            }
            return shardManagers;
        }
    }

    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
        }
    }

    public void dumpMeta(DataOutputStream out) throws IOException {
        try (LockCloseable lk = new LockCloseable(readLock())) {
            LOG.debug("start dump service manager meta data to file.");

            ServiceFileMeta.Builder builder = ServiceFileMeta.newBuilder();

            for (ServiceTemplate template : serviceTemplates.values()) {
                builder.addServiceTemplateInfos(template.toProtobuf());
            }

            for (Service service : services.values()) {
                builder.addServiceInfos(service.toProtobuf());
            }

            ServiceFileMeta meta = builder.build();
            byte[] bytes = meta.toByteArray();
            Text.writeBytes(out, bytes);

            // dump shard manager meta in service order
            for (ServiceInfo serviceInfo : meta.getServiceInfosList()) {
                ShardManager shardManager = getShardManager(serviceInfo.getServiceId());
                assert shardManager != null;
                shardManager.dumpMeta(out);
            }

            LOG.debug("end dump service manager meta data to file.");
        }
    }

    public void loadMeta(DataInputStream in) throws IOException {
        try (LockCloseable lk = new LockCloseable(writeLock())) {
            LOG.debug("start load service manager meta data from file.");

            byte[] bytes = Text.readBytes(in);
            ServiceFileMeta meta = ServiceFileMeta.parseFrom(bytes);

            List serviceTemplateInfos = meta.getServiceTemplateInfosList();
            for (ServiceTemplateInfo info : serviceTemplateInfos) {
                ServiceTemplate template = ServiceTemplate.fromProtobuf(info);
                serviceTemplates.put(template.getServiceTemplateName(), template);
            }

            List serviceInfos = meta.getServiceInfosList();
            for (ServiceInfo info : serviceInfos) {
                Service service = Service.fromProtobuf(info);
                addService(service);
            }

            // load shard manager meta in service order
            for (ServiceInfo serviceInfo : serviceInfos) {
                ShardManager shardManager = getShardManager(serviceInfo.getServiceId());
                assert shardManager != null;
                shardManager.loadMeta(in);
            }

            LOG.debug("end load service manager meta data from file.");
        }
    }

    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);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy