com.staros.service.ServiceManager 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.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