You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ti...@apache.org on 2020/09/30 06:09:01 UTC

[servicecomb-service-center] branch master updated: [Enhancement] Implement service/ms exist interface (#701)

This is an automated email from the ASF dual-hosted git repository.

tianxiaoliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new e3a9443  [Enhancement] Implement service/ms exist interface (#701)
e3a9443 is described below

commit e3a94432616248cc76a15d0e041d4b2af1342b34
Author: popozy <18...@163.com>
AuthorDate: Wed Sep 30 14:08:52 2020 +0800

    [Enhancement] Implement service/ms exist interface (#701)
    
    1. implement service/ms/etcd  interface
      + ExistService
      + ModifySchema
      + ModifySchemas
      + ExistSchema
    2. finish unit test
---
 server/service/ms/datasource.go     |   6 +-
 server/service/ms/etcd/etcd.go      | 386 +++++++++++++-
 server/service/ms/etcd/etcd_test.go | 989 +++++++++++++++++++++++++++++++++++-
 server/service/ms/etcd/util.go      | 306 ++++++++++-
 4 files changed, 1670 insertions(+), 17 deletions(-)

diff --git a/server/service/ms/datasource.go b/server/service/ms/datasource.go
index 61e81bb..62ff6d2 100644
--- a/server/service/ms/datasource.go
+++ b/server/service/ms/datasource.go
@@ -25,6 +25,7 @@ type DataSource interface {
 	RegisterService(ctx context.Context, request *pb.CreateServiceRequest) (*pb.CreateServiceResponse, error)
 	GetServices(ctx context.Context, request *pb.GetServicesRequest) (*pb.GetServicesResponse, error)
 	GetService(ctx context.Context, request *pb.GetServiceRequest) (*pb.GetServiceResponse, error)
+	ExistService(ctx context.Context, request *pb.GetExistenceRequest) (*pb.GetExistenceResponse, error)
 	UpdateService(ctx context.Context, request *pb.UpdateServicePropsRequest) (*pb.UpdateServicePropsResponse, error)
 	UnregisterService(ctx context.Context, request *pb.DeleteServiceRequest) (*pb.DeleteServiceResponse, error)
 
@@ -33,9 +34,10 @@ type DataSource interface {
 	UpdateInstance()
 	UnRegisterInstance()
 
-	AddSchema()
+	ModifySchemas(ctx context.Context, request *pb.ModifySchemasRequest) (*pb.ModifySchemasResponse, error)
+	ModifySchema(ctx context.Context, request *pb.ModifySchemaRequest) (*pb.ModifySchemaResponse, error)
+	ExistSchema(ctx context.Context, request *pb.GetExistenceRequest) (*pb.GetExistenceResponse, error)
 	GetSchema()
-	UpdateSchema()
 	DeleteSchema()
 
 	AddTag()
diff --git a/server/service/ms/etcd/etcd.go b/server/service/ms/etcd/etcd.go
index 4a77b8c..49e3b3a 100644
--- a/server/service/ms/etcd/etcd.go
+++ b/server/service/ms/etcd/etcd.go
@@ -17,22 +17,36 @@ package etcd
 
 import (
 	"context"
+	"fmt"
 	"github.com/apache/servicecomb-service-center/pkg/log"
 	pb "github.com/apache/servicecomb-service-center/pkg/registry"
 	"github.com/apache/servicecomb-service-center/pkg/util"
+	apt "github.com/apache/servicecomb-service-center/server/core"
 	"github.com/apache/servicecomb-service-center/server/core/backend"
 	"github.com/apache/servicecomb-service-center/server/core/proto"
+	"github.com/apache/servicecomb-service-center/server/plugin"
+	"github.com/apache/servicecomb-service-center/server/plugin/quota"
+	"github.com/apache/servicecomb-service-center/server/plugin/registry"
 	scerr "github.com/apache/servicecomb-service-center/server/scerror"
+	"os"
+	"strings"
 )
 
 // TODO: define error with names here
 
+var schemaEditable bool
+
 func init() {
 	// TODO: set logger
 	// TODO: register storage plugin to plugin manager
+	schemaEditableConfig := strings.ToLower(os.Getenv("SCHEMA_EDITABLE"))
+	schemaEditable = strings.Compare(schemaEditableConfig, "true") == 0
 }
 
-type DataSource struct{}
+type DataSource struct {
+	// schemaEditable determines whether schema modification is allowed for
+	SchemaEditable bool
+}
 
 func NewDataSource() *DataSource {
 	// TODO: construct a reasonable DataSource instance
@@ -47,6 +61,7 @@ func NewDataSource() *DataSource {
 
 func (ds *DataSource) initialize() error {
 	// TODO: init DataSource members
+	ds.SchemaEditable = schemaEditable
 	return nil
 }
 
@@ -60,7 +75,7 @@ func (ds *DataSource) RegisterService(ctx context.Context, request *pb.CreateSer
 	serviceBody := request.Service
 
 	// construct data to invoke etcd client
-	opts, uniqueCmpOpts, failOpts, err := CapRegisterData(ctx, request)
+	opts, uniqueCmpOpts, failOpts, err := capRegisterData(ctx, request)
 	if err != nil {
 		return &pb.CreateServiceResponse{
 			Response: proto.CreateResponse(scerr.ErrInternal, err.Error()),
@@ -68,14 +83,14 @@ func (ds *DataSource) RegisterService(ctx context.Context, request *pb.CreateSer
 	}
 	resp, err := backend.Registry().TxnWithCmp(ctx, opts, uniqueCmpOpts, failOpts)
 
-	return NewRegisterServiceResp(ctx, serviceBody, resp, err)
+	return newRegisterServiceResp(ctx, serviceBody, resp, err)
 }
 
 func (ds *DataSource) GetServices(ctx context.Context, request *pb.GetServicesRequest) (
 	*pb.GetServicesResponse, error) {
 	domainProject := util.ParseDomainProject(ctx)
 
-	services, err := GetAllServiceUtil(ctx, domainProject)
+	services, err := getAllServiceUtil(ctx, domainProject)
 	if err != nil {
 		log.Errorf(err, "get all services by domain failed")
 		return &pb.GetServicesResponse{
@@ -92,7 +107,7 @@ func (ds *DataSource) GetServices(ctx context.Context, request *pb.GetServicesRe
 func (ds *DataSource) GetService(ctx context.Context, request *pb.GetServiceRequest) (
 	*pb.GetServiceResponse, error) {
 	domainProject := util.ParseDomainProject(ctx)
-	singleService, err := GetSingleService(ctx, domainProject, request.ServiceId)
+	singleService, err := getSingleService(ctx, domainProject, request.ServiceId)
 
 	if err != nil {
 		log.Errorf(err, "get micro-service[%s] failed, get service file failed", request.ServiceId)
@@ -112,6 +127,44 @@ func (ds *DataSource) GetService(ctx context.Context, request *pb.GetServiceRequ
 	}, nil
 }
 
+func (ds *DataSource) ExistService(ctx context.Context, request *pb.GetExistenceRequest) (*pb.GetExistenceResponse,
+	error) {
+	domainProject := util.ParseDomainProject(ctx)
+	serviceFlag := util.StringJoin([]string{
+		request.Environment, request.AppId, request.ServiceName, request.Version}, "/")
+
+	ids, exist, err := findServiceIds(ctx, request.Version, &pb.MicroServiceKey{
+		Environment: request.Environment,
+		AppId:       request.AppId,
+		ServiceName: request.ServiceName,
+		Alias:       request.ServiceName,
+		Version:     request.Version,
+		Tenant:      domainProject,
+	})
+	if err != nil {
+		log.Errorf(err, "micro-service[%s] exist failed, find serviceIDs failed", serviceFlag)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrInternal, err.Error()),
+		}, err
+	}
+	if !exist {
+		log.Infof("micro-service[%s] exist failed, service does not exist", serviceFlag)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrServiceNotExists, serviceFlag+" does not exist."),
+		}, nil
+	}
+	if len(ids) == 0 {
+		log.Infof("micro-service[%s] exist failed, version mismatch", serviceFlag)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrServiceVersionNotExists, serviceFlag+" version mismatch."),
+		}, nil
+	}
+	return &pb.GetExistenceResponse{
+		Response:  proto.CreateResponse(proto.Response_SUCCESS, "get service id successfully."),
+		ServiceId: ids[0], // 约定多个时,取较新版本
+	}, nil
+}
+
 func (ds *DataSource) UpdateService(ctx context.Context, service *pb.UpdateServicePropsRequest) (
 	*pb.UpdateServicePropsResponse, error) {
 	panic("implement me")
@@ -138,8 +191,71 @@ func (ds *DataSource) UnRegisterInstance() {
 	panic("implement me")
 }
 
-func (ds *DataSource) AddSchema() {
-	panic("implement me")
+func (ds *DataSource) ModifySchemas(ctx context.Context, request *pb.ModifySchemasRequest) (
+	*pb.ModifySchemasResponse, error) {
+	remoteIP := util.GetIPFromContext(ctx)
+	serviceID := request.ServiceId
+	domainProject := util.ParseDomainProject(ctx)
+
+	serviceInfo, err := getService(ctx, domainProject, serviceID)
+	if err != nil {
+		log.Errorf(err, "modify service[%s] schemas failed, get service failed, operator: %s", serviceID, remoteIP)
+		return &pb.ModifySchemasResponse{
+			Response: proto.CreateResponse(scerr.ErrInternal, err.Error()),
+		}, err
+	}
+	if serviceInfo == nil {
+		log.Errorf(nil, "modify service[%s] schemas failed, service does not exist, operator: %s",
+			serviceID, remoteIP)
+		return &pb.ModifySchemasResponse{
+			Response: proto.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
+		}, nil
+	}
+
+	respErr := ds.modifySchemas(ctx, domainProject, serviceInfo, request.Schemas)
+	if respErr != nil {
+		log.Errorf(nil, "modify service[%s] schemas failed, operator: %s", serviceID, remoteIP)
+		resp := &pb.ModifySchemasResponse{
+			Response: proto.CreateResponseWithSCErr(respErr),
+		}
+		if respErr.InternalError() {
+			return resp, respErr
+		}
+		return resp, nil
+	}
+
+	return &pb.ModifySchemasResponse{
+		Response: proto.CreateResponse(proto.Response_SUCCESS, "modify schemas info successfully."),
+	}, nil
+}
+
+func (ds *DataSource) ModifySchema(ctx context.Context, request *pb.ModifySchemaRequest) (
+	*pb.ModifySchemaResponse, error) {
+	remoteIP := util.GetIPFromContext(ctx)
+	serviceID := request.ServiceId
+	schemaID := request.SchemaId
+
+	schema := pb.Schema{
+		SchemaId: schemaID,
+		Summary:  request.Summary,
+		Schema:   request.Schema,
+	}
+	err := ds.modifySchema(ctx, serviceID, &schema)
+	if err != nil {
+		log.Errorf(err, "modify schema[%s/%s] failed, operator: %s", serviceID, schemaID, remoteIP)
+		resp := &pb.ModifySchemaResponse{
+			Response: proto.CreateResponseWithSCErr(err),
+		}
+		if err.InternalError() {
+			return resp, err
+		}
+		return resp, nil
+	}
+
+	log.Infof("modify schema[%s/%s] successfully, operator: %s", serviceID, schemaID, remoteIP)
+	return &pb.ModifySchemaResponse{
+		Response: proto.CreateResponse(proto.Response_SUCCESS, "modify schema info success"),
+	}, nil
 }
 
 func (ds *DataSource) GetSchema() {
@@ -154,6 +270,46 @@ func (ds *DataSource) DeleteSchema() {
 	panic("implement me")
 }
 
+func (ds *DataSource) ExistSchema(ctx context.Context, request *pb.GetExistenceRequest) (
+	*pb.GetExistenceResponse, error) {
+	domainProject := util.ParseDomainProject(ctx)
+
+	if !serviceExist(ctx, domainProject, request.ServiceId) {
+		log.Warnf("schema[%s/%s] exist failed, service does not exist", request.ServiceId, request.SchemaId)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrServiceNotExists, "service does not exist."),
+		}, nil
+	}
+
+	key := apt.GenerateServiceSchemaKey(domainProject, request.ServiceId, request.SchemaId)
+	exist, err := checkSchemaInfoExist(ctx, key)
+	if err != nil {
+		log.Errorf(err, "schema[%s/%s] exist failed, get schema failed", request.ServiceId, request.SchemaId)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrInternal, err.Error()),
+		}, err
+	}
+	if !exist {
+		log.Infof("schema[%s/%s] exist failed, schema does not exist", request.ServiceId, request.SchemaId)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrSchemaNotExists, "schema does not exist."),
+		}, nil
+	}
+	schemaSummary, err := getSchemaSummary(ctx, domainProject, request.ServiceId, request.SchemaId)
+	if err != nil {
+		log.Errorf(err, "schema[%s/%s] exist failed, get schema summary failed",
+			request.ServiceId, request.SchemaId)
+		return &pb.GetExistenceResponse{
+			Response: proto.CreateResponse(scerr.ErrInternal, err.Error()),
+		}, err
+	}
+	return &pb.GetExistenceResponse{
+		Response: proto.CreateResponse(proto.Response_SUCCESS, "Schema exist."),
+		SchemaId: request.SchemaId,
+		Summary:  schemaSummary,
+	}, nil
+}
+
 func (ds *DataSource) AddTag() {
 	panic("implement me")
 }
@@ -185,3 +341,219 @@ func (ds *DataSource) UpdateRule() {
 func (ds *DataSource) DeleteRule() {
 	panic("implement me")
 }
+
+func (ds *DataSource) modifySchemas(ctx context.Context, domainProject string, service *pb.MicroService,
+	schemas []*pb.Schema) *scerr.Error {
+	remoteIP := util.GetIPFromContext(ctx)
+	serviceID := service.ServiceId
+	schemasFromDatabase, err := getSchemasFromDatabase(ctx, domainProject, serviceID)
+	if err != nil {
+		log.Errorf(nil, "modify service[%s] schemas failed, get schemas failed, operator: %s",
+			serviceID, remoteIP)
+		return scerr.NewError(scerr.ErrUnavailableBackend, err.Error())
+	}
+
+	needUpdateSchemas, needAddSchemas, needDeleteSchemas, nonExistSchemaIds :=
+		schemasAnalysis(schemas, schemasFromDatabase, service.Schemas)
+
+	pluginOps := make([]registry.PluginOp, 0)
+	if !ds.isSchemaEditable(service) {
+		if len(service.Schemas) == 0 {
+			res := quota.NewApplyQuotaResource(quota.SchemaQuotaType, domainProject, serviceID, int64(len(nonExistSchemaIds)))
+			rst := plugin.Plugins().Quota().Apply4Quotas(ctx, res)
+			errQuota := rst.Err
+			if errQuota != nil {
+				log.Errorf(errQuota, "modify service[%s] schemas failed, operator: %s", serviceID, remoteIP)
+				return errQuota
+			}
+
+			service.Schemas = nonExistSchemaIds
+			opt, err := updateService(domainProject, serviceID, service)
+			if err != nil {
+				log.Errorf(err, "modify service[%s] schemas failed, update service.Schemas failed, operator: %s",
+					serviceID, remoteIP)
+				return scerr.NewError(scerr.ErrInternal, err.Error())
+			}
+			pluginOps = append(pluginOps, opt)
+		} else {
+			if len(nonExistSchemaIds) != 0 {
+				errInfo := fmt.Errorf("non-existent schemaIDs %v", nonExistSchemaIds)
+				log.Errorf(errInfo, "modify service[%s] schemas failed, operator: %s", serviceID, remoteIP)
+				return scerr.NewError(scerr.ErrUndefinedSchemaID, errInfo.Error())
+			}
+			for _, needUpdateSchema := range needUpdateSchemas {
+				exist, err := isExistSchemaSummary(ctx, domainProject, serviceID, needUpdateSchema.SchemaId)
+				if err != nil {
+					return scerr.NewError(scerr.ErrInternal, err.Error())
+				}
+				if !exist {
+					opts := schemaWithDatabaseOpera(registry.OpPut, domainProject, serviceID, needUpdateSchema)
+					pluginOps = append(pluginOps, opts...)
+				} else {
+					log.Warnf("schema[%s/%s] and it's summary already exist, skip to update, operator: %s",
+						serviceID, needUpdateSchema.SchemaId, remoteIP)
+				}
+			}
+		}
+
+		for _, schema := range needAddSchemas {
+			log.Infof("add new schema[%s/%s], operator: %s", serviceID, schema.SchemaId, remoteIP)
+			opts := schemaWithDatabaseOpera(registry.OpPut, domainProject, service.ServiceId, schema)
+			pluginOps = append(pluginOps, opts...)
+		}
+	} else {
+		quotaSize := len(needAddSchemas) - len(needDeleteSchemas)
+		if quotaSize > 0 {
+			res := quota.NewApplyQuotaResource(quota.SchemaQuotaType, domainProject, serviceID, int64(quotaSize))
+			rst := plugin.Plugins().Quota().Apply4Quotas(ctx, res)
+			err := rst.Err
+			if err != nil {
+				log.Errorf(err, "modify service[%s] schemas failed, operator: %s", serviceID, remoteIP)
+				return err
+			}
+		}
+
+		var schemaIDs []string
+		for _, schema := range needAddSchemas {
+			log.Infof("add new schema[%s/%s], operator: %s", serviceID, schema.SchemaId, remoteIP)
+			opts := schemaWithDatabaseOpera(registry.OpPut, domainProject, service.ServiceId, schema)
+			pluginOps = append(pluginOps, opts...)
+			schemaIDs = append(schemaIDs, schema.SchemaId)
+		}
+
+		for _, schema := range needUpdateSchemas {
+			log.Infof("update schema[%s/%s], operator: %s", serviceID, schema.SchemaId, remoteIP)
+			opts := schemaWithDatabaseOpera(registry.OpPut, domainProject, serviceID, schema)
+			pluginOps = append(pluginOps, opts...)
+			schemaIDs = append(schemaIDs, schema.SchemaId)
+		}
+
+		for _, schema := range needDeleteSchemas {
+			log.Infof("delete non-existent schema[%s/%s], operator: %s", serviceID, schema.SchemaId, remoteIP)
+			opts := schemaWithDatabaseOpera(registry.OpDel, domainProject, serviceID, schema)
+			pluginOps = append(pluginOps, opts...)
+		}
+
+		service.Schemas = schemaIDs
+		opt, err := updateService(domainProject, serviceID, service)
+		if err != nil {
+			log.Errorf(err, "modify service[%s] schemas failed, update service.Schemas failed, operator: %s",
+				serviceID, remoteIP)
+			return scerr.NewError(scerr.ErrInternal, err.Error())
+		}
+		pluginOps = append(pluginOps, opt)
+	}
+
+	if len(pluginOps) != 0 {
+		resp, err := backend.BatchCommitWithCmp(ctx, pluginOps,
+			[]registry.CompareOp{registry.OpCmp(
+				registry.CmpVer(util.StringToBytesWithNoCopy(apt.GenerateServiceKey(domainProject, serviceID))),
+				registry.CmpNotEqual, 0)},
+			nil)
+		if err != nil {
+			return scerr.NewError(scerr.ErrUnavailableBackend, err.Error())
+		}
+		if !resp.Succeeded {
+			return scerr.NewError(scerr.ErrServiceNotExists, "Service does not exist.")
+		}
+	}
+	return nil
+}
+
+func (ds *DataSource) isSchemaEditable(service *pb.MicroService) bool {
+	return (len(service.Environment) != 0 && service.Environment != pb.ENV_PROD) || ds.SchemaEditable
+}
+
+func (ds *DataSource) modifySchema(ctx context.Context, serviceID string, schema *pb.Schema) *scerr.Error {
+	remoteIP := util.GetIPFromContext(ctx)
+	domainProject := util.ParseDomainProject(ctx)
+	schemaID := schema.SchemaId
+
+	microService, err := getService(ctx, domainProject, serviceID)
+	if err != nil {
+		log.Errorf(err, "modify schema[%s/%s] failed, get `microService failed, operator: %s",
+			serviceID, schemaID, remoteIP)
+		return scerr.NewError(scerr.ErrInternal, err.Error())
+	}
+	if microService == nil {
+		log.Errorf(nil, "modify schema[%s/%s] failed, microService does not exist, operator: %s",
+			serviceID, schemaID, remoteIP)
+		return scerr.NewError(scerr.ErrServiceNotExists, "Service does not exist")
+	}
+
+	var pluginOps []registry.PluginOp
+	isExist := isExistSchemaID(microService, []*pb.Schema{schema})
+
+	if !ds.isSchemaEditable(microService) {
+		if len(microService.Schemas) != 0 && !isExist {
+			return scerr.NewError(scerr.ErrUndefinedSchemaID, "Non-existent schemaID can't be added in "+pb.ENV_PROD)
+		}
+
+		key := apt.GenerateServiceSchemaKey(domainProject, serviceID, schemaID)
+		respSchema, err := backend.Store().Schema().Search(ctx, registry.WithStrKey(key), registry.WithCountOnly())
+		if err != nil {
+			log.Errorf(err, "modify schema[%s/%s] failed, get schema summary failed, operator: %s",
+				serviceID, schemaID, remoteIP)
+			return scerr.NewError(scerr.ErrUnavailableBackend, err.Error())
+		}
+
+		if respSchema.Count != 0 {
+			if len(schema.Summary) == 0 {
+				log.Errorf(err, "%s mode, schema[%s/%s] already exists, can not be changed, operator: %s",
+					pb.ENV_PROD, serviceID, schemaID, remoteIP)
+				return scerr.NewError(scerr.ErrModifySchemaNotAllow,
+					"schema already exist, can not be changed in "+pb.ENV_PROD)
+			}
+
+			exist, err := isExistSchemaSummary(ctx, domainProject, serviceID, schemaID)
+			if err != nil {
+				log.Errorf(err, "check schema[%s/%s] summary existence failed, operator: %s",
+					serviceID, schemaID, remoteIP)
+				return scerr.NewError(scerr.ErrInternal, err.Error())
+			}
+			if exist {
+				log.Errorf(err, "%s mode, schema[%s/%s] already exist, can not be changed, operator: %s",
+					pb.ENV_PROD, serviceID, schemaID, remoteIP)
+				return scerr.NewError(scerr.ErrModifySchemaNotAllow, "schema already exist, can not be changed in "+pb.ENV_PROD)
+			}
+		}
+
+		if len(microService.Schemas) == 0 {
+			microService.Schemas = append(microService.Schemas, schemaID)
+			opt, err := updateService(domainProject, serviceID, microService)
+			if err != nil {
+				log.Errorf(err, "modify schema[%s/%s] failed, update microService.Schemas failed, operator: %s",
+					serviceID, schemaID, remoteIP)
+				return scerr.NewError(scerr.ErrInternal, err.Error())
+			}
+			pluginOps = append(pluginOps, opt)
+		}
+	} else {
+		if !isExist {
+			microService.Schemas = append(microService.Schemas, schemaID)
+			opt, err := updateService(domainProject, serviceID, microService)
+			if err != nil {
+				log.Errorf(err, "modify schema[%s/%s] failed, update microService.Schemas failed, operator: %s",
+					serviceID, schemaID, remoteIP)
+				return scerr.NewError(scerr.ErrInternal, err.Error())
+			}
+			pluginOps = append(pluginOps, opt)
+		}
+	}
+
+	opts := commitSchemaInfo(domainProject, serviceID, schema)
+	pluginOps = append(pluginOps, opts...)
+
+	resp, err := backend.Registry().TxnWithCmp(ctx, pluginOps,
+		[]registry.CompareOp{registry.OpCmp(
+			registry.CmpVer(util.StringToBytesWithNoCopy(apt.GenerateServiceKey(domainProject, serviceID))),
+			registry.CmpNotEqual, 0)},
+		nil)
+	if err != nil {
+		return scerr.NewError(scerr.ErrUnavailableBackend, err.Error())
+	}
+	if !resp.Succeeded {
+		return scerr.NewError(scerr.ErrServiceNotExists, "Service does not exist.")
+	}
+	return nil
+}
diff --git a/server/service/ms/etcd/etcd_test.go b/server/service/ms/etcd/etcd_test.go
index 0bf73e1..605ee98 100644
--- a/server/service/ms/etcd/etcd_test.go
+++ b/server/service/ms/etcd/etcd_test.go
@@ -16,10 +16,12 @@
 package etcd_test
 
 import (
+	"github.com/apache/servicecomb-service-center/pkg/log"
 	pb "github.com/apache/servicecomb-service-center/pkg/registry"
 	"github.com/apache/servicecomb-service-center/server/core/proto"
 	"github.com/apache/servicecomb-service-center/server/plugin/quota"
 	scerr "github.com/apache/servicecomb-service-center/server/scerror"
+	"github.com/apache/servicecomb-service-center/server/service"
 	"github.com/apache/servicecomb-service-center/server/service/ms"
 	"github.com/apache/servicecomb-service-center/server/service/ms/etcd"
 	"github.com/go-chassis/go-archaius"
@@ -35,6 +37,7 @@ var (
 	TooLongAppId       = strings.Repeat("x", 162)
 	TooLongSchemaId    = strings.Repeat("x", 162)
 	TooLongServiceName = strings.Repeat("x", 130)
+	TooLongExistence   = strings.Repeat("x", 128+160+2)
 	TooLongAlias       = strings.Repeat("x", 130)
 	TooLongFramework   = strings.Repeat("x", 66)
 )
@@ -44,7 +47,7 @@ func TestInit(t *testing.T) {
 	_ = archaius.Set("servicecomb.ms.name", "etcd")
 }
 
-func TestEtcd_RegisterService(t *testing.T) {
+func TestService_Register(t *testing.T) {
 	t.Run("Register service after init & install, should pass", func(t *testing.T) {
 		ms.Install("etcd", func(opts ms.Options) (ms.DataSource, error) {
 			return etcd.NewDataSource(), nil
@@ -292,7 +295,7 @@ func TestEtcd_RegisterService(t *testing.T) {
 		})
 }
 
-func TestEtcd_GetService(t *testing.T) {
+func TestService_Get(t *testing.T) {
 	// get service test
 	t.Run("query all services, should pass", func(t *testing.T) {
 		ms.Install("etcd", func(opts ms.Options) (ms.DataSource, error) {
@@ -359,3 +362,985 @@ func TestEtcd_GetService(t *testing.T) {
 		assert.Equal(t, resp.Response.GetCode(), scerr.ErrServiceNotExists)
 	})
 }
+
+func TestService_Exist(t *testing.T) {
+	var (
+		serviceId1 string
+		serviceId2 string
+	)
+
+	ms.Install("etcd", func(opts ms.Options) (ms.DataSource, error) {
+		return etcd.NewDataSource(), nil
+	})
+
+	err := ms.Init(ms.Options{
+		Endpoint:       "",
+		PluginImplName: ms.ImplName(archaius.GetString("servicecomb.ms.name", "etcd")),
+	})
+	assert.NoError(t, err)
+
+	t.Run("create service", func(t *testing.T) {
+		svc := &pb.MicroService{
+			Alias:       "es_service_ms",
+			ServiceName: "exist_service_service_ms",
+			AppId:       "exist_appId_service_ms",
+			Version:     "1.0.0",
+			Level:       "FRONT",
+			Schemas: []string{
+				"first_schemaId_service_ms",
+			},
+			Status: "UP",
+		}
+		resp, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: svc,
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, "", resp.ServiceId)
+		serviceId1 = resp.ServiceId
+
+		svc.ServiceId = ""
+		svc.Environment = pb.ENV_PROD
+		resp, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: svc,
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, "", resp.ServiceId)
+		serviceId2 = resp.ServiceId
+	})
+
+	t.Run("check exist when service does not exist", func(t *testing.T) {
+		log.Info("check by querying a not exist serviceName")
+		resp, err := ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId",
+			ServiceName: "notExistService_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrServiceNotExists, resp.Response.GetCode())
+
+		log.Info("check by querying a not exist env")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			Environment: pb.ENV_TEST,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "exist_service_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrServiceNotExists, resp.Response.GetCode())
+
+		log.Info("check by querying a not exist env with alias")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			Environment: pb.ENV_TEST,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "es_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrServiceNotExists, resp.Response.GetCode())
+
+		log.Info("check by querying with a mismatching version")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "exist_service_service_ms",
+			Version:     "2.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrServiceNotExists, resp.Response.GetCode())
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "exist_service_service_ms",
+			Version:     "0.0.0-1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrServiceVersionNotExists, resp.Response.GetCode())
+	})
+
+	t.Run("check exist when service exists", func(t *testing.T) {
+		log.Info("search with serviceName")
+		resp, err := ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "exist_service_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId1, resp.ServiceId)
+
+		log.Info("check with serviceName and env")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			Environment: pb.ENV_PROD,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "exist_service_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId2, resp.ServiceId)
+
+		log.Info("check with alias")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "es_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId1, resp.ServiceId)
+
+		log.Info("check with alias and env")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			Environment: pb.ENV_PROD,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "es_service_ms",
+			Version:     "1.0.0",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId2, resp.ServiceId)
+
+		log.Info("check with latest versionRule")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "es_service_ms",
+			Version:     "latest",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId1, resp.ServiceId)
+
+		log.Info("check with 1.0.0+ versionRule")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "es_service_ms",
+			Version:     "1.0.0+",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId1, resp.ServiceId)
+
+		log.Info("check with range versionRule")
+		resp, err = ms.MicroService().ExistService(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeMicroservice,
+			AppId:       "exist_appId_service_ms",
+			ServiceName: "es_service_ms",
+			Version:     "0.9.1-1.0.1",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, serviceId1, resp.ServiceId)
+	})
+}
+
+func TestSchema_Create(t *testing.T) {
+	ms.Install("etcd", func(opts ms.Options) (ms.DataSource, error) {
+		return etcd.NewDataSource(), nil
+	})
+	err := ms.Init(ms.Options{
+		Endpoint:       "",
+		PluginImplName: ms.ImplName(archaius.GetString("servicecomb.ms.name", "etcd")),
+	})
+	assert.NoError(t, err)
+
+	var (
+		serviceIdDev string
+	)
+
+	t.Run("create service, should pass", func(t *testing.T) {
+		svc := &pb.MicroService{
+			Alias:       "create_schema_group_service_ms",
+			ServiceName: "create_schema_service_service_ms",
+			Version:     "1.0.0",
+			Level:       "FRONT",
+			Status:      pb.MS_UP,
+			Environment: pb.ENV_DEV,
+		}
+		resp, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: svc,
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, "", resp.ServiceId)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+		serviceIdDev = resp.ServiceId
+
+		resp, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_group_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, "", resp.ServiceId)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+	})
+
+	t.Run("create schemas out of gauge", func(t *testing.T) {
+		log.Info("create schemas out of gauge")
+		size := quota.DefaultSchemaQuota + 1
+		schemaIds := make([]string, 0, size)
+		schemas := make([]*pb.Schema, 0, size)
+		for i := 0; i < size; i++ {
+			s := "ServiceCombTestTheLimitOfSchemasServiceMS" + strconv.Itoa(i)
+
+			schemaIds = append(schemaIds, s)
+			schemas = append(schemas, &pb.Schema{
+				SchemaId: s,
+				Schema:   s,
+				Summary:  s,
+			})
+		}
+
+		log.Info("batch modify schemas 1, should failed")
+		resp, err := ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrNotEnoughQuota, resp.Response.GetCode())
+
+		log.Info("batch modify schemas 2")
+		resp, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev,
+			Schemas:   schemas[:quota.DefaultSchemaQuota],
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+
+		log.Info("should be failed in production env")
+		resp, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrNotEnoughQuota, resp.Response.GetCode())
+	})
+
+	t.Run("batch create schemas in dev env", func(t *testing.T) {
+		var (
+			serviceIdDev1 string
+			serviceIdDev2 string
+		)
+
+		log.Info("register service, should pass")
+		resp, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schemas_dev_service_ms",
+				ServiceName: "create_schemas_service_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_DEV,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+		serviceIdDev1 = resp.ServiceId
+
+		resp, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schemas_dev_service_ms",
+				ServiceName: "create_schemas_service_service_ms",
+				Version:     "1.0.1",
+				Level:       "FRONT",
+				Schemas: []string{
+					"first_schemaId_service_ms",
+				},
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_DEV,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+		serviceIdDev2 = resp.ServiceId
+
+		log.Info("create schemas with service schemaId set is empty")
+		schemas := []*pb.Schema{
+			{
+				SchemaId: "first_schemaId_service_ms",
+				Schema:   "first_schema_service_ms",
+				Summary:  "first0summary_service_ms",
+			},
+			{
+				SchemaId: "first_schemaId_service_ms",
+				Schema:   "first_schema_service_ms",
+				Summary:  "first0summary_service_ms",
+			},
+		}
+		respCreateSchema, err := ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateSchema.Response.GetCode())
+
+		// todo: test GetAllSchemaInfo interface refers to schema_test line 342
+
+		log.Info("modify schemas")
+		schemas = []*pb.Schema{
+			{
+				SchemaId: "first_schemaId_service_ms",
+				Schema:   "first_schema_change_service_ms",
+				Summary:  "first0summary1change_service_ms",
+			},
+		}
+		respCreateSchema, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev1,
+			Schemas:   schemas,
+		})
+
+		log.Info("add schemas")
+		schemas = []*pb.Schema{
+			{
+				SchemaId: "second_schemaId_service_ms",
+				Schema:   "second_schema_service_ms",
+				Summary:  "second0summary_service_ms",
+			},
+		}
+		respCreateSchema, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateSchema.Response.GetCode())
+
+		log.Info("query service by serviceID to obtain schema info")
+		respGetService, err := ms.MicroService().GetService(getContext(), &pb.GetServiceRequest{
+			ServiceId: serviceIdDev1,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respGetService.Response.GetCode())
+		assert.Equal(t, []string{"second_schemaId_service_ms"}, respGetService.Service.Schemas)
+
+		log.Info("add new schemaId not exist in service's schemaId list")
+		schemas = []*pb.Schema{
+			{
+				SchemaId: "second_schemaId_service_ms",
+				Schema:   "second_schema_service_ms",
+				Summary:  "second0summary_service_ms",
+			},
+		}
+		respCreateSchema, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdDev2,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateSchema.Response.GetCode())
+
+		respGetService, err = ms.MicroService().GetService(getContext(), &pb.GetServiceRequest{
+			ServiceId: serviceIdDev2,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respGetService.Response.GetCode())
+		assert.Equal(t, []string{"second_schemaId_service_ms"}, respGetService.Service.Schemas)
+	})
+
+	t.Run("batch create schemas in production env", func(t *testing.T) {
+		var (
+			serviceIdPro1 string
+			serviceIdPro2 string
+		)
+
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schemas_prod_service_ms",
+				ServiceName: "create_schemas_service_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro1 = respCreateService.ServiceId
+
+		respCreateService, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schemas_prod_service_ms",
+				ServiceName: "create_schemas_service_service_ms",
+				Version:     "1.0.1",
+				Level:       "FRONT",
+				Schemas: []string{
+					"first_schemaId_service_ms",
+					"second_schemaId_service_ms",
+				},
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro2 = respCreateService.ServiceId
+
+		log.Info("add schemas to service whose schemaId set is empty")
+		schemas := []*pb.Schema{
+			{
+				SchemaId: "first_schemaId_service_ms",
+				Schema:   "first_schema_service_ms",
+				Summary:  "first0summary_service_ms",
+			},
+			{
+				SchemaId: "first_schemaId_service_ms",
+				Schema:   "first_schema_service_ms",
+				Summary:  "first0summary_service_ms",
+			},
+		}
+		respModifySchemas, err := ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+		respGetService, err := ms.MicroService().GetService(getContext(), &pb.GetServiceRequest{
+			ServiceId: serviceIdPro1,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respGetService.Response.GetCode())
+		assert.Equal(t, []string{"first_schemaId_service_ms"}, respGetService.Service.Schemas)
+
+		// todo: finish ut after implementing GetAllSchemaInfo, refer to schema_test.go line. 496
+
+		log.Info("modify schemas content already exists, will skip more exist schema")
+		respModifySchemas, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+
+		log.Info("add schemas, non-exist schemaId")
+		schemas = []*pb.Schema{
+			{
+				SchemaId: "second_schemaId_service_ms",
+				Schema:   "second_schema_service_ms",
+				Summary:  "second0summary_service_ms",
+			},
+		}
+		respModifySchemas, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrUndefinedSchemaID, respModifySchemas.Response.GetCode())
+
+		log.Info("add schema when summary is empty")
+		respModifySchema, err := ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro2,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("add schemas when summary in database is empty")
+		schemas = []*pb.Schema{
+			{
+				SchemaId: "first_schemaId_service_ms",
+				Schema:   "first_schema_service_ms",
+				Summary:  "first0summary_service_ms",
+			},
+		}
+		respModifySchemas, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro2,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+		respExist, err := ms.MicroService().ExistSchema(getContext(), &pb.GetExistenceRequest{
+			Type:      service.ExistTypeSchema,
+			ServiceId: serviceIdPro2,
+			SchemaId:  "first_schemaId_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, "first0summary_service_ms", respExist.Summary)
+
+		respModifySchemas, err = ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro2,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+	})
+
+	t.Run("create a schema in dev env", func(t *testing.T) {
+		var (
+			serviceIdDev1 string
+			serviceIdDev2 string
+		)
+
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_dev_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_DEV,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdDev1 = respCreateService.ServiceId
+
+		respCreateService, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_dev_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.1",
+				Level:       "FRONT",
+				Schemas: []string{
+					"first_schemaId_service_ms",
+				},
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_DEV,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdDev2 = respCreateService.ServiceId
+
+		log.Info("create a schema for service whose schemaID is empty")
+		respModifySchema, err := ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdDev1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("create schema for the service whose schemaId already exist")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdDev2,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("create schema for the service whose schema summary is empty")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdDev1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_change_service_ms",
+			Summary:   "first0summary1change_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("create schema for the service whose schema summary already exist")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdDev1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+			Summary:   "first0summary_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("add schema")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdDev1,
+			SchemaId:  "second_schemaId_service_ms",
+			Schema:    "second_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+	})
+
+	t.Run("create a schema in production env", func(t *testing.T) {
+		var (
+			serviceIdPro1 string
+			serviceIdPro2 string
+		)
+
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_prod_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro1 = respCreateService.ServiceId
+
+		respCreateService, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_prod_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.1",
+				Level:       "FRONT",
+				Schemas: []string{
+					"first_schemaId_service_ms",
+					"second_schemaId_service_ms",
+				},
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro2 = respCreateService.ServiceId
+
+		log.Info("create a schema for service whose schemaID is empty")
+		respModifySchema, err := ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("modify schema for the service whose schema summary is empty")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_change_service_ms",
+			Summary:   "first0summary1change_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("modify schema for the service whose schema summary already exist")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+			Summary:   "first0summary_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("add schema")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "second_schemaId_service_ms",
+			Schema:    "second_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("modify schema for the service whose schemaId already exist")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro2,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+	})
+
+	t.Run("create a schema in empty env", func(t *testing.T) {
+		var (
+			serviceIdPro1 string
+			serviceIdPro2 string
+		)
+
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_empty_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro1 = respCreateService.ServiceId
+
+		respCreateService, err = ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "create_schema_empty_service_ms",
+				ServiceName: "create_schema_service_service_ms",
+				Version:     "1.0.1",
+				Level:       "FRONT",
+				Schemas: []string{
+					"first_schemaId_service_ms",
+					"second_schemaId_service_ms",
+				},
+				Status: pb.MS_UP,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro2 = respCreateService.ServiceId
+
+		log.Info("create a schema for service whose schemaID is empty")
+		respModifySchema, err := ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("modify schema for the service whose schema summary is empty")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_change_service_ms",
+			Summary:   "first0summary1change_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("modify schema for the service whose schema summary already exist")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+			Summary:   "first0summary_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("add schema")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  "second_schemaId_service_ms",
+			Schema:    "second_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.NotEqual(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+
+		log.Info("modify schema for the service whose schemaId already exist")
+		respModifySchema, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro2,
+			SchemaId:  "first_schemaId_service_ms",
+			Schema:    "first_schema_service_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+	})
+
+	t.Run("add a schemaId in production env while schema editable is set", func(t *testing.T) {
+		var (
+			serviceIdPro1 string
+		)
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "add_a_schemaId_prod_schema_lock_ms",
+				ServiceName: "add_a_schemaId_prod_schema_lock_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro1 = respCreateService.ServiceId
+
+		log.Info("add a schema with new schemaId, should pass")
+		schemas := []*pb.Schema{
+			{
+				SchemaId: "first_schemaId_ms",
+				Schema:   "first_schema_ms",
+				Summary:  "first0summary_ms",
+			},
+		}
+		respModifySchemas, err := ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+
+		respService, err := ms.MicroService().GetService(getContext(), &pb.GetServiceRequest{
+			ServiceId: serviceIdPro1,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respService.Response.GetCode())
+		assert.Equal(t, []string{"first_schemaId_ms"}, respService.Service.Schemas)
+
+		schemas = []*pb.Schema{
+			{
+				SchemaId: "second_schemaId_ms",
+				Schema:   "second_schema_ms",
+				Summary:  "second0summary_ms",
+			},
+		}
+		log.Info("schema edit not allowed, add a schema with new schemaId should fail")
+
+		localMicroServiceDs := &etcd.DataSource{SchemaEditable: false}
+		respModifySchemas, err = localMicroServiceDs.ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrUndefinedSchemaID, respModifySchemas.Response.GetCode())
+
+		log.Info("schema edit allowed, add a schema with new schemaId, should pass")
+		localMicroServiceDs = &etcd.DataSource{SchemaEditable: true}
+		respModifySchemas, err = localMicroServiceDs.ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+	})
+
+	t.Run("modify a schema in production env while schema editable is set", func(t *testing.T) {
+		var (
+			serviceIdPro1 string
+		)
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "modify_a_schema_prod_schema_lock_ms",
+				ServiceName: "modify_a_schema_prod_schema_lock_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_PROD,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceIdPro1 = respCreateService.ServiceId
+
+		log.Info("add schemas, should pass")
+		schemas := []*pb.Schema{
+			{
+				SchemaId: "first_schemaId_ms",
+				Schema:   "first_schema_ms",
+				Summary:  "first0summary_ms",
+			},
+		}
+		respModifySchemas, err := ms.MicroService().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+			ServiceId: serviceIdPro1,
+			Schemas:   schemas,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchemas.Response.GetCode())
+
+		respService, err := ms.MicroService().GetService(getContext(), &pb.GetServiceRequest{
+			ServiceId: serviceIdPro1,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, []string{"first_schemaId_ms"}, respService.Service.Schemas)
+
+		log.Info("schema edit not allowed, modify schema should fail")
+		localMicroServiceDs := &etcd.DataSource{SchemaEditable: false}
+		respModifySchema, err := localMicroServiceDs.ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  schemas[0].SchemaId,
+			Summary:   schemas[0].Summary,
+			Schema:    schemas[0].SchemaId,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, scerr.ErrModifySchemaNotAllow, respModifySchema.Response.GetCode())
+
+		log.Info("schema edit allowed, add a schema with new schemaId, should pass")
+		localMicroServiceDs = &etcd.DataSource{SchemaEditable: true}
+		respModifySchema, err = localMicroServiceDs.ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceIdPro1,
+			SchemaId:  schemas[0].SchemaId,
+			Summary:   schemas[0].Summary,
+			Schema:    schemas[0].SchemaId,
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respModifySchema.Response.GetCode())
+	})
+}
+
+func TestSchema_Exist(t *testing.T) {
+	ms.Install("etcd", func(opts ms.Options) (ms.DataSource, error) {
+		return etcd.NewDataSource(), nil
+	})
+	err := ms.Init(ms.Options{
+		Endpoint:       "",
+		PluginImplName: ms.ImplName(archaius.GetString("servicecomb.ms.name", "etcd")),
+	})
+	assert.NoError(t, err)
+
+	var (
+		serviceId string
+	)
+
+	t.Run("register service and add schema", func(t *testing.T) {
+		log.Info("register service")
+		respCreateService, err := ms.MicroService().RegisterService(getContext(), &pb.CreateServiceRequest{
+			Service: &pb.MicroService{
+				AppId:       "query_schema_group_ms",
+				ServiceName: "query_schema_service_ms",
+				Version:     "1.0.0",
+				Level:       "FRONT",
+				Status:      pb.MS_UP,
+				Environment: pb.ENV_DEV,
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, respCreateService.Response.GetCode())
+		serviceId = respCreateService.ServiceId
+
+		log.Info("add schemas, should pass")
+		resp, err := ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceId,
+			SchemaId:  "com.huawei.test.ms",
+			Schema:    "query schema ms",
+			Summary:   "summary_ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+
+		resp, err = ms.MicroService().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			ServiceId: serviceId,
+			SchemaId:  "com.huawei.test.no.summary.ms",
+			Schema:    "query schema ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+	})
+
+	t.Run("check exists", func(t *testing.T) {
+		log.Info("check schema exist, should pass")
+		resp, err := ms.MicroService().ExistSchema(getContext(), &pb.GetExistenceRequest{
+			Type:      service.ExistTypeSchema,
+			ServiceId: serviceId,
+			SchemaId:  "com.huawei.test.ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+		assert.Equal(t, "summary_ms", resp.Summary)
+
+		resp, err = ms.MicroService().ExistSchema(getContext(), &pb.GetExistenceRequest{
+			Type:        service.ExistTypeSchema,
+			ServiceId:   serviceId,
+			SchemaId:    "com.huawei.test.ms",
+			AppId:       "()",
+			ServiceName: "",
+			Version:     "()",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+
+		resp, err = ms.MicroService().ExistSchema(getContext(), &pb.GetExistenceRequest{
+			Type:      service.ExistTypeSchema,
+			ServiceId: serviceId,
+			SchemaId:  "com.huawei.test.no.summary.ms",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, proto.Response_SUCCESS, resp.Response.GetCode())
+		assert.Equal(t, "com.huawei.test.no.summary.ms", resp.SchemaId)
+		assert.Equal(t, "", resp.Summary)
+	})
+}
diff --git a/server/service/ms/etcd/util.go b/server/service/ms/etcd/util.go
index a0d6011..be422e4 100644
--- a/server/service/ms/etcd/util.go
+++ b/server/service/ms/etcd/util.go
@@ -31,10 +31,12 @@ import (
 	scerr "github.com/apache/servicecomb-service-center/server/scerror"
 	serviceUtil "github.com/apache/servicecomb-service-center/server/service/util"
 	"strconv"
+	"strings"
 	"time"
 )
 
-func GetSingleService(ctx context.Context, domainProject string, serviceID string) (*pb.MicroService, error) {
+// service
+func getSingleService(ctx context.Context, domainProject string, serviceID string) (*pb.MicroService, error) {
 	key := apt.GenerateServiceKey(domainProject, serviceID)
 	opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key))
 	serviceResp, err := backend.Store().Service().Search(ctx, opts...)
@@ -47,15 +49,15 @@ func GetSingleService(ctx context.Context, domainProject string, serviceID strin
 	return serviceResp.Kvs[0].Value.(*pb.MicroService), nil
 }
 
-func GetAllServiceUtil(ctx context.Context, domainProject string) ([]*pb.MicroService, error) {
-	services, err := GetServicesByDomainProject(ctx, domainProject)
+func getAllServiceUtil(ctx context.Context, domainProject string) ([]*pb.MicroService, error) {
+	services, err := getServicesByDomainProject(ctx, domainProject)
 	if err != nil {
 		return nil, err
 	}
 	return services, nil
 }
 
-func GetServicesByDomainProject(ctx context.Context, domainProject string) ([]*pb.MicroService, error) {
+func getServicesByDomainProject(ctx context.Context, domainProject string) ([]*pb.MicroService, error) {
 	kvs, err := getServicesRawData(ctx, domainProject)
 	if err != nil {
 		return nil, err
@@ -79,7 +81,7 @@ func getServicesRawData(ctx context.Context, domainProject string) ([]*discovery
 	return resp.Kvs, err
 }
 
-func CapRegisterData(ctx context.Context, request *pb.CreateServiceRequest) (
+func capRegisterData(ctx context.Context, request *pb.CreateServiceRequest) (
 	[]registry.PluginOp, []registry.CompareOp, []registry.PluginOp, error) {
 	remoteIP := util.GetIPFromContext(ctx)
 	serviceBody := request.Service
@@ -140,7 +142,7 @@ func CapRegisterData(ctx context.Context, request *pb.CreateServiceRequest) (
 	return opts, uniqueCmpOpts, failOpts, nil
 }
 
-func NewRegisterServiceResp(ctx context.Context, reqService *pb.MicroService, resp *registry.PluginResponse,
+func newRegisterServiceResp(ctx context.Context, reqService *pb.MicroService, resp *registry.PluginResponse,
 	err error) (*pb.CreateServiceResponse, error) {
 	remoteIP := util.GetIPFromContext(ctx)
 	serviceFlag := util.StringJoin([]string{
@@ -192,3 +194,295 @@ func NewRegisterServiceResp(ctx context.Context, reqService *pb.MicroService, re
 		ServiceId: reqService.ServiceId,
 	}, nil
 }
+
+func serviceExist(ctx context.Context, domainProject string, serviceID string) bool {
+	opts := append(serviceUtil.FromContext(ctx),
+		registry.WithStrKey(apt.GenerateServiceKey(domainProject, serviceID)),
+		registry.WithCountOnly())
+	resp, err := backend.Store().Service().Search(ctx, opts...)
+	if err != nil || resp.Count == 0 {
+		return false
+	}
+	return true
+}
+
+func findServiceIds(ctx context.Context, versionRule string, key *pb.MicroServiceKey) ([]string, bool, error) {
+	// 版本规则
+	match := serviceUtil.ParseVersionRule(versionRule)
+	if match == nil {
+		copyData := *key
+		copyData.Version = versionRule
+		serviceID, err := getServiceID(ctx, &copyData)
+		if err != nil {
+			return nil, false, err
+		}
+		if len(serviceID) > 0 {
+			return []string{serviceID}, true, nil
+		}
+		return nil, false, nil
+	}
+
+	searchAlias := false
+	alsoFindAlias := len(key.Alias) > 0
+
+FindRule:
+	resp, err := getServiceAllVersions(ctx, key, searchAlias)
+	if err != nil {
+		return nil, false, err
+	}
+	if len(resp.Kvs) == 0 {
+		if !alsoFindAlias {
+			return nil, false, nil
+		}
+		searchAlias = true
+		alsoFindAlias = false
+		goto FindRule
+	}
+	return match(resp.Kvs), true, nil
+}
+
+func getServiceID(ctx context.Context, key *pb.MicroServiceKey) (serviceID string, err error) {
+	serviceID, err = searchServiceID(ctx, key)
+	if err != nil {
+		return
+	}
+	if len(serviceID) == 0 {
+		// 别名查询
+		log.Debugf("could not search microservice[%s/%s/%s/%s] id by 'serviceName', now try 'alias'",
+			key.Environment, key.AppId, key.ServiceName, key.Version)
+		return searchServiceIDFromAlias(ctx, key)
+	}
+	return
+}
+
+func searchServiceID(ctx context.Context, key *pb.MicroServiceKey) (string, error) {
+	opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(apt.GenerateServiceIndexKey(key)))
+	resp, err := backend.Store().ServiceIndex().Search(ctx, opts...)
+	if err != nil {
+		return "", err
+	}
+	if len(resp.Kvs) == 0 {
+		return "", nil
+	}
+	return resp.Kvs[0].Value.(string), nil
+}
+
+func searchServiceIDFromAlias(ctx context.Context, key *pb.MicroServiceKey) (string, error) {
+	opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(apt.GenerateServiceAliasKey(key)))
+	resp, err := backend.Store().ServiceAlias().Search(ctx, opts...)
+	if err != nil {
+		return "", err
+	}
+	if len(resp.Kvs) == 0 {
+		return "", nil
+	}
+	return resp.Kvs[0].Value.(string), nil
+}
+
+func getServiceAllVersions(ctx context.Context, key *pb.MicroServiceKey, alias bool) (*discovery.Response, error) {
+	copyData := *key
+	copyData.Version = ""
+	var (
+		prefix  string
+		indexer discovery.Indexer
+	)
+	if alias {
+		prefix = apt.GenerateServiceAliasKey(&copyData)
+		indexer = backend.Store().ServiceAlias()
+	} else {
+		prefix = apt.GenerateServiceIndexKey(&copyData)
+		indexer = backend.Store().ServiceIndex()
+	}
+	opts := append(serviceUtil.FromContext(ctx),
+		registry.WithStrKey(prefix),
+		registry.WithPrefix(),
+		registry.WithDescendOrder())
+	resp, err := indexer.Search(ctx, opts...)
+	return resp, err
+}
+
+func updateService(domainProject string, serviceID string, service *pb.MicroService) (opt registry.PluginOp,
+	err error) {
+	opt = registry.PluginOp{}
+	key := apt.GenerateServiceKey(domainProject, serviceID)
+	data, err := json.Marshal(service)
+	if err != nil {
+		log.Errorf(err, "marshal service file failed")
+		return
+	}
+	opt = registry.OpPut(registry.WithStrKey(key), registry.WithValue(data))
+	return
+}
+
+// schema
+func getSchemaSummary(ctx context.Context, domainProject string, serviceID string, schemaID string) (string, error) {
+	key := apt.GenerateServiceSchemaSummaryKey(domainProject, serviceID, schemaID)
+	resp, err := backend.Store().SchemaSummary().Search(ctx,
+		registry.WithStrKey(key),
+	)
+	if err != nil {
+		log.Errorf(err, "get schema[%s/%s] summary failed", serviceID, schemaID)
+		return "", err
+	}
+	if len(resp.Kvs) == 0 {
+		return "", nil
+	}
+	return resp.Kvs[0].Value.(string), nil
+}
+
+func getService(ctx context.Context, domainProject string, serviceID string) (*pb.MicroService, error) {
+	key := apt.GenerateServiceKey(domainProject, serviceID)
+	opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key))
+	serviceResp, err := backend.Store().Service().Search(ctx, opts...)
+	if err != nil {
+		return nil, err
+	}
+	if len(serviceResp.Kvs) == 0 {
+		return nil, nil
+	}
+	return serviceResp.Kvs[0].Value.(*pb.MicroService), nil
+}
+
+func getSchemasFromDatabase(ctx context.Context, domainProject string, serviceID string) ([]*pb.Schema, error) {
+	key := apt.GenerateServiceSchemaKey(domainProject, serviceID, "")
+	resp, err := backend.Store().Schema().Search(ctx,
+		registry.WithPrefix(),
+		registry.WithStrKey(key))
+	if err != nil {
+		log.Errorf(err, "get service[%s]'s schema failed", serviceID)
+		return nil, err
+	}
+	schemas := make([]*pb.Schema, 0, len(resp.Kvs))
+	for _, kv := range resp.Kvs {
+		key := util.BytesToStringWithNoCopy(kv.Key)
+		tmp := strings.Split(key, "/")
+		schemaID := tmp[len(tmp)-1]
+		schema := util.BytesToStringWithNoCopy(kv.Value.([]byte))
+		schemaStruct := &pb.Schema{
+			SchemaId: schemaID,
+			Schema:   schema,
+		}
+		schemas = append(schemas, schemaStruct)
+	}
+	return schemas, nil
+}
+
+func schemasAnalysis(schemas []*pb.Schema, schemasFromDb []*pb.Schema, schemaIDsInService []string) (
+	[]*pb.Schema, []*pb.Schema, []*pb.Schema, []string) {
+	needUpdateSchemas := make([]*pb.Schema, 0, len(schemas))
+	needAddSchemas := make([]*pb.Schema, 0, len(schemas))
+	needDeleteSchemas := make([]*pb.Schema, 0, len(schemasFromDb))
+	nonExistSchemaIds := make([]string, 0, len(schemas))
+
+	duplicate := make(map[string]struct{})
+	for _, schema := range schemas {
+		if _, ok := duplicate[schema.SchemaId]; ok {
+			continue
+		}
+		duplicate[schema.SchemaId] = struct{}{}
+
+		exist := false
+		for _, schemaFromDb := range schemasFromDb {
+			if schema.SchemaId == schemaFromDb.SchemaId {
+				needUpdateSchemas = append(needUpdateSchemas, schema)
+				exist = true
+				break
+			}
+		}
+		if !exist {
+			needAddSchemas = append(needAddSchemas, schema)
+		}
+
+		exist = false
+		for _, schemaID := range schemaIDsInService {
+			if schema.SchemaId == schemaID {
+				exist = true
+			}
+		}
+		if !exist {
+			nonExistSchemaIds = append(nonExistSchemaIds, schema.SchemaId)
+		}
+	}
+
+	for _, schemaFromDb := range schemasFromDb {
+		exist := false
+		for _, schema := range schemas {
+			if schema.SchemaId == schemaFromDb.SchemaId {
+				exist = true
+				break
+			}
+		}
+		if !exist {
+			needDeleteSchemas = append(needDeleteSchemas, schemaFromDb)
+		}
+	}
+
+	return needUpdateSchemas, needAddSchemas, needDeleteSchemas, nonExistSchemaIds
+}
+
+func checkSchemaInfoExist(ctx context.Context, key string) (bool, error) {
+	opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key), registry.WithCountOnly())
+	resp, errDo := backend.Store().Schema().Search(ctx, opts...)
+	if errDo != nil {
+		return false, errDo
+	}
+	if resp.Count == 0 {
+		return false, nil
+	}
+	return true, nil
+}
+
+func isExistSchemaSummary(ctx context.Context, domainProject, serviceID, schemaID string) (bool, error) {
+	key := apt.GenerateServiceSchemaSummaryKey(domainProject, serviceID, schemaID)
+	resp, err := backend.Store().SchemaSummary().Search(ctx, registry.WithStrKey(key), registry.WithCountOnly())
+	if err != nil {
+		return true, err
+	}
+	if resp.Count == 0 {
+		return false, nil
+	}
+	return true, nil
+}
+
+func schemaWithDatabaseOpera(invoke registry.Operation, domainProject string, serviceID string, schema *pb.Schema) []registry.PluginOp {
+	pluginOps := make([]registry.PluginOp, 0)
+	key := apt.GenerateServiceSchemaKey(domainProject, serviceID, schema.SchemaId)
+	opt := invoke(registry.WithStrKey(key), registry.WithStrValue(schema.Schema))
+	pluginOps = append(pluginOps, opt)
+	keySummary := apt.GenerateServiceSchemaSummaryKey(domainProject, serviceID, schema.SchemaId)
+	opt = invoke(registry.WithStrKey(keySummary), registry.WithStrValue(schema.Summary))
+	pluginOps = append(pluginOps, opt)
+	return pluginOps
+}
+
+func isExistSchemaID(service *pb.MicroService, schemas []*pb.Schema) bool {
+	serviceSchemaIds := service.Schemas
+	for _, schema := range schemas {
+		if !containsValueInSlice(serviceSchemaIds, schema.SchemaId) {
+			log.Errorf(nil, "schema[%s/%s] does not exist schemaID", service.ServiceId, schema.SchemaId)
+			return false
+		}
+	}
+	return true
+}
+
+func containsValueInSlice(in []string, value string) bool {
+	if in == nil || len(value) == 0 {
+		return false
+	}
+	for _, i := range in {
+		if i == value {
+			return true
+		}
+	}
+	return false
+}
+
+func commitSchemaInfo(domainProject string, serviceID string, schema *pb.Schema) []registry.PluginOp {
+	if len(schema.Summary) != 0 {
+		return schemaWithDatabaseOpera(registry.OpPut, domainProject, serviceID, schema)
+	}
+	key := apt.GenerateServiceSchemaKey(domainProject, serviceID, schema.SchemaId)
+	opt := registry.OpPut(registry.WithStrKey(key), registry.WithStrValue(schema.Schema))
+	return []registry.PluginOp{opt}
+}