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

com.staros.provisioner.StarProvisionServer 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.provisioner;

import com.google.gson.Gson;
import com.staros.exception.AlreadyExistsStarException;
import com.staros.exception.NotExistStarException;
import com.staros.exception.ResourceExhaustedStarException;
import com.staros.proto.NodeInfo;
import com.staros.util.Config;
import com.staros.util.LockCloseable;
import io.grpc.BindableService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

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

    private static class Node {
        private final String host;

        public Node(String host) {
            this.host = host;
        }

        public String getHost() {
            return this.host;
        }
    }

    /**
     * Helper class to serialize/deserialize key data of StarProvisionServer
     */
    private static class Serializer {
        private final List freeNodes;
        private final Map> assignedPools;

        public Serializer() {
            this.freeNodes = new ArrayList<>();
            this.assignedPools = new HashMap<>();
        }

        public Serializer(List freeNodes, Map> assignedPools) {
            this.freeNodes = freeNodes;
            this.assignedPools = assignedPools;
        }

        public String toGson() {
            return new Gson().toJson(this);
        }

        public static Serializer fromGson(Reader reader) {
            return new Gson().fromJson(reader, Serializer.class);
        }
    }

    private List freeNodes;
    private Map> assignedPools;
    private final ReentrantLock lock;
    private final String persistFile;

    public StarProvisionServer() {
        this.freeNodes = new ArrayList<>();
        this.assignedPools = new HashMap<>();
        this.lock = new ReentrantLock();
        String dataFile = "";
        if (!Config.BUILTIN_PROVISION_SERVER_DATA_DIR.isEmpty()) {
            dataFile = String.format("%s/provisioner.dat", Config.BUILTIN_PROVISION_SERVER_DATA_DIR);
        }
        this.persistFile = dataFile;
        if (!this.persistFile.isEmpty()) {
            loadData();
        }
    }

    public static List getServices(StarProvisionServer server) {
        List services = new ArrayList<>();
        services.add(new StarProvisionService(server));
        services.add(new StarProvisionManageService(server));
        return services;
    }

    public void processAddNodeRequest(String host) {
        try (LockCloseable ignored = new LockCloseable(lock)) {
            if (freeNodes.stream().anyMatch(x -> x.getHost().equals(host))) {
                throw new AlreadyExistsStarException("Host:{} already exists!", host);
            }
            assignedPools.forEach((key, val) -> {
                if (val.stream().anyMatch(x -> x.getHost().equals(host))) {
                    throw new AlreadyExistsStarException("Host:{} already exists!", host);
                }
            });
            Node node = new Node(host);
            freeNodes.add(node);
            dumpData();
        }
    }

    public List processProvisionResourceRequest(String name, int numOfNodes) {
        try (LockCloseable ignored = new LockCloseable(lock)) {
            if (assignedPools.containsKey(name)) {
                throw new AlreadyExistsStarException("PoolName:{} already exists!", name);
            }
            if (freeNodes.size() < numOfNodes) {
                throw new ResourceExhaustedStarException("Not enough resource to meet requirement.");
            }
            List newPool = new ArrayList<>();
            for (int i = 0; i < numOfNodes; ++i) {
                newPool.add(freeNodes.get(i));
            }
            assignedPools.put(name, newPool);
            freeNodes.removeAll(newPool);
            dumpData();
            return newPool.stream().map(x -> NodeInfo.newBuilder().setHost(x.getHost()).build()).collect(Collectors.toList());
        }
    }

    public List processGetResourceRequest(String name) {
        try (LockCloseable ignored = new LockCloseable(lock)) {
            if (!assignedPools.containsKey(name)) {
                throw new NotExistStarException("PoolName:{} not exist!", name);
            }
            return assignedPools.get(name).stream().map(x -> NodeInfo.newBuilder().setHost(x.getHost()).build())
                    .collect(Collectors.toList());
        }
    }

    public List processScaleResourceRequest(String name, int numOfNodes) {
        try (LockCloseable ignored = new LockCloseable(lock)) {
            if (!assignedPools.containsKey(name)) {
                throw new NotExistStarException("PoolName:{} not exist!", name);
            }
            List pool = assignedPools.get(name);
            if (pool.size() < numOfNodes) { // scale out
                int diff = numOfNodes - pool.size();
                if (freeNodes.size() < diff) {
                    throw new ResourceExhaustedStarException("Not enough resource for scale pool:{}", name);
                }
                while (diff-- > 0) {
                    pool.add(freeNodes.remove(0));
                }
            } else if (pool.size() > numOfNodes) {
                int diff = pool.size() - numOfNodes;
                while (diff-- > 0) {
                    freeNodes.add(pool.remove(pool.size() - 1));
                }
            }
            dumpData();
            return pool.stream().map(x -> NodeInfo.newBuilder().setHost(x.getHost()).build())
                    .collect(Collectors.toList());
        }
    }

    public void processDeleteResourceRequest(String name) {
        try (LockCloseable ignored = new LockCloseable(lock)) {
            if (assignedPools.containsKey(name)) {
                freeNodes.addAll(assignedPools.get(name));
                assignedPools.remove(name);
                dumpData();
            }
        }
    }

    public void clear() {
        try (LockCloseable ignored = new LockCloseable(lock)) {
            assignedPools.clear();
            freeNodes.clear();
        }
    }

    public long getFreeNodeCount() {
        return freeNodes.size();
    }

    public long getAssignedPoolsSize() {
        return assignedPools.size();
    }

    private void dumpData() {
        if (persistFile.isEmpty()) {
            return;
        }
        try (LockCloseable ignored = new LockCloseable(lock)) {
            Writer writer = Files.newBufferedWriter(Paths.get(persistFile));
            String str = new Serializer(freeNodes, assignedPools).toGson();
            writer.write(str);
            writer.close();
        } catch (Exception exception) {
            LOG.warn("Fail to dump data to file:{}, {}", persistFile, exception.toString());
        }
    }

    private void loadData() {
        if (persistFile.isEmpty()) {
            LOG.info("persist file is empty. Skip load data");
            return;
        }
        try (LockCloseable ignored = new LockCloseable(lock)) {
            Path path = Paths.get(persistFile);
            if (!Files.exists(path)) {
                LOG.info("{} not exist, skip loading data", persistFile);
                return;
            }
            Reader reader = Files.newBufferedReader(path);
            Serializer serializer = Serializer.fromGson(reader);
            reader.close();
            // Copy freeNodes and assignedPools
            this.freeNodes.clear();
            this.assignedPools.clear();
            this.freeNodes.addAll(serializer.freeNodes);
            this.assignedPools.putAll(serializer.assignedPools);
        } catch (Exception exception) {
            LOG.warn("Excepted when load meta from persistent file:{}, {}", persistFile, exception.toString());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy