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 504c529   [SCB-2094] add schema/rule/service ut (#779)
504c529 is described below

commit 504c5297a7098fad07158ad6386795508aba0440
Author: panqian <[email protected]>
AuthorDate: Tue Dec 15 10:54:39 2020 +0800

     [SCB-2094] add schema/rule/service ut (#779)
---
 .github/workflows/golangci-lint.yml   |    2 +-
 datasource/etcd/util.go               |   59 +-
 datasource/mongo/client/errortypes.go |   14 +-
 datasource/mongo/client/mongo.go      |   25 +-
 datasource/mongo/mongo.go             |   32 +-
 datasource/mongo/ms.go                |  698 +++++++----
 datasource/mongo/ms_test.go           | 2228 ++++++++++++++++++++++++++++-----
 datasource/mongo/util.go              |   71 +-
 datasource/ms_util.go                 |   71 +-
 9 files changed, 2589 insertions(+), 611 deletions(-)

diff --git a/.github/workflows/golangci-lint.yml 
b/.github/workflows/golangci-lint.yml
index f7041e5..a8963a9 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -10,4 +10,4 @@ jobs:
         uses: golangci/golangci-lint-action@v2
         with:
           version: v1.29
-          args: --timeout=5m 
--skip-dirs='test,pkg/registry,.*/controller/(v3|v4)$,.*/bootstrap$,server/broker/brokerpb,examples,frontend,scctl,integration'
 --out-format=colored-line-number --enable golint,gocyclo,goimports 
--skip-files=.*_test.go$
\ No newline at end of file
+          args: --timeout=5m 
--skip-dirs='test,.*/controller/(v3|v4)$,.*/bootstrap$,server/broker,examples,frontend,scctl,integration'
 --out-format=colored-line-number --enable golint,gocyclo,goimports 
--skip-files=.*_test.go$
\ No newline at end of file
diff --git a/datasource/etcd/util.go b/datasource/etcd/util.go
index 007869a..4b21e9d 100644
--- a/datasource/etcd/util.go
+++ b/datasource/etcd/util.go
@@ -23,6 +23,8 @@ import (
        "strings"
        "time"
 
+       "github.com/apache/servicecomb-service-center/datasource"
+
        "github.com/apache/servicecomb-service-center/datasource/etcd/client"
        "github.com/apache/servicecomb-service-center/datasource/etcd/kv"
        "github.com/apache/servicecomb-service-center/datasource/etcd/path"
@@ -44,11 +46,6 @@ type ServiceDetailOpt struct {
        options       []string
 }
 
-type GetInstanceCountByDomainResponse struct {
-       err           error
-       countByDomain int64
-}
-
 // schema
 func getSchemaSummary(ctx context.Context, domainProject string, serviceID 
string, schemaID string) (string, error) {
        key := path.GenerateServiceSchemaSummaryKey(domainProject, serviceID, 
schemaID)
@@ -386,30 +383,17 @@ func statistics(ctx context.Context, withShared bool) 
(*pb.Statistics, error) {
                return nil, err
        }
 
-       app := make(map[string]struct{}, respSvc.Count)
-       svcWithNonVersion := make(map[string]struct{}, respSvc.Count)
-       svcIDToNonVerKey := make(map[string]string, respSvc.Count)
+       var svcIDs []string
+       var svcKeys []*pb.MicroServiceKey
        for _, keyValue := range respSvc.Kvs {
                key := path.GetInfoFromSvcIndexKV(keyValue.Key)
-               if !withShared && core.IsGlobal(key) {
-                       continue
-               }
-               if _, ok := app[key.AppId]; !ok {
-                       app[key.AppId] = struct{}{}
-               }
-
-               key.Version = ""
-               svcWithNonVersionKey := path.GenerateServiceIndexKey(key)
-               if _, ok := svcWithNonVersion[svcWithNonVersionKey]; !ok {
-                       svcWithNonVersion[svcWithNonVersionKey] = struct{}{}
-               }
-               svcIDToNonVerKey[keyValue.Value.(string)] = svcWithNonVersionKey
+               svcKeys = append(svcKeys, key)
+               svcIDs = append(svcIDs, keyValue.Value.(string))
        }
 
-       result.Services.Count = int64(len(svcWithNonVersion))
-       result.Apps.Count = int64(len(app))
+       svcIDToNonVerKey := datasource.SetStaticServices(result, svcKeys, 
svcIDs, withShared)
 
-       respGetInstanceCountByDomain := make(chan 
GetInstanceCountByDomainResponse, 1)
+       respGetInstanceCountByDomain := make(chan 
datasource.GetInstanceCountByDomainResponse, 1)
        gopool.Go(func(_ context.Context) {
                getInstanceCountByDomain(ctx, svcIDToNonVerKey, 
respGetInstanceCountByDomain)
        })
@@ -425,30 +409,23 @@ func statistics(ctx context.Context, withShared bool) 
(*pb.Statistics, error) {
                return nil, err
        }
 
-       onlineServices := make(map[string]struct{}, respSvc.Count)
+       var instIDs []string
        for _, keyValue := range respIns.Kvs {
                serviceID, _, _ := path.GetInfoFromInstKV(keyValue.Key)
-               key, ok := svcIDToNonVerKey[serviceID]
-               if !ok {
-                       continue
-               }
-               result.Instances.Count++
-               if _, ok := onlineServices[key]; !ok {
-                       onlineServices[key] = struct{}{}
-               }
+               instIDs = append(instIDs, serviceID)
        }
-       result.Services.OnlineCount = int64(len(onlineServices))
+       datasource.SetStaticInstances(result, svcIDToNonVerKey, instIDs)
 
        data := <-respGetInstanceCountByDomain
        close(respGetInstanceCountByDomain)
-       if data.err != nil {
-               return nil, data.err
+       if data.Err != nil {
+               return nil, data.Err
        }
-       result.Instances.CountByDomain = data.countByDomain
+       result.Instances.CountByDomain = data.CountByDomain
        return result, nil
 }
 
-func getInstanceCountByDomain(ctx context.Context, svcIDToNonVerKey 
map[string]string, resp chan GetInstanceCountByDomainResponse) {
+func getInstanceCountByDomain(ctx context.Context, svcIDToNonVerKey 
map[string]string, resp chan datasource.GetInstanceCountByDomainResponse) {
        domainID := util.ParseDomain(ctx)
        key := path.GetInstanceRootKey(domainID) + "/"
        instOpts := append([]client.PluginOpOption{},
@@ -456,8 +433,8 @@ func getInstanceCountByDomain(ctx context.Context, 
svcIDToNonVerKey map[string]s
                client.WithPrefix(),
                client.WithKeyOnly())
        respIns, err := kv.Store().Instance().Search(ctx, instOpts...)
-       ret := GetInstanceCountByDomainResponse{
-               err: err,
+       ret := datasource.GetInstanceCountByDomainResponse{
+               Err: err,
        }
 
        if err != nil {
@@ -469,7 +446,7 @@ func getInstanceCountByDomain(ctx context.Context, 
svcIDToNonVerKey map[string]s
                        if !ok {
                                continue
                        }
-                       ret.countByDomain++
+                       ret.CountByDomain++
                }
        }
 
diff --git a/datasource/mongo/client/errortypes.go 
b/datasource/mongo/client/errortypes.go
index c70ffbb..9d7311a 100644
--- a/datasource/mongo/client/errortypes.go
+++ b/datasource/mongo/client/errortypes.go
@@ -15,7 +15,15 @@
 
 package client
 
-import "go.mongodb.org/mongo-driver/mongo"
+import (
+       "errors"
+
+       "go.mongodb.org/mongo-driver/mongo"
+)
+
+var (
+       ErrNoDocuments = errors.New("no doc found")
+)
 
 const (
        DuplicateKey = 11000
@@ -34,3 +42,7 @@ func IsDuplicateKey(err error) bool {
        }
        return false
 }
+
+func IsNoneDoc(err error) bool {
+       return err == ErrNoDocuments
+}
diff --git a/datasource/mongo/client/mongo.go b/datasource/mongo/client/mongo.go
index 4be189a..aac7be7 100644
--- a/datasource/mongo/client/mongo.go
+++ b/datasource/mongo/client/mongo.go
@@ -48,6 +48,11 @@ type MongoClient struct {
        goroutine *gopool.Pool
 }
 
+type MongoOperation struct {
+       Table  string
+       Models []mongo.WriteModel
+}
+
 func GetMongoClient() *MongoClient {
        return mc
 }
@@ -135,6 +140,14 @@ func (mc *MongoClient) newClient(ctx context.Context) (err 
error) {
        return nil
 }
 
+func (mc *MongoClient) CreateIndexes(ctx context.Context, Table string, 
indexes []mongo.IndexModel) error {
+       _, err := mc.db.Collection(Table).Indexes().CreateMany(ctx, indexes)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
 func (mc *MongoClient) Insert(ctx context.Context, Table string, document 
interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) 
{
        return mc.db.Collection(Table).InsertOne(ctx, document, opts...)
 }
@@ -187,6 +200,16 @@ func (mc *MongoClient) StartSession(ctx context.Context) 
(mongo.Session, error)
        return mc.client.StartSession()
 }
 
+func (mc *MongoClient) MultiTableBatchUpdate(ctx context.Context, opts 
[]MongoOperation) error {
+       for _, op := range opts {
+               _, err := mc.BatchUpdate(ctx, op.Table, op.Models)
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
 func (mc *MongoClient) DocExist(ctx context.Context, table string, filter 
bson.M) (bool, error) {
        res, err := mc.FindOne(ctx, table, filter)
        if err != nil {
@@ -205,7 +228,7 @@ func (mc *MongoClient) DocUpdate(ctx context.Context, table 
string, filter inter
        }
        if res.Err() != nil {
                // means no doc find, if the operation is update,should return 
err
-               return res.Err()
+               return ErrNoDocuments
        }
        return nil
 }
diff --git a/datasource/mongo/mongo.go b/datasource/mongo/mongo.go
index 0dc6870..fb607cc 100644
--- a/datasource/mongo/mongo.go
+++ b/datasource/mongo/mongo.go
@@ -18,12 +18,17 @@
 package mongo
 
 import (
+       "context"
+
        "github.com/apache/servicecomb-service-center/datasource"
        "github.com/apache/servicecomb-service-center/datasource/mongo/client"
        
"github.com/apache/servicecomb-service-center/datasource/mongo/heartbeat"
        "github.com/apache/servicecomb-service-center/pkg/log"
        "github.com/apache/servicecomb-service-center/server/config"
        "github.com/go-chassis/go-chassis/v2/storage"
+       "go.mongodb.org/mongo-driver/mongo"
+       "go.mongodb.org/mongo-driver/mongo/options"
+       "go.mongodb.org/mongo-driver/x/bsonx"
 )
 
 func init() {
@@ -39,7 +44,6 @@ type DataSource struct {
 
 func NewDataSource(opts datasource.Options) (datasource.DataSource, error) {
        // TODO: construct a reasonable DataSource instance
-       log.Warn("data source enable mongo mode")
 
        inst := &DataSource{
                SchemaEditable: opts.SchemaEditable,
@@ -64,6 +68,10 @@ func (ds *DataSource) initialize() error {
        if err != nil {
                return err
        }
+       err = ds.createIndexes()
+       if err != nil {
+               return err
+       }
        return nil
 }
 
@@ -88,3 +96,25 @@ func (ds *DataSource) initClient() error {
                return nil
        }
 }
+
+//{Key: StringBuilder([]string{ColumnServiceInfo, ColumnAlias}), Value: 
bsonx.Int32(1)}
+func (ds *DataSource) createIndexes() (err error) {
+       err = client.GetMongoClient().CreateIndexes(context.TODO(), 
CollectionService, []mongo.IndexModel{{
+               Keys:    bsonx.Doc{{Key: 
StringBuilder([]string{ColumnServiceInfo, ColumnServiceID}), Value: 
bsonx.Int32(1)}},
+               Options: options.Index().SetUnique(true),
+       }, {
+               Keys:    bsonx.Doc{{Key: 
StringBuilder([]string{ColumnServiceInfo, ColumnAppID}), Value: 
bsonx.Int32(1)}, {Key: StringBuilder([]string{ColumnServiceInfo, 
ColumnServiceName}), Value: bsonx.Int32(1)}, {Key: 
StringBuilder([]string{ColumnServiceInfo, ColumnEnv}), Value: bsonx.Int32(1)}, 
{Key: StringBuilder([]string{ColumnServiceInfo, ColumnVersion}), Value: 
bsonx.Int32(1)}},
+               Options: options.Index().SetUnique(true),
+       }})
+       if err != nil {
+               return
+       }
+       err = client.GetMongoClient().CreateIndexes(context.TODO(), 
CollectionInstance, []mongo.IndexModel{{
+               Keys:    bsonx.Doc{{Key: 
StringBuilder([]string{ColumnInstanceInfo, ColumnInstanceID}), Value: 
bsonx.Int32(1)}},
+               Options: options.Index().SetUnique(true),
+       }, {Keys: bsonx.Doc{{Key: StringBuilder([]string{ColumnInstanceID, 
ColumnServiceID}), Value: bsonx.Int32(1)}}}})
+       if err != nil {
+               return
+       }
+       return
+}
diff --git a/datasource/mongo/ms.go b/datasource/mongo/ms.go
index 106cfca..3ecc494 100644
--- a/datasource/mongo/ms.go
+++ b/datasource/mongo/ms.go
@@ -27,6 +27,9 @@ import (
        "strings"
        "time"
 
+       "github.com/apache/servicecomb-service-center/server/plugin/quota"
+       "go.mongodb.org/mongo-driver/mongo"
+
        "github.com/go-chassis/cari/discovery"
        "go.mongodb.org/mongo-driver/bson"
        "go.mongodb.org/mongo-driver/mongo/options"
@@ -44,9 +47,11 @@ import (
 func (ds *DataSource) RegisterService(ctx context.Context, request 
*discovery.CreateServiceRequest) (
        *discovery.CreateServiceResponse, error) {
        service := request.Service
-
+       remoteIP := util.GetIPFromContext(ctx)
        domain := util.ParseDomain(ctx)
        project := util.ParseProject(ctx)
+       serviceFlag := util.StringJoin([]string{
+               service.Environment, service.AppId, service.ServiceName, 
service.Version}, "/")
        //todo add quota check
        requestServiceID := service.ServiceId
 
@@ -54,45 +59,48 @@ func (ds *DataSource) RegisterService(ctx context.Context, 
request *discovery.Cr
                ctx = util.SetContext(ctx, uuid.ContextKey, 
util.StringJoin([]string{domain, project, service.Environment, service.AppId, 
service.ServiceName, service.Alias, service.Version}, "/"))
                service.ServiceId = uuid.Generator().GetServiceID(ctx)
        }
+       service.Timestamp = strconv.FormatInt(time.Now().Unix(), 10)
+       service.ModTimestamp = service.Timestamp
        // the service unique index in table is 
(serviceId,serviceEnv,serviceAppid,servicename,serviceAlias,serviceVersion)
-       existID, err := ServiceExistID(ctx, service.ServiceId)
-       if err != nil {
-               return &discovery.CreateServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Check service exist failed"),
-               }, err
-       }
-       exist, err := ServiceExist(ctx, &discovery.MicroServiceKey{
-               Environment: service.Environment,
-               AppId:       service.AppId,
-               ServiceName: service.ServiceName,
-               Alias:       service.Alias,
-               Version:     service.Version,
-       })
-       if err != nil {
-               return &discovery.CreateServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Check service exist failed"),
-               }, err
-       }
-       if existID || exist {
-               return &discovery.CreateServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrServiceAlreadyExists, "ServiceID conflict 
or found the same service."),
-               }, nil
-       }
        insertRes, err := client.GetMongoClient().Insert(ctx, 
CollectionService, &Service{Domain: domain, Project: project, ServiceInfo: 
service})
        if err != nil {
                if client.IsDuplicateKey(err) {
+                       if len(requestServiceID) == 0 {
+                               serviceIDInner, err := GetServiceID(ctx, 
&discovery.MicroServiceKey{
+                                       Environment: service.Environment,
+                                       AppId:       service.AppId,
+                                       ServiceName: service.ServiceName,
+                                       Version:     service.Version,
+                               })
+                               if err != nil {
+                                       return &discovery.CreateServiceResponse{
+                                               Response: 
discovery.CreateResponse(discovery.ErrUnavailableBackend, err.Error()),
+                                       }, err
+                               }
+                               if len(serviceIDInner) != 0 {
+                                       log.Warn(fmt.Sprintf("create 
micro-service[%s][%s] failed, service already exists, operator: %s",
+                                               serviceIDInner, serviceFlag, 
remoteIP))
+                                       return &discovery.CreateServiceResponse{
+                                               Response:  
discovery.CreateResponse(discovery.ResponseSuccess, "register service 
successfully"),
+                                               ServiceId: serviceIDInner,
+                                       }, nil
+                               }
+                       }
+                       log.Warn(fmt.Sprintf("create micro-service[%s] failed, 
service already exists, operator: %s",
+                               serviceFlag, remoteIP))
                        return &discovery.CreateServiceResponse{
-                               Response: 
discovery.CreateResponse(discovery.ErrServiceAlreadyExists, "ServiceID or 
ServiceInfo conflict."),
+                               Response: 
discovery.CreateResponse(discovery.ErrServiceAlreadyExists,
+                                       "ServiceID conflict or found the same 
service with different id."),
                        }, nil
                }
+               log.Error(fmt.Sprintf("create micro-service[%s] failed, service 
already exists, operator: %s",
+                       serviceFlag, remoteIP), err)
                return &discovery.CreateServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Register service failed."),
+                       Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
                }, err
        }
 
-       remoteIP := util.GetIPFromContext(ctx)
-       log.Info(fmt.Sprintf("create micro-service[%s][%s] 
successfully,operator: %s",
-               service.ServiceId, insertRes.InsertedID, remoteIP))
+       log.Info(fmt.Sprintf("create micro-service[%s][%s] 
successfully,operator: %s", service.ServiceId, insertRes.InsertedID, remoteIP))
 
        return &discovery.CreateServiceResponse{
                Response:  discovery.CreateResponse(discovery.ResponseSuccess, 
"Register service successfully"),
@@ -139,7 +147,7 @@ func (ds *DataSource) GetApplications(ctx context.Context, 
request *discovery.Ge
        l := len(services)
        if l == 0 {
                return &discovery.GetAppsResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "get services data failed."),
+                       Response: 
discovery.CreateResponse(discovery.ResponseSuccess, "Get all applications 
successfully."),
                }, nil
        }
        apps := make([]string, 0, l)
@@ -235,54 +243,18 @@ func (ds *DataSource) ExistService(ctx context.Context, 
request *discovery.GetEx
 }
 
 func (ds *DataSource) UnregisterService(ctx context.Context, request 
*discovery.DeleteServiceRequest) (*discovery.DeleteServiceResponse, error) {
-       exist, err := ServiceExistID(ctx, request.ServiceId)
+       res, err := ds.DelServicePri(ctx, request.ServiceId, request.Force)
        if err != nil {
                return &discovery.DeleteServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Delete service failed,failed 
to get service."),
-               }, err
-       }
-       if !exist {
-               return &discovery.DeleteServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "Delete service 
failed,service not exist."),
-               }, nil
-       }
-       session, err := client.GetMongoClient().StartSession(ctx)
-       if err != nil {
-               return &discovery.DeleteServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "DelService failed to create 
session."),
-               }, err
-       }
-       if err = session.StartTransaction(); err != nil {
-               return &discovery.DeleteServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "DelService failed to start 
session."),
-               }, err
-       }
-       defer session.EndSession(ctx)
-       //todo delete instance,tags,schemas...
-       res, err := DelServicePri(ctx, request.ServiceId, request.Force)
-       if err != nil {
-               errAbort := session.AbortTransaction(ctx)
-               if errAbort != nil {
-                       return &discovery.DeleteServiceResponse{
-                               Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn delete service abort 
failed."),
-                       }, errAbort
-               }
-               return &discovery.DeleteServiceResponse{
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, "Delete service failed"),
                }, err
        }
-       errCommit := session.CommitTransaction(ctx)
-       if errCommit != nil {
-               return &discovery.DeleteServiceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn delete service commit 
failed."),
-               }, errCommit
-       }
        return &discovery.DeleteServiceResponse{
                Response: res,
        }, nil
 }
 
-func DelServicePri(ctx context.Context, serviceID string, force bool) 
(*discovery.Response, error) {
+func (ds *DataSource) DelServicePri(ctx context.Context, serviceID string, 
force bool) (*discovery.Response, error) {
        remoteIP := util.GetIPFromContext(ctx)
        title := "delete"
        if force {
@@ -306,42 +278,41 @@ func DelServicePri(ctx context.Context, serviceID string, 
force bool) (*discover
        }
        // 强制删除,则与该服务相关的信息删除,非强制删除: 
如果作为该被依赖(作为provider,提供服务,且不是只存在自依赖)或者存在实例,则不能删除
        if !force {
-               log.Info("force delete,should del instance...")
                //todo wait for dep interface
-       }
-       filter := GeneratorServiceFilter(ctx, serviceID)
-       //todo del instances
-       tables := []string{CollectionService, CollectionSchema, CollectionRule}
-       for _, col := range tables {
-               _, err := client.GetMongoClient().Delete(ctx, col, filter)
+               instancesExist, err := client.GetMongoClient().DocExist(ctx, 
CollectionInstance, bson.M{StringBuilder([]string{ColumnInstanceInfo, 
ColumnServiceID}): serviceID})
                if err != nil {
-                       return discovery.CreateResponse(discovery.ErrInternal, 
err.Error()), err
+                       log.Error(fmt.Sprintf("delete micro-service[%s] failed, 
get instances number failed, operator: %s",
+                               serviceID, remoteIP), err)
+                       return 
discovery.CreateResponse(discovery.ErrUnavailableBackend, err.Error()), err
                }
+               if instancesExist {
+                       log.Error(fmt.Sprintf("delete micro-service[%s] failed, 
service deployed instances, operator: %s",
+                               serviceID, remoteIP), nil)
+                       return 
discovery.CreateResponse(discovery.ErrDeployedInstance, "Can not delete the 
service deployed instance(s)."), err
+               }
+
        }
-       return discovery.CreateResponse(discovery.ResponseSuccess, "Unregister 
service successfully."), nil
+       //todo del dep
+       schemaOps := client.MongoOperation{Table: CollectionSchema, Models: 
[]mongo.WriteModel{mongo.NewDeleteManyModel().SetFilter(bson.M{ColumnServiceID: 
serviceID})}}
+       rulesOps := client.MongoOperation{Table: CollectionRule, Models: 
[]mongo.WriteModel{mongo.NewDeleteManyModel().SetFilter(bson.M{ColumnServiceID: 
serviceID})}}
+       instanceOps := client.MongoOperation{Table: CollectionInstance, Models: 
[]mongo.WriteModel{mongo.NewDeleteManyModel().SetFilter(bson.M{StringBuilder([]string{ColumnInstanceInfo,
 ColumnServiceID}): serviceID})}}
+       serviceOps := client.MongoOperation{Table: CollectionService, Models: 
[]mongo.WriteModel{mongo.NewDeleteOneModel().SetFilter(bson.M{StringBuilder([]string{ColumnServiceInfo,
 ColumnServiceID}): serviceID})}}
 
+       err = client.GetMongoClient().MultiTableBatchUpdate(ctx, 
[]client.MongoOperation{schemaOps, rulesOps, instanceOps, serviceOps})
+       if err != nil {
+               log.Error(fmt.Sprintf("micro-service[%s] failed, operator: %s", 
serviceID, remoteIP), err)
+               return 
discovery.CreateResponse(discovery.ErrUnavailableBackend, err.Error()), err
+       }
+       return discovery.CreateResponse(discovery.ResponseSuccess, "Unregister 
service successfully."), nil
 }
 
 func (ds *DataSource) UpdateService(ctx context.Context, request 
*discovery.UpdateServicePropsRequest) (
        *discovery.UpdateServicePropsResponse, error) {
-
-       exist, err := ServiceExistID(ctx, request.ServiceId)
-       if err != nil {
-               return &discovery.UpdateServicePropsResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "UpdateService failed,failed to 
get service."),
-               }, err
-       }
-       if !exist {
-               return &discovery.UpdateServicePropsResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "UpdateService 
failed,service not exist."),
-               }, nil
-       }
-
        updateData := bson.M{
                "$set": bson.M{
                        StringBuilder([]string{ColumnServiceInfo, 
ColumnModTime}):  strconv.FormatInt(time.Now().Unix(), 10),
                        StringBuilder([]string{ColumnServiceInfo, 
ColumnProperty}): request.Properties}}
-       err = UpdateService(ctx, GeneratorServiceFilter(ctx, 
request.ServiceId), updateData)
+       err := UpdateService(ctx, GeneratorServiceFilter(ctx, 
request.ServiceId), updateData)
        if err != nil {
                log.Error(fmt.Sprintf("update service %s properties failed, 
update mongo failed", request.ServiceId), err)
                return &discovery.UpdateServicePropsResponse{
@@ -355,7 +326,20 @@ func (ds *DataSource) UpdateService(ctx context.Context, 
request *discovery.Upda
 
 func (ds *DataSource) GetDeleteServiceFunc(ctx context.Context, serviceID 
string, force bool,
        serviceRespChan chan<- *discovery.DelServicesRspInfo) 
func(context.Context) {
-       return func(_ context.Context) {}
+       return func(_ context.Context) {
+               serviceRst := &discovery.DelServicesRspInfo{
+                       ServiceId:  serviceID,
+                       ErrMessage: "",
+               }
+               resp, err := ds.DelServicePri(ctx, serviceID, force)
+               if err != nil {
+                       serviceRst.ErrMessage = err.Error()
+               } else if resp.GetCode() != discovery.ResponseSuccess {
+                       serviceRst.ErrMessage = resp.GetMessage()
+               }
+
+               serviceRespChan <- serviceRst
+       }
 }
 
 func (ds *DataSource) GetServiceDetail(ctx context.Context, request 
*discovery.GetServiceRequest) (
@@ -372,7 +356,12 @@ func (ds *DataSource) GetServiceDetail(ctx 
context.Context, request *discovery.G
                }, nil
        }
        svc := mgSvc.ServiceInfo
-       versions, err := GetServicesVersions(ctx, bson.M{})
+       key := &discovery.MicroServiceKey{
+               Environment: svc.Environment,
+               AppId:       svc.AppId,
+               ServiceName: svc.ServiceName,
+       }
+       versions, err := GetServicesVersions(ctx, 
GeneratorServiceVersionsFilter(ctx, key))
        if err != nil {
                log.Error(fmt.Sprintf("get service %s %s %s all versions 
failed", svc.Environment, svc.AppId, svc.ServiceName), err)
                return &discovery.GetServiceDetailResponse{
@@ -411,7 +400,22 @@ func (ds *DataSource) GetServicesInfo(ctx context.Context, 
request *discovery.Ge
                        options = append(options, opt)
                }
        }
-       //todo add get statistics info
+       var st *discovery.Statistics
+       if _, ok := optionMap["statistics"]; ok {
+               var err error
+               st, err = statistics(ctx, request.WithShared)
+               if err != nil {
+                       return &discovery.GetServicesInfoResponse{
+                               Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+                       }, err
+               }
+               if len(optionMap) == 1 {
+                       return &discovery.GetServicesInfoResponse{
+                               Response:   
discovery.CreateResponse(discovery.ResponseSuccess, "Statistics successfully."),
+                               Statistics: st,
+                       }, nil
+               }
+       }
        services, err := GetMongoServices(ctx, bson.M{})
        if err != nil {
                log.Error("get all services by domain failed", err)
@@ -452,6 +456,7 @@ func (ds *DataSource) GetServicesInfo(ctx context.Context, 
request *discovery.Ge
 }
 
 func (ds *DataSource) AddTags(ctx context.Context, request 
*discovery.AddServiceTagsRequest) (*discovery.AddServiceTagsResponse, error) {
+       remoteIP := util.GetIPFromContext(ctx)
        service, err := GetService(ctx, GeneratorServiceFilter(ctx, 
request.ServiceId))
        if err != nil {
                log.Error(fmt.Sprintf("failed to add tags for service %s for 
get service failed", request.ServiceId), err)
@@ -462,9 +467,21 @@ func (ds *DataSource) AddTags(ctx context.Context, request 
*discovery.AddService
        if service == nil {
                return &discovery.AddServiceTagsResponse{Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "Service not exist")}, 
nil
        }
-       //todo add quto check
-       dataTags := service.Tags
        tags := request.Tags
+       res := quota.NewApplyQuotaResource(quota.TagQuotaType, 
util.ParseDomainProject(ctx), request.ServiceId, int64(len(tags)))
+       rst := quota.Apply(ctx, res)
+       errQuota := rst.Err
+       if errQuota != nil {
+               log.Error(fmt.Sprintf("add service[%s]'s tags %v failed, 
operator: %s", request.ServiceId, tags, remoteIP), errQuota)
+               response := &discovery.AddServiceTagsResponse{
+                       Response: discovery.CreateResponseWithSCErr(errQuota),
+               }
+               if errQuota.InternalError() {
+                       return response, errQuota
+               }
+               return response, nil
+       }
+       dataTags := service.Tags
        for key, value := range dataTags {
                if _, ok := tags[key]; ok {
                        continue
@@ -593,38 +610,64 @@ func (ds *DataSource) GetSchema(ctx context.Context, 
request *discovery.GetSchem
                        Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "GetSchema service does 
not exist."),
                }, nil
        }
-       Schema, err := GetSchema(ctx, GeneratorSchemaFilter(ctx, 
request.ServiceId, request.SchemaId))
+       schema, err := GetSchema(ctx, GeneratorSchemaFilter(ctx, 
request.ServiceId, request.SchemaId))
        if err != nil {
                return &discovery.GetSchemaResponse{
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, "GetSchema failed from 
mongodb."),
                }, nil
        }
+       if schema == nil {
+               return &discovery.GetSchemaResponse{
+                       Response: 
discovery.CreateResponse(discovery.ErrSchemaNotExists, "Do not have this schema 
info."),
+               }, nil
+       }
        return &discovery.GetSchemaResponse{
                Response:      
discovery.CreateResponse(discovery.ResponseSuccess, "Get schema info 
successfully."),
-               Schema:        Schema.SchemaInfo,
-               SchemaSummary: Schema.SchemaSummary,
+               Schema:        schema.SchemaInfo,
+               SchemaSummary: schema.SchemaSummary,
        }, nil
 }
 
 func (ds *DataSource) GetAllSchemas(ctx context.Context, request 
*discovery.GetAllSchemaRequest) (*discovery.GetAllSchemaResponse, error) {
-       exist, err := ServiceExistID(ctx, request.ServiceId)
+       svc, err := GetService(ctx, GeneratorServiceFilter(ctx, 
request.ServiceId))
        if err != nil {
+               log.Error(fmt.Sprintf("get service[%s] all schemas failed, get 
service failed", request.ServiceId), err)
                return &discovery.GetAllSchemaResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "GetAllSchemas failed 
for get service failed"),
-               }, nil
+                       Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+               }, err
        }
-       if !exist {
+       if svc == nil {
                return &discovery.GetAllSchemaResponse{
                        Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "GetAllSchemas failed 
for service not exist"),
                }, nil
        }
-
-       schemas, err := GetSchemas(ctx, GeneratorServiceFilter(ctx, 
request.ServiceId))
-       if err != nil {
+       schemasList := svc.ServiceInfo.Schemas
+       if len(schemasList) == 0 {
                return &discovery.GetAllSchemaResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "GetAllSchemas failed for get 
schemas failed"),
+                       Response: 
discovery.CreateResponse(discovery.ResponseSuccess, "Do not have this schema 
info."),
+                       Schemas:  []*discovery.Schema{},
                }, nil
        }
+       schemas := make([]*discovery.Schema, 0, len(schemasList))
+       for _, schemaID := range schemasList {
+               tempSchema := &discovery.Schema{}
+               tempSchema.SchemaId = schemaID
+               schema, err := GetSchema(ctx, GeneratorSchemaFilter(ctx, 
request.ServiceId, schemaID))
+               if err != nil {
+                       return &discovery.GetAllSchemaResponse{
+                               Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+                       }, err
+               }
+               if schema == nil {
+                       schemas = append(schemas, tempSchema)
+                       continue
+               }
+               tempSchema.Summary = schema.SchemaSummary
+               if request.WithSchema {
+                       tempSchema.Schema = schema.SchemaInfo
+               }
+               schemas = append(schemas, tempSchema)
+       }
        return &discovery.GetAllSchemaResponse{
                Response: discovery.CreateResponse(discovery.ResponseSuccess, 
"Get all schema info successfully."),
                Schemas:  schemas,
@@ -675,10 +718,15 @@ func (ds *DataSource) DeleteSchema(ctx context.Context, 
request *discovery.Delet
                }, nil
        }
        filter := GeneratorSchemaFilter(ctx, request.ServiceId, 
request.SchemaId)
-       _, err = client.GetMongoClient().Delete(ctx, CollectionSchema, filter)
+       res, err := client.GetMongoClient().DocDelete(ctx, CollectionSchema, 
filter)
        if err != nil {
                return &discovery.DeleteSchemaResponse{
                        Response: 
discovery.CreateResponse(discovery.ErrUnavailableBackend, "DeleteSchema failed 
for delete schema failed."),
+               }, err
+       }
+       if !res {
+               return &discovery.DeleteSchemaResponse{
+                       Response: 
discovery.CreateResponse(discovery.ErrSchemaNotExists, "DeleteSchema failed for 
schema not exist."),
                }, nil
        }
        return &discovery.DeleteSchemaResponse{
@@ -695,36 +743,16 @@ func (ds *DataSource) ModifySchema(ctx context.Context, 
request *discovery.Modif
                Summary:  request.Summary,
                Schema:   request.Schema,
        }
-       session, err := client.GetMongoClient().StartSession(ctx)
+       err := ds.modifySchema(ctx, request.ServiceId, &schema)
        if err != nil {
-               return &discovery.ModifySchemaResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "ModifySchema failed to create 
session."),
-               }, err
-       }
-       if err = session.StartTransaction(); err != nil {
-               return &discovery.ModifySchemaResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "ModifySchema failed to start 
session."),
-               }, err
-       }
-       defer session.EndSession(ctx)
-       err = ds.modifySchema(ctx, request.ServiceId, &schema)
-       if err != nil {
-               log.Error(fmt.Sprintf("modify schema %s %s failed, operator 
%s", serviceID, schemaID, remoteIP), err)
-               errAbort := session.AbortTransaction(ctx)
-               if errAbort != nil {
-                       return &discovery.ModifySchemaResponse{
-                               Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn ModifySchema Abort 
failed."),
-                       }, errAbort
+               log.Error(fmt.Sprintf("modify schema[%s/%s] failed, operator: 
%s", serviceID, schemaID, remoteIP), err)
+               resp := &discovery.ModifySchemaResponse{
+                       Response: discovery.CreateResponseWithSCErr(err),
                }
-               return &discovery.ModifySchemaResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn ModifySchema failed."),
-               }, err
-       }
-       err = session.CommitTransaction(ctx)
-       if err != nil {
-               return &discovery.ModifySchemaResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn ModifySchema 
CommitTransaction failed."),
-               }, err
+               if err.InternalError() {
+                       return resp, err
+               }
+               return resp, nil
        }
        log.Info(fmt.Sprintf("modify schema[%s/%s] successfully, operator: %s", 
serviceID, schemaID, remoteIP))
        return &discovery.ModifySchemaResponse{
@@ -740,33 +768,15 @@ func (ds *DataSource) ModifySchemas(ctx context.Context, 
request *discovery.Modi
        if svc == nil {
                return &discovery.ModifySchemasResponse{Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "Service not exist")}, 
nil
        }
-       session, err := client.GetMongoClient().StartSession(ctx)
-       if err != nil {
-               return &discovery.ModifySchemasResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "ModifySchemas failed to start 
session"),
-               }, err
-       }
-       if err = session.StartTransaction(); err != nil {
-               return &discovery.ModifySchemasResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "ModifySchemas failed to start 
session"),
-               }, err
-       }
-       defer session.EndSession(ctx)
-       err = ds.modifySchemas(ctx, svc.ServiceInfo, request.Schemas)
-       if err != nil {
-               errAbort := session.AbortTransaction(ctx)
-               if errAbort != nil {
-                       return &discovery.ModifySchemasResponse{
-                               Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn ModifySchemas Abort 
failed."),
-                       }, errAbort
+       respErr := ds.modifySchemas(ctx, svc.ServiceInfo, request.Schemas)
+       if respErr != nil {
+               resp := &discovery.ModifySchemasResponse{
+                       Response: discovery.CreateResponseWithSCErr(respErr),
                }
-               return &discovery.ModifySchemasResponse{Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error())}, err
-       }
-       err = session.CommitTransaction(ctx)
-       if err != nil {
-               return &discovery.ModifySchemasResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrInternal, "Txn ModifySchemas 
CommitTransaction failed."),
-               }, err
+               if respErr.InternalError() {
+                       return resp, err
+               }
+               return resp, nil
        }
        return &discovery.ModifySchemasResponse{
                Response: discovery.CreateResponse(discovery.ResponseSuccess, 
"modify schemas info success"),
@@ -774,90 +784,33 @@ func (ds *DataSource) ModifySchemas(ctx context.Context, 
request *discovery.Modi
 
 }
 
-func (ds *DataSource) modifySchema(ctx context.Context, serviceID string, 
schema *discovery.Schema) *discovery.Error {
-       remoteIP := util.GetIPFromContext(ctx)
-       svc, err := GetService(ctx, GeneratorServiceFilter(ctx, serviceID))
-       if err != nil {
-               return discovery.NewError(discovery.ErrInternal, err.Error())
-       }
-       if svc == nil {
-               return discovery.NewError(discovery.ErrServiceNotExists, 
"service does not exist.")
-       }
-       microservice := svc.ServiceInfo
-       var isExist bool
-       for _, sid := range microservice.Schemas {
-               if sid == schema.SchemaId {
-                       isExist = true
-                       break
-               }
-       }
-       var newSchemas []string
-       if !ds.isSchemaEditable(microservice) {
-               if len(microservice.Schemas) != 0 && !isExist {
-                       return 
discovery.NewError(discovery.ErrUndefinedSchemaID, "non-existent schemaID can't 
be added request "+discovery.ENV_PROD)
-               }
-               respSchema, err := GetSchema(ctx, GeneratorSchemaFilter(ctx, 
serviceID, schema.SchemaId))
-               if err != nil {
-                       return 
discovery.NewError(discovery.ErrUnavailableBackend, err.Error())
-               }
-               if schema != nil {
-                       if len(schema.Summary) == 0 {
-                               log.Error(fmt.Sprintf("modify schema %s %s 
failed, get schema summary failed, operator: %s",
-                                       serviceID, schema.SchemaId, remoteIP), 
err)
-                               return 
discovery.NewError(discovery.ErrUnavailableBackend, err.Error())
-                       }
-                       if len(respSchema.SchemaSummary) != 0 {
-                               log.Error(fmt.Sprintf("mode, schema %s %s 
already exist, can not be changed, operator: %s",
-                                       serviceID, schema.SchemaId, remoteIP), 
err)
-                               return 
discovery.NewError(discovery.ErrModifySchemaNotAllow, "schema already exist, 
can not be changed request "+discovery.ENV_PROD)
-                       }
-               }
-               if len(microservice.Schemas) == 0 {
-                       copy(newSchemas, microservice.Schemas)
-                       newSchemas = append(newSchemas, schema.SchemaId)
-               }
-       } else {
-               if !isExist {
-                       copy(newSchemas, microservice.Schemas)
-                       newSchemas = append(newSchemas, schema.SchemaId)
-               }
-       }
-       if len(newSchemas) != len(microservice.Schemas) {
-
-               updateData := bson.M{StringBuilder([]string{ColumnServiceInfo, 
ColumnSchemas}): newSchemas}
-               err := UpdateService(ctx, GeneratorServiceFilter(ctx, 
serviceID), bson.M{"$set": updateData})
-               if err != nil {
-                       return discovery.NewError(discovery.ErrInternal, 
err.Error())
-               }
-       }
-       newSchema := bson.M{"$set": bson.M{ColumnSchemaInfo: schema.Schema, 
ColumnSchemaSummary: schema.Summary}}
-       err = UpdateSchema(ctx, GeneratorSchemaFilter(ctx, serviceID, 
schema.SchemaId), newSchema, options.FindOneAndUpdate().SetUpsert(true))
-       if err != nil {
-               return discovery.NewError(discovery.ErrInternal, err.Error())
-       }
-       return nil
-}
-
 func (ds *DataSource) modifySchemas(ctx context.Context, service 
*discovery.MicroService, schemas []*discovery.Schema) *discovery.Error {
+       domain := util.ParseDomain(ctx)
+       project := util.ParseProject(ctx)
        remoteIP := util.GetIPFromContext(ctx)
+
        serviceID := service.ServiceId
-       schemasFromDatabase, err := GetSchemas(ctx, GeneratorServiceFilter(ctx, 
serviceID))
+       schemasFromDatabase, err := GetSchemas(ctx, bson.M{ColumnServiceID: 
serviceID})
        if err != nil {
                log.Error(fmt.Sprintf("modify service %s schemas failed, get 
schemas failed, operator: %s", serviceID, remoteIP), err)
                return discovery.NewError(discovery.ErrUnavailableBackend, 
err.Error())
        }
+
        needUpdateSchemas, needAddSchemas, needDeleteSchemas, nonExistSchemaIds 
:=
                datasource.SchemasAnalysis(schemas, schemasFromDatabase, 
service.Schemas)
+
+       var schemasOps []mongo.WriteModel
+       var serviceOps []mongo.WriteModel
        if !ds.isSchemaEditable(service) {
                if len(service.Schemas) == 0 {
-                       //todo add quota check
-                       updateData := 
bson.M{StringBuilder([]string{ColumnServiceInfo, ColumnSchemas}): 
nonExistSchemaIds}
-                       err := UpdateService(ctx, GeneratorServiceFilter(ctx, 
serviceID), bson.M{"$set": updateData})
-                       if err != nil {
-                               log.Error(fmt.Sprintf("modify service %s 
schemas failed, update service.Schemas failed, operator: %s",
-                                       serviceID, remoteIP), err)
-                               return 
discovery.NewError(discovery.ErrInternal, err.Error())
+                       res := 
quota.NewApplyQuotaResource(quota.SchemaQuotaType, 
util.ParseDomainProject(ctx), serviceID, int64(len(nonExistSchemaIds)))
+                       rst := quota.Apply(ctx, res)
+                       errQuota := rst.Err
+                       if errQuota != nil {
+                               log.Error(fmt.Sprintf("modify service[%s] 
schemas failed, operator: %s", serviceID, remoteIP), errQuota)
+                               return errQuota
                        }
+                       serviceOps = append(serviceOps, 
mongo.NewUpdateOneModel().SetUpdate(bson.M{"$set": 
bson.M{StringBuilder([]string{ColumnServiceInfo, ColumnSchemas}): 
nonExistSchemaIds}}).SetFilter(GeneratorServiceFilter(ctx, serviceID)))
                } else {
                        if len(nonExistSchemaIds) != 0 {
                                errInfo := fmt.Errorf("non-existent schemaIDs 
%v", nonExistSchemaIds)
@@ -865,15 +818,12 @@ func (ds *DataSource) modifySchemas(ctx context.Context, 
service *discovery.Micr
                                return 
discovery.NewError(discovery.ErrUndefinedSchemaID, errInfo.Error())
                        }
                        for _, needUpdateSchema := range needUpdateSchemas {
-                               exist, err := SchemaExist(ctx, serviceID, 
needUpdateSchema.SchemaId)
+                               exist, err := SchemaSummaryExist(ctx, 
serviceID, needUpdateSchema.SchemaId)
                                if err != nil {
                                        return 
discovery.NewError(discovery.ErrInternal, err.Error())
                                }
                                if !exist {
-                                       err := UpdateSchema(ctx, 
GeneratorSchemaFilter(ctx, serviceID, needUpdateSchema.SchemaId), 
bson.M{"$set": bson.M{ColumnSchemaInfo: needUpdateSchema.Schema, 
ColumnSchemaSummary: needUpdateSchema.Summary}}, 
options.FindOneAndUpdate().SetUpsert(true))
-                                       if err != nil {
-                                               return 
discovery.NewError(discovery.ErrInternal, err.Error())
-                                       }
+                                       schemasOps = append(schemasOps, 
mongo.NewUpdateOneModel().SetFilter(GeneratorSchemaFilter(ctx, serviceID, 
needUpdateSchema.SchemaId)).SetUpdate(bson.M{"$set": bson.M{ColumnSchemaInfo: 
needUpdateSchema.Schema, ColumnSchemaSummary: needUpdateSchema.Summary}}))
                                } else {
                                        log.Warn(fmt.Sprintf("schema[%s/%s] and 
it's summary already exist, skip to update, operator: %s",
                                                serviceID, 
needUpdateSchema.SchemaId, remoteIP))
@@ -883,51 +833,139 @@ func (ds *DataSource) modifySchemas(ctx context.Context, 
service *discovery.Micr
 
                for _, schema := range needAddSchemas {
                        log.Info(fmt.Sprintf("add new schema[%s/%s], operator: 
%s", serviceID, schema.SchemaId, remoteIP))
-                       err := UpdateSchema(ctx, GeneratorSchemaFilter(ctx, 
serviceID, schema.SchemaId), bson.M{"$set": bson.M{ColumnSchemaInfo: 
schema.Schema, ColumnSchemaSummary: schema.Summary}}, 
options.FindOneAndUpdate().SetUpsert(true))
+                       schemasOps = append(schemasOps, 
mongo.NewInsertOneModel().SetDocument(&Schema{
+                               Domain:        domain,
+                               Project:       project,
+                               ServiceID:     serviceID,
+                               SchemaID:      schema.SchemaId,
+                               SchemaInfo:    schema.Schema,
+                               SchemaSummary: schema.Summary,
+                       }))
+               }
+       } else {
+               quotaSize := len(needAddSchemas) - len(needDeleteSchemas)
+               if quotaSize > 0 {
+                       res := 
quota.NewApplyQuotaResource(quota.SchemaQuotaType, 
util.ParseDomainProject(ctx), serviceID, int64(quotaSize))
+                       rst := quota.Apply(ctx, res)
+                       err := rst.Err
                        if err != nil {
-                               return 
discovery.NewError(discovery.ErrInternal, err.Error())
+                               log.Error(fmt.Sprintf("modify service[%s] 
schemas failed, operator: %s", serviceID, remoteIP), err)
+                               return err
                        }
                }
-       } else {
-
                var schemaIDs []string
                for _, schema := range needAddSchemas {
                        log.Info(fmt.Sprintf("add new schema[%s/%s], operator: 
%s", serviceID, schema.SchemaId, remoteIP))
-                       err := UpdateSchema(ctx, GeneratorSchemaFilter(ctx, 
serviceID, schema.SchemaId), bson.M{"$set": bson.M{ColumnSchemaInfo: 
schema.Schema, ColumnSchemaSummary: schema.Summary}}, 
options.FindOneAndUpdate().SetUpsert(true))
-                       if err != nil {
-                               return 
discovery.NewError(discovery.ErrInternal, err.Error())
-                       }
+                       schemasOps = append(schemasOps, 
mongo.NewInsertOneModel().SetDocument(&Schema{
+                               Domain:        domain,
+                               Project:       project,
+                               ServiceID:     serviceID,
+                               SchemaID:      schema.SchemaId,
+                               SchemaInfo:    schema.Schema,
+                               SchemaSummary: schema.Summary,
+                       }))
                        schemaIDs = append(schemaIDs, schema.SchemaId)
                }
 
                for _, schema := range needUpdateSchemas {
                        log.Info(fmt.Sprintf("update schema[%s/%s], operator: 
%s", serviceID, schema.SchemaId, remoteIP))
-                       err := UpdateSchema(ctx, GeneratorSchemaFilter(ctx, 
serviceID, schema.SchemaId), bson.M{"$set": bson.M{ColumnSchemaInfo: 
schema.Schema, ColumnSchemaSummary: schema.Summary}}, 
options.FindOneAndUpdate().SetUpsert(true))
-                       if err != nil {
-                               return 
discovery.NewError(discovery.ErrInternal, err.Error())
-                       }
+                       schemasOps = append(schemasOps, 
mongo.NewUpdateOneModel().SetFilter(GeneratorSchemaFilter(ctx, serviceID, 
schema.SchemaId)).SetUpdate(bson.M{"$set": bson.M{ColumnSchemaInfo: 
schema.Schema, ColumnSchemaSummary: schema.Summary}}))
                        schemaIDs = append(schemaIDs, schema.SchemaId)
                }
 
                for _, schema := range needDeleteSchemas {
                        log.Info(fmt.Sprintf("delete non-existent 
schema[%s/%s], operator: %s", serviceID, schema.SchemaId, remoteIP))
-                       err = DeleteSchema(ctx, GeneratorSchemaFilter(ctx, 
serviceID, schema.SchemaId))
-                       if err != nil {
-                               return 
discovery.NewError(discovery.ErrInternal, err.Error())
-                       }
+                       schemasOps = append(schemasOps, 
mongo.NewDeleteOneModel().SetFilter(GeneratorSchemaFilter(ctx, serviceID, 
schema.SchemaId)))
                }
 
-               updateData := bson.M{StringBuilder([]string{ColumnServiceInfo, 
ColumnSchemas}): schemaIDs}
-               err := UpdateService(ctx, GeneratorServiceFilter(ctx, 
serviceID), bson.M{"$set": updateData})
+               serviceOps = append(serviceOps, 
mongo.NewUpdateOneModel().SetUpdate(bson.M{"$set": 
bson.M{StringBuilder([]string{ColumnServiceInfo, ColumnSchemas}): 
schemaIDs}}).SetFilter(GeneratorServiceFilter(ctx, serviceID)))
+       }
+       if len(schemasOps) > 0 {
+               _, err = client.GetMongoClient().BatchUpdate(ctx, 
CollectionSchema, schemasOps)
+               if err != nil {
+                       return discovery.NewError(discovery.ErrInternal, 
err.Error())
+               }
+       }
+       if len(serviceOps) > 0 {
+               _, err = client.GetMongoClient().BatchUpdate(ctx, 
CollectionService, serviceOps)
                if err != nil {
-                       log.Error(fmt.Sprintf("modify service %s schemas 
failed, update service.Schemas failed, operator: %s", serviceID, remoteIP), err)
                        return discovery.NewError(discovery.ErrInternal, 
err.Error())
                }
        }
        return nil
 }
 
+// modifySchema will be modified in the following cases
+// 1.service have no relation --> update the schema && update the service
+// 2.service is editable && service have relation with the schema --> update 
the shema
+// 3.service is editable && service have no relation with the schema --> 
update the schema && update the service
+// 4.service can't edit && service have relation with the schema && schema 
summary not exist --> update the schema
+func (ds *DataSource) modifySchema(ctx context.Context, serviceID string, 
schema *discovery.Schema) *discovery.Error {
+       remoteIP := util.GetIPFromContext(ctx)
+       svc, err := GetService(ctx, GeneratorServiceFilter(ctx, serviceID))
+       if err != nil {
+               return discovery.NewError(discovery.ErrInternal, err.Error())
+       }
+       if svc == nil {
+               return discovery.NewError(discovery.ErrServiceNotExists, 
"Service does not exist.")
+       }
+       microservice := svc.ServiceInfo
+       var isExist bool
+       for _, sid := range microservice.Schemas {
+               if sid == schema.SchemaId {
+                       isExist = true
+                       break
+               }
+       }
+       var newSchemas []string
+       if !ds.isSchemaEditable(microservice) {
+               if len(microservice.Schemas) != 0 && !isExist {
+                       return 
discovery.NewError(discovery.ErrUndefinedSchemaID, "Non-existent schemaID can't 
be added request "+discovery.ENV_PROD)
+               }
+               respSchema, err := GetSchema(ctx, GeneratorSchemaFilter(ctx, 
serviceID, schema.SchemaId))
+               if err != nil {
+                       return 
discovery.NewError(discovery.ErrUnavailableBackend, err.Error())
+               }
+               if respSchema != nil {
+                       if len(schema.Summary) == 0 {
+                               log.Error(fmt.Sprintf("modify schema %s %s 
failed, get schema summary failed, operator: %s",
+                                       serviceID, schema.SchemaId, remoteIP), 
err)
+                               return 
discovery.NewError(discovery.ErrModifySchemaNotAllow,
+                                       "schema already exist, can not be 
changed request "+discovery.ENV_PROD)
+                       }
+                       if len(respSchema.SchemaSummary) != 0 {
+                               log.Error(fmt.Sprintf("mode, schema %s %s 
already exist, can not be changed, operator: %s",
+                                       serviceID, schema.SchemaId, remoteIP), 
err)
+                               return 
discovery.NewError(discovery.ErrModifySchemaNotAllow, "schema already exist, 
can not be changed request "+discovery.ENV_PROD)
+                       }
+               }
+               if len(microservice.Schemas) == 0 {
+                       copy(newSchemas, microservice.Schemas)
+                       newSchemas = append(newSchemas, schema.SchemaId)
+               }
+       } else {
+               if !isExist {
+                       copy(newSchemas, microservice.Schemas)
+                       newSchemas = append(newSchemas, schema.SchemaId)
+               }
+       }
+       if len(newSchemas) != 0 {
+               updateData := bson.M{StringBuilder([]string{ColumnServiceInfo, 
ColumnSchemas}): newSchemas}
+               err = UpdateService(ctx, GeneratorServiceFilter(ctx, 
serviceID), bson.M{"$set": updateData})
+               if err != nil {
+                       return discovery.NewError(discovery.ErrInternal, 
err.Error())
+               }
+       }
+       newSchema := bson.M{"$set": bson.M{ColumnSchemaInfo: schema.Schema, 
ColumnSchemaSummary: schema.Summary}}
+       err = UpdateSchema(ctx, GeneratorSchemaFilter(ctx, serviceID, 
schema.SchemaId), newSchema, options.FindOneAndUpdate().SetUpsert(true))
+       if err != nil {
+               return discovery.NewError(discovery.ErrInternal, err.Error())
+       }
+       return nil
+}
+
 func (ds *DataSource) AddRule(ctx context.Context, request 
*discovery.AddServiceRulesRequest) (*discovery.AddServiceRulesResponse, error) {
+       remoteIP := util.GetIPFromContext(ctx)
        exist, err := ServiceExistID(ctx, request.ServiceId)
        if err != nil {
                log.Error(fmt.Sprintf("failed to add rules for service %s for 
get service failed", request.ServiceId), err)
@@ -938,7 +976,19 @@ func (ds *DataSource) AddRule(ctx context.Context, request 
*discovery.AddService
        if !exist {
                return &discovery.AddServiceRulesResponse{Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "Service does not 
exist")}, nil
        }
-       //todo add quota check
+       res := quota.NewApplyQuotaResource(quota.RuleQuotaType, 
util.ParseDomainProject(ctx), request.ServiceId, int64(len(request.Rules)))
+       rst := quota.Apply(ctx, res)
+       errQuota := rst.Err
+       if errQuota != nil {
+               log.Error(fmt.Sprintf("add service[%s] rule failed, operator: 
%s", request.ServiceId, remoteIP), errQuota)
+               response := &discovery.AddServiceRulesResponse{
+                       Response: discovery.CreateResponseWithSCErr(errQuota),
+               }
+               if errQuota.InternalError() {
+                       return response, errQuota
+               }
+               return response, nil
+       }
        rules, err := GetRules(ctx, request.ServiceId)
        if err != nil {
                return &discovery.AddServiceRulesResponse{
@@ -1034,25 +1084,33 @@ func (ds *DataSource) DeleteRule(ctx context.Context, 
request *discovery.DeleteS
        if !exist {
                return &discovery.DeleteServiceRulesResponse{Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, "Service not exist")}, 
nil
        }
+       var delRules []mongo.WriteModel
        for _, ruleID := range request.RuleIds {
                exist, err := RuleExist(ctx, GeneratorRuleFilter(ctx, 
request.ServiceId, ruleID))
                if err != nil {
                        return &discovery.DeleteServiceRulesResponse{
                                Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
-                       }, nil
+                       }, err
                }
                if !exist {
                        return &discovery.DeleteServiceRulesResponse{
                                Response: 
discovery.CreateResponse(discovery.ErrRuleNotExists, "This rule does not 
exist."),
                        }, nil
                }
+               delRules = append(delRules, 
mongo.NewDeleteOneModel().SetFilter(GeneratorRuleFilter(ctx, request.ServiceId, 
ruleID)))
+       }
+       if len(delRules) > 0 {
+               _, err := client.GetMongoClient().BatchDelete(ctx, 
CollectionRule, delRules)
+               if err != nil {
+                       return &discovery.DeleteServiceRulesResponse{
+                               Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+                       }, err
+               }
        }
-
        return &discovery.DeleteServiceRulesResponse{
                Response: discovery.CreateResponse(discovery.ResponseSuccess, 
"Delete service rules successfully."),
        }, nil
 }
-
 func (ds *DataSource) UpdateRule(ctx context.Context, request 
*discovery.UpdateServiceRuleRequest) (
        *discovery.UpdateServiceRuleResponse, error) {
        exist, err := ServiceExistID(ctx, request.ServiceId)
@@ -1179,18 +1237,19 @@ func GetServicesVersions(ctx context.Context, filter 
interface{}) ([]string, err
        }
        var versions []string
        for res.Next(ctx) {
-               var tmp string
+               var tmp Service
                err := res.Decode(&tmp)
                if err != nil {
                        return nil, err
                }
-               versions = append(versions, tmp)
+               versions = append(versions, tmp.ServiceInfo.Version)
        }
        return versions, nil
 }
 
 func getServiceDetailUtil(ctx context.Context, mgs *Service, countOnly bool, 
options []string) (*discovery.ServiceDetail, error) {
        serviceDetail := new(discovery.ServiceDetail)
+       serviceID := mgs.ServiceInfo.ServiceId
        if countOnly {
                serviceDetail.Statics = new(discovery.Statistics)
        }
@@ -1210,7 +1269,23 @@ func getServiceDetailUtil(ctx context.Context, mgs 
*Service, countOnly bool, opt
                        }
                        serviceDetail.Rules = rules
                case "instances":
-                       //todo wait instance interface
+                       if countOnly {
+                               instanceCount, err := 
GetInstanceCountOfOneService(ctx, serviceID)
+                               if err != nil {
+                                       log.Error(fmt.Sprintf("get number of 
service [%s]'s instances failed", serviceID), err)
+                                       return nil, err
+                               }
+                               serviceDetail.Statics.Instances = 
&discovery.StInstance{
+                                       Count: instanceCount,
+                               }
+                               continue
+                       }
+                       instances, err := GetAllInstancesOfOneService(ctx, 
serviceID)
+                       if err != nil {
+                               log.Error(fmt.Sprintf("get service[%s]'s all 
instances failed", serviceID), err)
+                               return nil, err
+                       }
+                       serviceDetail.Instances = instances
                case "schemas":
                        schemas, err := GetSchemas(ctx, 
GeneratorServiceFilter(ctx, mgs.ServiceInfo.ServiceId))
                        if err != nil {
@@ -1219,7 +1294,7 @@ func getServiceDetailUtil(ctx context.Context, mgs 
*Service, countOnly bool, opt
                        }
                        serviceDetail.SchemaInfos = schemas
                case "dependencies":
-                       //todo wait dependencied interface
+                       //todo wait dependency interface
                case "":
                        continue
                default:
@@ -1259,7 +1334,11 @@ func UpdateRule(ctx context.Context, filter interface{}, 
m bson.M) error {
 }
 
 func UpdateSchema(ctx context.Context, filter interface{}, m bson.M, opts 
...*options.FindOneAndUpdateOptions) error {
-       return client.GetMongoClient().DocUpdate(ctx, CollectionSchema, filter, 
m, opts...)
+       _, err := client.GetMongoClient().FindOneAndUpdate(ctx, 
CollectionSchema, filter, m, opts...)
+       if err != nil {
+               return err
+       }
+       return nil
 }
 
 func DeleteSchema(ctx context.Context, filter interface{}) error {
@@ -1364,6 +1443,10 @@ func GetSchema(ctx context.Context, filter bson.M) 
(*Schema, error) {
        if err != nil {
                return nil, err
        }
+       if findRes.Err() != nil {
+               //not get any service,not db err
+               return nil, nil
+       }
        var schema *Schema
        err = findRes.Decode(&schema)
        if err != nil {
@@ -1372,12 +1455,20 @@ func GetSchema(ctx context.Context, filter bson.M) 
(*Schema, error) {
        return schema, nil
 }
 
-func SchemaExist(ctx context.Context, serviceID, schemaID string) (bool, 
error) {
-       num, err := client.GetMongoClient().Count(ctx, CollectionSchema, 
GeneratorSchemaFilter(ctx, serviceID, schemaID))
+func SchemaSummaryExist(ctx context.Context, serviceID, schemaID string) 
(bool, error) {
+       res, err := client.GetMongoClient().FindOne(ctx, CollectionSchema, 
GeneratorSchemaFilter(ctx, serviceID, schemaID))
        if err != nil {
                return false, err
        }
-       return num != 0, nil
+       if res.Err() != nil {
+               return false, nil
+       }
+       var s Schema
+       err = res.Decode(&s)
+       if err != nil {
+               return false, err
+       }
+       return len(s.SchemaSummary) != 0, nil
 }
 
 // Instance management
@@ -1430,7 +1521,7 @@ func (ds *DataSource) RegisterInstance(ctx 
context.Context, request *discovery.R
 
 // GetInstances returns instances under the current domain
 func (ds *DataSource) GetInstance(ctx context.Context, request 
*discovery.GetOneInstanceRequest) (*discovery.GetOneInstanceResponse, error) {
-       service := &Service{}
+       var service *Service
        var err error
        var serviceIDs []string
        if len(request.ConsumerServiceId) > 0 {
@@ -2665,3 +2756,68 @@ func allowAcrossDimension(ctx context.Context, 
providerService *Service, consume
        }
        return nil
 }
+
+func GetInstanceCountOfOneService(ctx context.Context, serviceID string) 
(int64, error) {
+       filter := GeneratorServiceInstanceFilter(ctx, serviceID)
+       count, err := client.GetMongoClient().Count(ctx, CollectionInstance, 
filter)
+       if err != nil {
+               return 0, nil
+       }
+       return count, nil
+}
+
+func GetAllInstancesOfOneService(ctx context.Context, serviceID string) 
([]*discovery.MicroServiceInstance, error) {
+       filter := GeneratorServiceInstanceFilter(ctx, serviceID)
+       res, err := client.GetMongoClient().Find(ctx, CollectionInstance, 
filter)
+       if err != nil {
+               return nil, err
+       }
+       var instances []*discovery.MicroServiceInstance
+       for res.Next(ctx) {
+               var tmp Instance
+               err := res.Decode(&tmp)
+               if err != nil {
+                       return nil, err
+               }
+               instances = append(instances, tmp.InstanceInfo)
+       }
+       return instances, nil
+}
+
+func GetInstances(ctx context.Context, filter bson.M) ([]*Instance, error) {
+       res, err := client.GetMongoClient().Find(ctx, CollectionInstance, 
filter)
+       if err != nil {
+               return nil, err
+       }
+       var instances []*Instance
+       for res.Next(ctx) {
+               var tmp *Instance
+               err := res.Decode(&tmp)
+               if err != nil {
+                       return nil, err
+               }
+               instances = append(instances, tmp)
+       }
+       return instances, nil
+}
+
+func GeneratorServiceVersionsFilter(ctx context.Context, service 
*discovery.MicroServiceKey) bson.M {
+       domain := util.ParseDomain(ctx)
+       project := util.ParseProject(ctx)
+
+       return bson.M{
+               ColumnDomain:  domain,
+               ColumnProject: project,
+               StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):         
service.Environment,
+               StringBuilder([]string{ColumnServiceInfo, ColumnAppID}):       
service.AppId,
+               StringBuilder([]string{ColumnServiceInfo, ColumnServiceName}): 
service.ServiceName}
+}
+
+func GeneratorServiceInstanceFilter(ctx context.Context, serviceID string) 
bson.M {
+       domain := util.ParseDomain(ctx)
+       project := util.ParseProject(ctx)
+       return bson.M{
+               ColumnDomain:  domain,
+               ColumnProject: project,
+               StringBuilder([]string{ColumnInstanceInfo, ColumnServiceID}): 
serviceID}
+}
diff --git a/datasource/mongo/ms_test.go b/datasource/mongo/ms_test.go
index 8ba907d..7fcf983 100644
--- a/datasource/mongo/ms_test.go
+++ b/datasource/mongo/ms_test.go
@@ -18,6 +18,7 @@
 package mongo_test
 
 import (
+       "fmt"
        "strconv"
        "strings"
        "testing"
@@ -36,6 +37,7 @@ import (
        "github.com/apache/servicecomb-service-center/server/config"
        "github.com/apache/servicecomb-service-center/server/core"
        "github.com/apache/servicecomb-service-center/server/plugin/quota"
+       "github.com/apache/servicecomb-service-center/server/service"
 )
 
 func init() {
@@ -45,19 +47,13 @@ func init() {
        client.NewMongoClient(config)
 }
 
-func TestServiceRegister(t *testing.T) {
-       t.Run("Register service by mongo, should pass", func(t *testing.T) {
+func TestService_Register(t *testing.T) {
+       t.Run("Register service after init & install, should pass", func(t 
*testing.T) {
                size := quota.DefaultSchemaQuota + 1
                paths := make([]*pb.ServicePath, 0, size)
                properties := make(map[string]string, size)
-               for i := 0; i < size; i++ {
-                       s := strconv.Itoa(i) + strings.Repeat("x", 253)
-                       paths = append(paths, &pb.ServicePath{Path: s, 
Property: map[string]string{s: s}})
-                       properties[s] = s
-               }
                request := &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "service-ms-appID_id",
                                AppId:       "service-ms-appID",
                                ServiceName: "service-ms-serviceName",
                                Version:     "32767.32767.32767.32767",
@@ -81,82 +77,195 @@ func TestServiceRegister(t *testing.T) {
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
        })
-       t.Run("Register service with the same id by mongo, should pass", func(t 
*testing.T) {
-               request := &pb.CreateServiceRequest{
+
+       t.Run("register service with same key, should pass", func(t *testing.T) 
{
+               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "service-ms-appID_id",
-                               AppId:       "service-ms-appID11",
-                               ServiceName: "service-ms-serviceName11",
-                               Version:     "32767.32767.32767.3276711",
-                               Alias:       "service-ms-alias11",
+                               ServiceName: "some-relay-ms-service-name",
+                               Alias:       "sr-ms-service-name",
+                               AppId:       "default",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
                        },
-               }
-               resp, err := 
datasource.Instance().RegisterService(getContext(), request)
+               })
                assert.NotNil(t, resp)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
-       })
-       t.Run("Register service with the same id by mongo, should pass", func(t 
*testing.T) {
-               request := &pb.CreateServiceRequest{
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               sameId := resp.ServiceId
+
+               // serviceName: some-relay-ms-service-name
+               // alias: sr1-ms-service-name
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "service-ms-appID_id_test",
-                               AppId:       "service-ms-appID",
-                               ServiceName: "service-ms-serviceName",
-                               Version:     "32767.32767.32767.32767",
-                               Alias:       "service-ms-alias",
+                               ServiceName: "some-relay-ms-service-name",
+                               Alias:       "sr1-ms-service-name",
+                               AppId:       "default",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
                        },
-               }
-               resp, err := 
datasource.Instance().RegisterService(getContext(), request)
+               })
                assert.NotNil(t, resp)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
-       })
-}
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
-func TestGetService(t *testing.T) {
-       t.Run("get a exist service by mongo, should pass", func(t *testing.T) {
-               request := &pb.CreateServiceRequest{
+               // serviceName: some-relay1-ms-service-name
+               // alias: sr-ms-service-name
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "ms-service-query-id",
-                               ServiceName: "ms-service-query",
+                               ServiceName: "some-relay1-ms-service-name",
+                               Alias:       "sr-ms-service-name",
                                AppId:       "default",
-                               Version:     "1.0.4",
-                               Level:       "BACK",
-                               Properties:  make(map[string]string),
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
                        },
-               }
-
-               resp, err := 
datasource.Instance().RegisterService(getContext(), request)
+               })
+               assert.NotNil(t, resp)
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
-               request = &pb.CreateServiceRequest{
+               // serviceName: some-relay1-ms-service-name
+               // alias: sr-ms-service-name
+               // add serviceId field: sameId
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "ms-service-query-id1",
-                               ServiceName: "ms-service-query1",
+                               ServiceId:   sameId,
+                               ServiceName: "some-relay1-ms-service-name",
+                               Alias:       "sr-ms-service-name",
                                AppId:       "default",
-                               Version:     "1.0.4",
-                               Level:       "BACK",
-                               Properties:  make(map[string]string),
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
                        },
-               }
+               })
+               assert.NotNil(t, resp)
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
+
+               // serviceName: some-relay-ms-service-name
+               // alias: sr1-ms-service-name
+               // serviceId: sameId
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               ServiceId:   sameId,
+                               ServiceName: "some-relay-ms-service-name",
+                               Alias:       "sr1-ms-service-name",
+                               AppId:       "default",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
+                       },
+               })
+               assert.NotNil(t, resp)
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
 
-               resp, err = datasource.Instance().RegisterService(getContext(), 
request)
+               // serviceName: some-relay-ms-service-name
+               // alias: sr1-ms-service-name
+               // serviceId: custom-id-ms-service-id -- different
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               ServiceId:   "custom-id-ms-service-id",
+                               ServiceName: "some-relay-ms-service-name",
+                               Alias:       "sr1-ms-service-name",
+                               AppId:       "default",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
+                       },
+               })
+               assert.NotNil(t, resp)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
 
-               // search service by serviceID
-               queryResp, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
-                       ServiceId: "ms-service-query-id",
+               // serviceName: some-relay1-ms-service-name
+               // alias: sr-ms-service-name
+               // serviceId: custom-id-ms-service-id -- different
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               ServiceId:   "custom-id-ms-service-id",
+                               ServiceName: "some-relay1-ms-service-name",
+                               Alias:       "sr-ms-service-name",
+                               AppId:       "default",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       "xxxxxxxx",
+                               },
+                               Status: "UP",
+                       },
                })
+               assert.NotNil(t, resp)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
queryResp.Response.GetCode())
+               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
        })
-       t.Run("get all service by mongo, should pass", func(t *testing.T) {
+
+       t.Run("same serviceId,different service, can not register again,error 
is same as the service register twice",
+               func(t *testing.T) {
+                       resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                               Service: &pb.MicroService{
+                                       ServiceId:   
"same-serviceId-service-ms",
+                                       ServiceName: "serviceA-service-ms",
+                                       AppId:       "default-service-ms",
+                                       Version:     "1.0.0",
+                                       Level:       "FRONT",
+                                       Schemas: []string{
+                                               "xxxxxxxx",
+                                       },
+                                       Status: "UP",
+                               },
+                       })
+
+                       assert.NotNil(t, resp)
+                       assert.NoError(t, err)
+                       assert.Equal(t, resp.Response.GetCode(), 
pb.ResponseSuccess)
+
+                       // same serviceId with different service name
+                       resp, err = 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                               Service: &pb.MicroService{
+                                       ServiceId:   
"same-serviceId-service-ms",
+                                       ServiceName: "serviceB-service-ms",
+                                       AppId:       "default-service-ms",
+                                       Version:     "1.0.0",
+                                       Level:       "FRONT",
+                                       Schemas: []string{
+                                               "xxxxxxxx",
+                                       },
+                                       Status: "UP",
+                               },
+                       })
+                       assert.NotNil(t, resp)
+                       assert.NoError(t, err)
+                       assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
+               })
+}
+
+func TestService_Get(t *testing.T) {
+       t.Run("get a exist service, should pass", func(t *testing.T) {
                request := &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "ms-service-query-id3",
-                               ServiceName: "ms-service-query3",
+                               ServiceId:   "ms-service-query-id",
+                               ServiceName: "ms-service-query",
                                AppId:       "default",
                                Version:     "1.0.4",
                                Level:       "BACK",
@@ -166,223 +275,1430 @@ func TestGetService(t *testing.T) {
 
                resp, err := 
datasource.Instance().RegisterService(getContext(), request)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, resp.Response.GetCode(), pb.ResponseSuccess)
 
                // search service by serviceID
-               queryResp, err := 
datasource.Instance().GetServices(getContext(), &pb.GetServicesRequest{})
+               queryResp, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
+                       ServiceId: "ms-service-query-id",
+               })
                assert.NoError(t, err)
-               assert.Greater(t, len(queryResp.Services), 0)
+               assert.Equal(t, queryResp.Response.GetCode(), 
pb.ResponseSuccess)
        })
-       t.Run("get a exist service with id by mongo, should pass", func(t 
*testing.T) {
-               queryResp, err := 
datasource.Instance().ExistServiceByID(getContext(), 
&pb.GetExistenceByIDRequest{
-                       ServiceId: "ms-service-query-id1",
-               })
+       t.Run("query all services, should pass", func(t *testing.T) {
+               resp, err := datasource.Instance().GetServices(getContext(), 
&pb.GetServicesRequest{})
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
queryResp.Response.GetCode())
-               assert.Equal(t, true, queryResp.Exist)
+               assert.Greater(t, len(resp.Services), 0)
        })
+
        t.Run("query a service by a not existed serviceId, should not pass", 
func(t *testing.T) {
                // not exist service
                resp, err := datasource.Instance().GetService(getContext(), 
&pb.GetServiceRequest{
                        ServiceId: "no-exist-service",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+               assert.Equal(t, resp.Response.GetCode(), pb.ErrServiceNotExists)
        })
-
 }
 
-func TestUpdateService(t *testing.T) {
-       t.Run("update service by mongo, should pass", func(t *testing.T) {
-               request := &pb.CreateServiceRequest{
+func TestService_Update(t *testing.T) {
+       var serviceId string
+
+       t.Run("create service", func(t *testing.T) {
+               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "ms-service-update-new-id",
-                               ServiceName: "ms-service-update",
-                               AppId:       "default",
-                               Version:     "1.0.4",
-                               Level:       "BACK",
-                               Properties:  make(map[string]string),
+                               Alias:       "es_service_ms",
+                               ServiceName: "update_prop_service_service_ms",
+                               AppId:       "update_prop_appId_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      "UP",
                        },
-               }
-
-               resp, err := 
datasource.Instance().RegisterService(getContext(), request)
+               })
                assert.NoError(t, err)
-               assert.Equal(t, resp.Response.GetCode(), pb.ResponseSuccess)
+               assert.NotNil(t, resp)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.NotEqual(t, "", resp.ServiceId)
+               serviceId = resp.ServiceId
+       })
 
-               requestNew := &pb.UpdateServicePropsRequest{
-                       ServiceId:  "ms-service-update-new-id",
+       t.Run("update properties while properties not nil", func(t *testing.T) {
+               request := &pb.UpdateServicePropsRequest{
+                       ServiceId:  serviceId,
+                       Properties: make(map[string]string),
+               }
+               request2 := &pb.UpdateServicePropsRequest{
+                       ServiceId:  serviceId,
                        Properties: make(map[string]string),
                }
-               requestNew.Properties["k"] = "v"
-               res, err := datasource.Instance().UpdateService(getContext(), 
requestNew)
+               request.Properties["test"] = "1"
+               request2.Properties["k"] = "v"
+               resp, err := datasource.Instance().UpdateService(getContext(), 
request)
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().UpdateService(getContext(), 
request2)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, res.Response.GetCode())
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
-               respGetService, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{ServiceId: 
"ms-service-update-new-id"})
+               respGetService, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceId,
+               })
                assert.NoError(t, err)
+               assert.Equal(t, serviceId, respGetService.Service.ServiceId)
+               assert.Equal(t, "", respGetService.Service.Properties["test"])
                assert.Equal(t, "v", respGetService.Service.Properties["k"])
        })
-}
 
-func TestTagsAdd(t *testing.T) {
-       // create service
-       t.Run("create service, the request is valid, should pass", func(t 
*testing.T) {
-               svc1 := &pb.MicroService{
-                       ServiceId:   "service_tag_id",
-                       AppId:       "create_tag_group_ms",
-                       ServiceName: "create_tag_service_ms",
-                       Version:     "1.0.0",
-                       Level:       "FRONT",
-                       Status:      pb.MS_UP,
+       t.Run("update service that does not exist", func(t *testing.T) {
+               r := &pb.UpdateServicePropsRequest{
+                       ServiceId:  "not_exist_service_service_ms",
+                       Properties: make(map[string]string),
                }
-               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
-                       Service: svc1,
-               })
+               resp, err := datasource.Instance().UpdateService(getContext(), 
r)
                assert.NoError(t, err)
-               assert.NotEqual(t, "", resp.ServiceId)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
        })
 
-       //
-       t.Run("create service, the request is valid, should pass", func(t 
*testing.T) {
-               defaultQuota := quota.DefaultTagQuota
-               tags := make(map[string]string, defaultQuota)
-               for i := 0; i < defaultQuota; i++ {
-                       s := "tag" + strconv.Itoa(i)
-                       tags[s] = s
+       t.Run("update service by removing the properties", func(t *testing.T) {
+               r := &pb.UpdateServicePropsRequest{
+                       ServiceId:  serviceId,
+                       Properties: nil,
                }
-               resp, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
-                       ServiceId: "service_tag_id",
-                       Tags:      tags,
-               })
+               resp, err := datasource.Instance().UpdateService(getContext(), 
r)
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-       })
 
+               r = &pb.UpdateServicePropsRequest{
+                       ServiceId:  "",
+                       Properties: map[string]string{},
+               }
+               resp, err = datasource.Instance().UpdateService(getContext(), r)
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
 }
 
-func TestTagsGet(t *testing.T) {
-       t.Run("create service and add tags, the request is valid, should pass", 
func(t *testing.T) {
-               svc := &pb.MicroService{
-                       ServiceId:   "get_tag_group_ms_id",
-                       AppId:       "get_tag_group_ms",
-                       ServiceName: "get_tag_service_ms",
-                       Version:     "1.0.0",
-                       Level:       "FRONT",
-                       Status:      pb.MS_UP,
-               }
+func TestService_Detail(t *testing.T) {
+       var (
+               serviceId string
+       )
+
+       t.Run("execute 'get detail' operation", func(t *testing.T) {
+               log.Info("should be passed")
                resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
-                       Service: svc,
+                       Service: &pb.MicroService{
+                               AppId:       "govern_service_group",
+                               ServiceName: "govern_service_name",
+                               Version:     "3.0.0",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceId = resp.ServiceId
 
-               respAddTags, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
-                       ServiceId: "get_tag_group_ms_id",
-                       Tags: map[string]string{
-                               "a": "test",
-                               "b": "b",
-                       },
+               datasource.Instance().ModifySchema(getContext(), 
&pb.ModifySchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "schemaId",
+                       Schema:    "detail",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
respAddTags.Response.GetCode())
-       })
-       t.Run("the request is valid", func(t *testing.T) {
-               resp, err := datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
-                       ServiceId: "get_tag_group_ms_id",
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               datasource.Instance().RegisterInstance(getContext(), 
&pb.RegisterInstanceRequest{
+                       Instance: &pb.MicroServiceInstance{
+                               ServiceId: serviceId,
+                               Endpoints: []string{
+                                       "govern:127.0.0.1:8080",
+                               },
+                               HostName: "UT-HOST",
+                               Status:   pb.MSI_UP,
+                       },
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-               assert.Equal(t, "test", resp.Tags["a"])
+
+               log.Info("when get invalid service detail, should be failed")
+               respD, err := 
datasource.Instance().GetServiceDetail(getContext(), &pb.GetServiceRequest{
+                       ServiceId: "",
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, respD.Response.GetCode())
+               //
+               log.Info("when get a service detail, should be passed")
+               respGetServiceDetail, err := 
datasource.Instance().GetServiceDetail(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceId,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respGetServiceDetail.Response.GetCode())
+               //
+               respDelete, err := 
datasource.Instance().UnregisterService(getContext(), &pb.DeleteServiceRequest{
+                       ServiceId: serviceId,
+                       Force:     true,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respDelete.Response.GetCode())
+               //
+               respGetServiceDetail, err = 
datasource.Instance().GetServiceDetail(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceId,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, 
respGetServiceDetail.Response.GetCode())
        })
 }
 
-func TestTagUpdate(t *testing.T) {
-       t.Run("add service and add tags, the request is valid, should pass", 
func(t *testing.T) {
-               svc := &pb.MicroService{
-                       ServiceId:   "update_tag_group_ms_id",
-                       AppId:       "update_tag_group_ms",
-                       ServiceName: "update_tag_service_ms",
-                       Version:     "1.0.0",
-                       Level:       "FRONT",
-                       Status:      pb.MS_UP,
-               }
-               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
-                       Service: svc,
+func TestService_Info(t *testing.T) {
+       t.Run("get all services", func(t *testing.T) {
+               log.Info("should be passed")
+               resp, err := 
datasource.Instance().GetServicesInfo(getContext(), &pb.GetServicesInfoRequest{
+                       Options: []string{"all"},
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
-               respAddTags, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
-                       ServiceId: "update_tag_group_ms_id",
-                       Tags: map[string]string{
-                               "a": "test",
-                               "b": "b",
-                       },
+               resp, err = datasource.Instance().GetServicesInfo(getContext(), 
&pb.GetServicesInfoRequest{
+                       Options: []string{""},
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
respAddTags.Response.GetCode())
-       })
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
-       t.Run("the request is valid, should pass", func(t *testing.T) {
-               resp, err := datasource.Instance().UpdateTag(getContext(), 
&pb.UpdateServiceTagRequest{
-                       ServiceId: "update_tag_group_ms_id",
-                       Key:       "a",
-                       Value:     "update",
+               resp, err = datasource.Instance().GetServicesInfo(getContext(), 
&pb.GetServicesInfoRequest{
+                       Options: []string{"tags", "rules", "instances", 
"schemas", "statistics"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().GetServicesInfo(getContext(), 
&pb.GetServicesInfoRequest{
+                       Options: []string{"statistics"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().GetServicesInfo(getContext(), 
&pb.GetServicesInfoRequest{
+                       Options:   []string{"instances"},
+                       CountOnly: true,
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
        })
 }
 
-func TestTagsDelete(t *testing.T) {
-       t.Run("create service and add tags, the request is valid, should pass", 
func(t *testing.T) {
-               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+func TestService_Delete(t *testing.T) {
+       var (
+               serviceContainInstId string
+               serviceNoInstId      string
+       )
+
+       t.Run("create service & instance", func(t *testing.T) {
+               respCreate, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "delete_tag_group_ms_id",
-                               AppId:       "delete_tag_group_ms",
-                               ServiceName: "delete_tag_service_ms",
+                               ServiceName: "delete_service_with_inst_ms",
+                               AppId:       "delete_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      "UP",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreate.Response.GetCode())
+               serviceContainInstId = respCreate.ServiceId
+
+               log.Info("attach instance")
+               instance := &pb.MicroServiceInstance{
+                       ServiceId: serviceContainInstId,
+                       Endpoints: []string{
+                               "deleteService:127.0.0.1:8080",
+                       },
+                       HostName: "delete-host-ms",
+                       Status:   pb.MSI_UP,
+               }
+               respCreateIns, err := 
datasource.Instance().RegisterInstance(getContext(), 
&pb.RegisterInstanceRequest{
+                       Instance: instance,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateIns.Response.GetCode())
+
+               log.Info("create service without instance")
+               provider := &pb.MicroService{
+                       ServiceName: "delete_service_no_inst_ms",
+                       AppId:       "delete_service_ms",
+                       Version:     "1.0.0",
+                       Level:       "FRONT",
+                       Status:      "UP",
+               }
+               respCreate, err = 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: provider,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreate.Response.GetCode())
+               serviceNoInstId = respCreate.ServiceId
+
+               respCreate, err = 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               ServiceName: "delete_service_consumer_ms",
+                               AppId:       "delete_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      "UP",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreate.Response.GetCode())
+       })
+
+       t.Run("delete a service which contains instances with no force flag", 
func(t *testing.T) {
+               log.Info("should not pass")
+               resp, err := 
datasource.Instance().UnregisterService(getContext(), &pb.DeleteServiceRequest{
+                       ServiceId: serviceContainInstId,
+                       Force:     false,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+
+       t.Run("delete a service which contains instances with force flag", 
func(t *testing.T) {
+               log.Info("should pass")
+               resp, err := 
datasource.Instance().UnregisterService(getContext(), &pb.DeleteServiceRequest{
+                       ServiceId: serviceContainInstId,
+                       Force:     true,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+
+       // todo: add delete service depended by consumer after finishing 
dependency management
+
+       t.Run("delete a service which depended by consumer with force flag", 
func(t *testing.T) {
+               log.Info("should pass")
+               resp, err := 
datasource.Instance().UnregisterService(getContext(), &pb.DeleteServiceRequest{
+                       ServiceId: serviceNoInstId,
+                       Force:     true,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+
+       t.Run("delete a service with no force flag", func(t *testing.T) {
+               log.Info("should not pass")
+               resp, err := 
datasource.Instance().UnregisterService(getContext(), &pb.DeleteServiceRequest{
+                       ServiceId: serviceNoInstId,
+                       Force:     false,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+}
+
+func TestApplication_Get(t *testing.T) {
+       t.Run("execute 'get apps' operation", func(t *testing.T) {
+               log.Info("when request is valid, should be passed")
+               resp, err := 
datasource.Instance().GetApplications(getContext(), &pb.GetAppsRequest{})
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().GetApplications(getContext(), 
&pb.GetAppsRequest{
+                       Environment: pb.ENV_ACCEPT,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+}
+
+func TestTags_Add(t *testing.T) {
+       var (
+               serviceId1 string
+               serviceId2 string
+       )
+
+       // create service
+       t.Run("create service", func(t *testing.T) {
+               svc1 := &pb.MicroService{
+                       AppId:       "create_tag_group_ms",
+                       ServiceName: "create_tag_service_ms",
+                       Version:     "1.0.0",
+                       Level:       "FRONT",
+                       Status:      pb.MS_UP,
+               }
+               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: svc1,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, "", resp.ServiceId)
+               serviceId1 = resp.ServiceId
+
+               svc2 := &pb.MicroService{
+                       AppId:       "create_tag_group_ms",
+                       ServiceName: "create_tag_service_ms",
+                       Version:     "1.0.1",
+                       Level:       "FRONT",
+                       Status:      pb.MS_UP,
+               }
+               resp, err = datasource.Instance().RegisterService(getContext(), 
&pb.CreateServiceRequest{
+                       Service: svc2,
+               })
+               assert.NoError(t, err)
+               assert.NotNil(t, "", resp.ServiceId)
+               serviceId2 = resp.ServiceId
+       })
+
+       t.Run("the request is invalid", func(t *testing.T) {
+               resp, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
+                       ServiceId: "noServiceTest",
+                       Tags: map[string]string{
+                               "a": "test",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+       })
+
+       t.Run("the request is valid", func(t *testing.T) {
+               defaultQuota := quota.DefaultTagQuota
+               tags := make(map[string]string, defaultQuota)
+               for i := 0; i < defaultQuota; i++ {
+                       s := "tag" + strconv.Itoa(i)
+                       tags[s] = s
+               }
+               resp, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
+                       ServiceId: serviceId1,
+                       Tags:      tags,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+
+       t.Run("tag's quota exceeded", func(t *testing.T) {
+               size := quota.DefaultTagQuota / 2
+               tags := make(map[string]string, size)
+               for i := 0; i < size; i++ {
+                       s := "tag" + strconv.Itoa(i)
+                       tags[s] = s
+               }
+               resp, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
+                       ServiceId: serviceId2,
+                       Tags:      tags,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               tags["out"] = "range"
+               resp, _ = datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
+                       ServiceId: serviceId2,
+                       Tags:      tags,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrNotEnoughQuota, resp.Response.GetCode())
+       })
+}
+
+func TestTags_Get(t *testing.T) {
+       var serviceId string
+       t.Run("create service and add tags", func(t *testing.T) {
+               svc := &pb.MicroService{
+                       AppId:       "get_tag_group_ms",
+                       ServiceName: "get_tag_service_ms",
+                       Version:     "1.0.0",
+                       Level:       "FRONT",
+                       Status:      pb.MS_UP,
+               }
+               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: svc,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceId = resp.ServiceId
+
+               respAddTags, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
+                       ServiceId: serviceId,
+                       Tags: map[string]string{
+                               "a": "test",
+                               "b": "b",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddTags.Response.GetCode())
+       })
+
+       t.Run("the request is invalid", func(t *testing.T) {
+               resp, err := datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
+                       ServiceId: "noThisService",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
+                       ServiceId: "",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
+                       ServiceId: strings.Repeat("x", 65),
+               })
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+       })
+
+       t.Run("the request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
+                       ServiceId: serviceId,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, "test", resp.Tags["a"])
+       })
+}
+
+func TestTag_Update(t *testing.T) {
+       var serviceId string
+       t.Run("add service and add tags", func(t *testing.T) {
+               svc := &pb.MicroService{
+                       AppId:       "update_tag_group_ms",
+                       ServiceName: "update_tag_service_ms",
+                       Version:     "1.0.0",
+                       Level:       "FRONT",
+                       Status:      pb.MS_UP,
+               }
+               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: svc,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceId = resp.ServiceId
+
+               respAddTags, err := datasource.Instance().AddTags(getContext(), 
&pb.AddServiceTagsRequest{
+                       ServiceId: serviceId,
+                       Tags: map[string]string{
+                               "a": "test",
+                               "b": "b",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddTags.Response.GetCode())
+       })
+
+       t.Run("the request is invalid", func(t *testing.T) {
+
+               resp, err := datasource.Instance().UpdateTag(getContext(), 
&pb.UpdateServiceTagRequest{
+                       ServiceId: "noneservice",
+                       Key:       "a",
+                       Value:     "update",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().UpdateTag(getContext(), 
&pb.UpdateServiceTagRequest{
+                       ServiceId: serviceId,
+                       Key:       "notexisttag",
+                       Value:     "update",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrTagNotExists, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().UpdateTag(getContext(), 
&pb.UpdateServiceTagRequest{
+                       ServiceId: serviceId,
+                       Key:       strings.Repeat("x", 65),
+                       Value:     "v",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrTagNotExists, resp.Response.GetCode())
+       })
+
+       t.Run("the request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().UpdateTag(getContext(), 
&pb.UpdateServiceTagRequest{
+                       ServiceId: serviceId,
+                       Key:       "a",
+                       Value:     "update",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+}
+
+func TestTags_Delete(t *testing.T) {
+       var serviceId string
+       t.Run("create service and add tags", func(t *testing.T) {
+               resp, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "delete_tag_group_ms",
+                               ServiceName: "delete_tag_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceId = resp.ServiceId
+
+               respAddTages, err := 
datasource.Instance().AddTags(getContext(), &pb.AddServiceTagsRequest{
+                       ServiceId: serviceId,
+                       Tags: map[string]string{
+                               "a": "test",
+                               "b": "b",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddTages.Response.GetCode())
+       })
+
+       t.Run("the request is invalid", func(t *testing.T) {
+               resp, err := datasource.Instance().DeleteTags(getContext(), 
&pb.DeleteServiceTagsRequest{
+                       ServiceId: "noneservice",
+                       Keys:      []string{"a", "b"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().DeleteTags(getContext(), 
&pb.DeleteServiceTagsRequest{
+                       ServiceId: serviceId,
+                       Keys:      []string{"c"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrTagNotExists, resp.Response.GetCode())
+       })
+
+       t.Run("the request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().DeleteTags(getContext(), 
&pb.DeleteServiceTagsRequest{
+                       ServiceId: serviceId,
+                       Keys:      []string{"a", "b"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               respGetTags, err := datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
+                       ServiceId: serviceId,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, "", respGetTags.Tags["a"])
+       })
+}
+
+func TestRule_Add(t *testing.T) {
+       var (
+               serviceId1 string
+               serviceId2 string
+       )
+
+       t.Run("register service and instance", func(t *testing.T) {
+               respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "create_rule_group_ms",
+                               ServiceName: "create_rule_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId1 = respCreateService.ServiceId
+
+               respCreateService, err = 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "create_rule_group_ms",
+                               ServiceName: "create_rule_service_ms",
+                               Version:     "1.0.1",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId2 = respCreateService.ServiceId
+       })
+
+       t.Run("invalid request", func(t *testing.T) {
+               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: "not_exist_service_ms",
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "BLACK",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test white",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, respAddRule)
+       })
+
+       t.Run("request is valid", func(t *testing.T) {
+               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId1,
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "BLACK",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test black",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
+               ruleId := respAddRule.RuleIds[0]
+               assert.NotEqual(t, "", ruleId)
+
+               respAddRule, err = datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId1,
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "BLACK",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test change black",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
+               assert.Equal(t, 0, len(respAddRule.RuleIds))
+
+               respAddRule, err = datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId1,
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "WHITE",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test white",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
+       })
+
+       t.Run("create rule out of quota", func(t *testing.T) {
+               size := quota.DefaultRuleQuota + 1
+               rules := make([]*pb.AddOrUpdateServiceRule, 0, size)
+               for i := 0; i < size; i++ {
+                       rules = append(rules, &pb.AddOrUpdateServiceRule{
+                               RuleType:    "BLACK",
+                               Attribute:   "ServiceName",
+                               Pattern:     strconv.Itoa(i),
+                               Description: "test white",
+                       })
+               }
+
+               resp, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId2,
+                       Rules:     rules[:size-1],
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId2,
+                       Rules:     rules[size-1:],
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrNotEnoughQuota, resp.Response.GetCode())
+       })
+}
+
+func TestRule_Get(t *testing.T) {
+       var (
+               serviceId string
+               ruleId    string
+       )
+
+       t.Run("register service and rules", func(t *testing.T) {
+               respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "get_rule_group_ms",
+                               ServiceName: "get_rule_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId = respCreateService.ServiceId
+
+               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId,
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "BLACK",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test BLACK",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
+               ruleId = respAddRule.RuleIds[0]
+               assert.NotEqual(t, "", ruleId)
+       })
+
+       t.Run("get when request is invalid", func(t *testing.T) {
+               respGetRule, err := 
datasource.Instance().GetRules(getContext(), &pb.GetServiceRulesRequest{
+                       ServiceId: "not_exist_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, 
respGetRule.Response.GetCode())
+       })
+
+       t.Run("get when request is valid", func(t *testing.T) {
+               respGetRule, err := 
datasource.Instance().GetRules(getContext(), &pb.GetServiceRulesRequest{
+                       ServiceId: serviceId,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respGetRule.Response.GetCode())
+               assert.Equal(t, ruleId, respGetRule.Rules[0].RuleId)
+       })
+}
+
+func TestRule_Delete(t *testing.T) {
+       var (
+               serviceId string
+               ruleId    string
+       )
+       t.Run("register service and rules", func(t *testing.T) {
+               respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "delete_rule_group_ms",
+                               ServiceName: "delete_rule_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId = respCreateService.ServiceId
+
+               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId,
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "BLACK",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test BLACK",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
+               ruleId = respAddRule.RuleIds[0]
+               assert.NotEqual(t, "", ruleId)
+       })
+
+       t.Run("delete when request is invalid", func(t *testing.T) {
+               resp, err := datasource.Instance().DeleteRule(getContext(), 
&pb.DeleteServiceRulesRequest{
+                       ServiceId: "not_exist_service_ms",
+                       RuleIds:   []string{"1000000"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().DeleteRule(getContext(), 
&pb.DeleteServiceRulesRequest{
+                       ServiceId: serviceId,
+                       RuleIds:   []string{"not_exist_rule"},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrRuleNotExists, resp.Response.GetCode())
+       })
+
+       t.Run("delete when request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().DeleteRule(getContext(), 
&pb.DeleteServiceRulesRequest{
+                       ServiceId: serviceId,
+                       RuleIds:   []string{ruleId},
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               respGetRule, err := 
datasource.Instance().GetRules(getContext(), &pb.GetServiceRulesRequest{
+                       ServiceId: serviceId,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, 0, len(respGetRule.Rules))
+       })
+}
+
+func TestRule_Update(t *testing.T) {
+       var (
+               serviceId string
+               ruleId    string
+       )
+
+       t.Run("create service and rules", func(t *testing.T) {
+               respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "update_rule_group_ms",
+                               ServiceName: "update_rule_service_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Status:      pb.MS_UP,
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId = respCreateService.ServiceId
+
+               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
+                       ServiceId: serviceId,
+                       Rules: []*pb.AddOrUpdateServiceRule{
+                               {
+                                       RuleType:    "BLACK",
+                                       Attribute:   "ServiceName",
+                                       Pattern:     "Test*",
+                                       Description: "test BLACK",
+                               },
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
+               ruleId = respAddRule.RuleIds[0]
+               assert.NotEqual(t, "", ruleId)
+       })
+
+       t.Run("update when request is invalid", func(t *testing.T) {
+               rule := &pb.AddOrUpdateServiceRule{
+                       RuleType:    "BLACK",
+                       Attribute:   "ServiceName",
+                       Pattern:     "Test*",
+                       Description: "test BLACK update",
+               }
+               resp, err := datasource.Instance().UpdateRule(getContext(), 
&pb.UpdateServiceRuleRequest{
+                       ServiceId: "not_exist_service_ms",
+                       RuleId:    ruleId,
+                       Rule:      rule,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().UpdateRule(getContext(), 
&pb.UpdateServiceRuleRequest{
+                       ServiceId: serviceId,
+                       RuleId:    "not_exist_rule_ms",
+                       Rule:      rule,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().UpdateRule(getContext(), 
&pb.UpdateServiceRuleRequest{
+                       ServiceId: serviceId,
+                       RuleId:    ruleId,
+                       Rule: &pb.AddOrUpdateServiceRule{
+                               RuleType:    "WHITE",
+                               Attribute:   "ServiceName",
+                               Pattern:     "Test*",
+                               Description: "test white update",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+
+       t.Run("update when request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().UpdateRule(getContext(), 
&pb.UpdateServiceRuleRequest{
+                       ServiceId: serviceId,
+                       RuleId:    ruleId,
+                       Rule: &pb.AddOrUpdateServiceRule{
+                               RuleType:    "BLACK",
+                               Attribute:   "AppId",
+                               Pattern:     "Test*",
+                               Description: "test white update",
+                       },
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+}
+
+func TestSchema_Create(t *testing.T) {
+       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 := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: svc,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, "", resp.ServiceId)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceIdDev = resp.ServiceId
+
+               resp, err = datasource.Instance().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, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+
+       t.Run("create schemas out of gauge", func(t *testing.T) {
+               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,
+                       })
+               }
+
+               resp, err := datasource.Instance().ModifySchemas(getContext(), 
&pb.ModifySchemasRequest{
+                       ServiceId: serviceIdDev,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrNotEnoughQuota, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().ModifySchemas(getContext(), 
&pb.ModifySchemasRequest{
+                       ServiceId: serviceIdDev,
+                       Schemas:   schemas[:quota.DefaultSchemaQuota],
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               //resp, err = datasource.Instance().ModifySchemas(getContext(), 
&pb.ModifySchemasRequest{
+               //      ServiceId: serviceIdDev,
+               //      Schemas:   schemas,
+               //})
+               //assert.NoError(t, err)
+               //assert.Equal(t, pb.ErrNotEnoughQuota, resp.Response.GetCode())
+       })
+
+       //
+       t.Run("batch create schemas in dev env", func(t *testing.T) {
+               var (
+                       serviceIdDev1 string
+                       serviceIdDev2 string
+               )
+
+               resp, err := 
datasource.Instance().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, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceIdDev1 = resp.ServiceId
+
+               resp, err = datasource.Instance().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, pb.ResponseSuccess, resp.Response.GetCode())
+               serviceIdDev2 = resp.ServiceId
+
+               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 := 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdDev1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateSchema.Response.GetCode())
+               //
+               schemas = []*pb.Schema{
+                       {
+                               SchemaId: "first_schemaId_service_ms",
+                               Schema:   "first_schema_change_service_ms",
+                               Summary:  "first0summary1change_service_ms",
+                       },
+               }
+               respCreateSchema, err = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdDev1,
+                       Schemas:   schemas,
+               })
+
+               schemas = []*pb.Schema{
+                       {
+                               SchemaId: "second_schemaId_service_ms",
+                               Schema:   "second_schema_service_ms",
+                               Summary:  "second0summary_service_ms",
+                       },
+               }
+               respCreateSchema, err = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdDev1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateSchema.Response.GetCode())
+
+               respGetService, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceIdDev1,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respGetService.Response.GetCode())
+               assert.Equal(t, []string{"second_schemaId_service_ms"}, 
respGetService.Service.Schemas)
+
+               schemas = []*pb.Schema{
+                       {
+                               SchemaId: "second_schemaId_service_ms",
+                               Schema:   "second_schema_service_ms",
+                               Summary:  "second0summary_service_ms",
+                       },
+               }
+               respCreateSchema, err = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdDev2,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateSchema.Response.GetCode())
+
+               respGetService, err = 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceIdDev2,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
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
+               )
+
+               respCreateService, err := 
datasource.Instance().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, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdPro1 = respCreateService.ServiceId
+
+               respCreateService, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdPro2 = respCreateService.ServiceId
+
+               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 := 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchemas.Response.GetCode())
+
+               respGetService, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceIdPro1,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respGetService.Response.GetCode())
+               assert.Equal(t, []string{"first_schemaId_service_ms"}, 
respGetService.Service.Schemas)
+
+               respModifySchemas, err = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchemas.Response.GetCode())
+
+               schemas = []*pb.Schema{
+                       {
+                               SchemaId: "second_schemaId_service_ms",
+                               Schema:   "second_schema_service_ms",
+                               Summary:  "second0summary_service_ms",
+                       },
+               }
+               respModifySchemas, err = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrUndefinedSchemaID, 
respModifySchemas.Response.GetCode())
+               fmt.Println(serviceIdPro2)
+
+               respModifySchema, err := 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro2,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               schemas = []*pb.Schema{
+                       {
+                               SchemaId: "first_schemaId_service_ms",
+                               Schema:   "first_schema_service_ms",
+                               Summary:  "first0summary_service_ms",
+                       },
+               }
+               respModifySchemas, err = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro2,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchemas.Response.GetCode())
+               //
+               respExist, err := 
datasource.Instance().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 = 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro2,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchemas.Response.GetCode())
+       })
+
+       //
+       t.Run("create a schema in dev env", func(t *testing.T) {
+               var (
+                       serviceIdDev1 string
+                       serviceIdDev2 string
+               )
+
+               respCreateService, err := 
datasource.Instance().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, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdDev1 = respCreateService.ServiceId
+
+               respCreateService, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdDev2 = respCreateService.ServiceId
+
+               respModifySchema, err := 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdDev1,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdDev2,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdDev1,
+                       SchemaId:  "second_schemaId_service_ms",
+                       Schema:    "second_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+       })
+       //
+       t.Run("create a schema in production env", func(t *testing.T) {
+               var (
+                       serviceIdPro1 string
+                       serviceIdPro2 string
+               )
+
+               respCreateService, err := 
datasource.Instance().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, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdPro1 = respCreateService.ServiceId
 
-               respAddTages, err := 
datasource.Instance().AddTags(getContext(), &pb.AddServiceTagsRequest{
-                       ServiceId: "delete_tag_group_ms_id",
-                       Tags: map[string]string{
-                               "a": "test",
-                               "b": "b",
+               respCreateService, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respAddTages.Response.GetCode())
-       })
-       t.Run("the request is valid, should pass", func(t *testing.T) {
-               resp, err := datasource.Instance().DeleteTags(getContext(), 
&pb.DeleteServiceTagsRequest{
-                       ServiceId: "delete_tag_group_ms_id",
-                       Keys:      []string{"b"},
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdPro2 = respCreateService.ServiceId
+
+               respModifySchema, err := 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro1,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               respModifySchema, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro1,
+                       SchemaId:  "second_schemaId_service_ms",
+                       Schema:    "second_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
 
-               respTags, err := datasource.Instance().GetTags(getContext(), 
&pb.GetServiceTagsRequest{
-                       ServiceId: "delete_tag_group_ms_id",
+               respModifySchema, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro2,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, respTags.Response.GetCode())
-               assert.Equal(t, "", respTags.Tags["b"])
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
        })
-}
+       //
+       t.Run("create a schema in empty env", func(t *testing.T) {
+               var (
+                       serviceIdPro1 string
+                       serviceIdPro2 string
+               )
 
-func TestRuleAdd(t *testing.T) {
-       t.Run("register service, the request is valid, should pass", func(t 
*testing.T) {
+               log.Info("register service")
                respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "create_rule_group_ms_id",
-                               AppId:       "create_rule_group_ms",
-                               ServiceName: "create_rule_service_ms",
+                               AppId:       "create_schema_empty_service_ms",
+                               ServiceName: "create_schema_service_service_ms",
                                Version:     "1.0.0",
                                Level:       "FRONT",
                                Status:      pb.MS_UP,
@@ -390,91 +1706,289 @@ func TestRuleAdd(t *testing.T) {
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
-       })
-       t.Run("register service, the request is valid, should pass", func(t 
*testing.T) {
-               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
-                       ServiceId: "create_rule_group_ms_id",
-                       Rules: []*pb.AddOrUpdateServiceRule{
-                               {
-                                       RuleType:    "BLACK",
-                                       Attribute:   "ServiceName",
-                                       Pattern:     "Test*",
-                                       Description: "test black",
+               serviceIdPro1 = respCreateService.ServiceId
+
+               respCreateService, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
-               ruleId := respAddRule.RuleIds[0]
-               assert.NotEqual(t, "", ruleId)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceIdPro2 = respCreateService.ServiceId
+
+               log.Info("create a schema for service whose schemaID is empty")
+               respModifySchema, err := 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro1,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               log.Info("modify schema for the service whose schema summary is 
empty")
+               respModifySchema, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               log.Info("modify schema for the service whose schema summary 
already exist")
+               respModifySchema, err = 
datasource.Instance().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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               log.Info("add schema")
+               respModifySchema, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro1,
+                       SchemaId:  "second_schemaId_service_ms",
+                       Schema:    "second_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
+
+               log.Info("modify schema for the service whose schemaId already 
exist")
+               respModifySchema, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceIdPro2,
+                       SchemaId:  "first_schemaId_service_ms",
+                       Schema:    "first_schema_service_ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
        })
-       t.Run("request rule is already exist, should pass", func(t *testing.T) {
-               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
-                       ServiceId: "create_rule_group_ms_id",
-                       Rules: []*pb.AddOrUpdateServiceRule{
-                               {
-                                       RuleType:    "BLACK",
-                                       Attribute:   "ServiceName",
-                                       Pattern:     "Test*",
-                                       Description: "test black",
-                               },
+       //
+       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 := 
datasource.Instance().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, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
-               assert.Equal(t, 0, len(respAddRule.RuleIds))
+               assert.Equal(t, pb.ResponseSuccess, 
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 := 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchemas.Response.GetCode())
+
+               respService, err := 
datasource.Instance().GetService(getContext(), &pb.GetServiceRequest{
+                       ServiceId: serviceIdPro1,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
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 := &mongo.DataSource{SchemaEditable: false}
+               respModifySchemas, err = 
localMicroServiceDs.ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrUndefinedSchemaID, 
respModifySchemas.Response.GetCode())
+
+               log.Info("schema edit allowed, add a schema with new schemaId, 
should pass")
+               localMicroServiceDs = &mongo.DataSource{SchemaEditable: true}
+               respModifySchemas, err = 
localMicroServiceDs.ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
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 := 
datasource.Instance().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, pb.ResponseSuccess, 
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 := 
datasource.Instance().ModifySchemas(getContext(), &pb.ModifySchemasRequest{
+                       ServiceId: serviceIdPro1,
+                       Schemas:   schemas,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respModifySchemas.Response.GetCode())
+
+               respService, err := 
datasource.Instance().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 := &mongo.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, pb.ErrModifySchemaNotAllow, 
respModifySchema.Response.GetCode())
+
+               log.Info("schema edit allowed, add a schema with new schemaId, 
should pass")
+               localMicroServiceDs = &mongo.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, pb.ResponseSuccess, 
respModifySchema.Response.GetCode())
        })
 }
 
-func TestRuleGet(t *testing.T) {
-       t.Run("register service and rules, the request is valid, should pass", 
func(t *testing.T) {
+func TestSchema_Exist(t *testing.T) {
+       var (
+               serviceId string
+       )
+
+       t.Run("register service and add schema", func(t *testing.T) {
+               log.Info("register service")
                respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "get_rule_group_ms_id",
-                               AppId:       "get_rule_group_ms",
-                               ServiceName: "get_rule_service_ms",
+                               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, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId = respCreateService.ServiceId
 
-               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
-                       ServiceId: "get_rule_group_ms_id",
-                       Rules: []*pb.AddOrUpdateServiceRule{
-                               {
-                                       RuleType:    "BLACK",
-                                       Attribute:   "ServiceName",
-                                       Pattern:     "Test*",
-                                       Description: "test BLACK",
-                               },
-                       },
+               log.Info("add schemas, should pass")
+               resp, err := datasource.Instance().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, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
-               ruleId := respAddRule.RuleIds[0]
-               assert.NotEqual(t, "", ruleId)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().ModifySchema(getContext(), 
&pb.ModifySchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.no.summary.ms",
+                       Schema:    "query schema ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
        })
-       t.Run("get rule,  when request is valid, should pass", func(t 
*testing.T) {
-               respGetRule, err := 
datasource.Instance().GetRules(getContext(), &pb.GetServiceRulesRequest{
-                       ServiceId: "get_rule_group_ms_id",
+
+       t.Run("check exists", func(t *testing.T) {
+               log.Info("check schema exist, should pass")
+               resp, err := datasource.Instance().ExistSchema(getContext(), 
&pb.GetExistenceRequest{
+                       Type:      service.ExistTypeSchema,
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
respGetRule.Response.GetCode())
-               assert.Equal(t, 1, len(respGetRule.Rules))
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, "summary_ms", resp.Summary)
+
+               resp, err = datasource.Instance().ExistSchema(getContext(), 
&pb.GetExistenceRequest{
+                       Type:        service.ExistTypeSchema,
+                       ServiceId:   serviceId,
+                       SchemaId:    "com.huawei.test.ms",
+                       AppId:       "()",
+                       ServiceName: "",
+                       Version:     "()",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+
+               resp, err = datasource.Instance().ExistSchema(getContext(), 
&pb.GetExistenceRequest{
+                       Type:      service.ExistTypeSchema,
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.no.summary.ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, "com.huawei.test.no.summary.ms", resp.SchemaId)
+               assert.Equal(t, "", resp.Summary)
        })
 }
 
-func TestRuleDelete(t *testing.T) {
-       var ruleId string
-       t.Run("register service and rules, when request is valid, should pass", 
func(t *testing.T) {
+func TestSchema_Delete(t *testing.T) {
+       var (
+               serviceId string
+       )
+
+       t.Run("register service and instance", func(t *testing.T) {
                respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "delete_rule_group_ms_id",
-                               AppId:       "delete_rule_group_ms",
-                               ServiceName: "delete_rule_service_ms",
+                               AppId:       "delete_schema_group_ms",
+                               ServiceName: "delete_schema_service_ms",
                                Version:     "1.0.0",
                                Level:       "FRONT",
                                Status:      pb.MS_UP,
@@ -482,84 +1996,212 @@ func TestRuleDelete(t *testing.T) {
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId = respCreateService.ServiceId
 
-               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
-                       ServiceId: "delete_rule_group_ms_id",
-                       Rules: []*pb.AddOrUpdateServiceRule{
-                               {
-                                       RuleType:    "BLACK",
-                                       Attribute:   "ServiceName",
-                                       Pattern:     "Test*",
-                                       Description: "test BLACK",
-                               },
-                       },
+               resp, err := datasource.Instance().ModifySchema(getContext(), 
&pb.ModifySchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
+                       Schema:    "delete schema ms",
+                       Summary:   "summary_ms",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
-               ruleId = respAddRule.RuleIds[0]
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
        })
-       t.Run("delete rule, when request is valid, should pass", func(t 
*testing.T) {
-               resp, err := datasource.Instance().DeleteRule(getContext(), 
&pb.DeleteServiceRulesRequest{
-                       ServiceId: "delete_rule_group_ms_id",
-                       RuleIds:   []string{ruleId},
+
+       t.Run("test delete when request is invalid", func(t *testing.T) {
+               log.Info("schema id does not exist")
+               resp, err := datasource.Instance().DeleteSchema(getContext(), 
&pb.DeleteSchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "none_exist_schema",
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
 
-               respGetRule, err := 
datasource.Instance().GetRules(getContext(), &pb.GetServiceRulesRequest{
-                       ServiceId: "delete_rule_group_ms_id",
+               log.Info("service id does not exist")
+               resp, err = datasource.Instance().DeleteSchema(getContext(), 
&pb.DeleteSchemaRequest{
+                       ServiceId: "not_exist_service",
+                       SchemaId:  "com.huawei.test.ms",
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+       })
+       //
+       t.Run("test delete when request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().DeleteSchema(getContext(), 
&pb.DeleteSchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-               assert.Equal(t, 1, len(respGetRule.Rules))
+
+               respGet, err := datasource.Instance().GetSchema(getContext(), 
&pb.GetSchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrSchemaNotExists, 
respGet.Response.GetCode())
+
+               respExist, err := 
datasource.Instance().ExistSchema(getContext(), &pb.GetExistenceRequest{
+                       Type:      "schema",
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrSchemaNotExists, 
respExist.Response.GetCode())
        })
 }
 
-func TestRuleUpdate(t *testing.T) {
-       var ruleId string
-       t.Run("create service and rules, when request is valid, should pass", 
func(t *testing.T) {
+func TestSchema_Get(t *testing.T) {
+       var (
+               serviceId  string
+               serviceId1 string
+       )
+
+       var (
+               schemaId1     string = "all_schema1_ms"
+               schemaId2     string = "all_schema2_ms"
+               schemaId3     string = "all_schema3_ms"
+               summary       string = "this0is1a2test3ms"
+               schemaContent string = "the content is vary large"
+       )
+
+       t.Run("register service and instance", func(t *testing.T) {
                respCreateService, err := 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
                        Service: &pb.MicroService{
-                               ServiceId:   "update_rule_group_ms_id",
-                               AppId:       "update_rule_group_ms",
-                               ServiceName: "update_rule_service_ms",
+                               AppId:       "get_schema_group_ms",
+                               ServiceName: "get_schema_service_ms",
                                Version:     "1.0.0",
                                Level:       "FRONT",
+                               Schemas: []string{
+                                       "non-schema-content",
+                               },
                                Status:      pb.MS_UP,
+                               Environment: pb.ENV_DEV,
                        },
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId = respCreateService.ServiceId
 
-               respAddRule, err := datasource.Instance().AddRule(getContext(), 
&pb.AddServiceRulesRequest{
-                       ServiceId: "update_rule_group_ms_id",
-                       Rules: []*pb.AddOrUpdateServiceRule{
-                               {
-                                       RuleType:    "BLACK",
-                                       Attribute:   "ServiceName",
-                                       Pattern:     "Test*",
-                                       Description: "test BLACK",
+               respCreateSchema, err := 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
+                       Schema:    "get schema ms",
+                       Summary:   "schema0summary1ms",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateSchema.Response.GetCode())
+
+               respCreateService, err = 
datasource.Instance().RegisterService(getContext(), &pb.CreateServiceRequest{
+                       Service: &pb.MicroService{
+                               AppId:       "get_all_schema_ms",
+                               ServiceName: "get_all_schema_ms",
+                               Version:     "1.0.0",
+                               Level:       "FRONT",
+                               Schemas: []string{
+                                       schemaId1,
+                                       schemaId2,
+                                       schemaId3,
                                },
+                               Status: pb.MS_UP,
                        },
                })
                assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, 
respAddRule.Response.GetCode())
-               assert.NotEqual(t, "", respAddRule.RuleIds[0])
-               ruleId = respAddRule.RuleIds[0]
+               assert.Equal(t, pb.ResponseSuccess, 
respCreateService.Response.GetCode())
+               serviceId1 = respCreateService.ServiceId
+               //
+               respPutData, err := 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceId1,
+                       SchemaId:  schemaId2,
+                       Schema:    schemaContent,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respPutData.Response.GetCode())
+               //
+               respPutData, err = 
datasource.Instance().ModifySchema(getContext(), &pb.ModifySchemaRequest{
+                       ServiceId: serviceId1,
+                       SchemaId:  schemaId3,
+                       Schema:    schemaContent,
+                       Summary:   summary,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respPutData.Response.GetCode())
+               //
+               respGetAllSchema, err := 
datasource.Instance().GetAllSchemas(getContext(), &pb.GetAllSchemaRequest{
+                       ServiceId:  serviceId1,
+                       WithSchema: false,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respGetAllSchema.Response.GetCode())
+               schemas := respGetAllSchema.Schemas
+               for _, schema := range schemas {
+                       fmt.Println(schema.SchemaId)
+                       if schema.SchemaId == schemaId1 && schema.SchemaId == 
schemaId2 {
+                               assert.Empty(t, schema.Summary)
+                               assert.Empty(t, schema.Schema)
+                       }
+                       if schema.SchemaId == schemaId3 {
+                               assert.Equal(t, summary, schema.Summary)
+                               assert.Empty(t, schema.Schema)
+                       }
+               }
+               //
+               respGetAllSchema, err = 
datasource.Instance().GetAllSchemas(getContext(), &pb.GetAllSchemaRequest{
+                       ServiceId:  serviceId1,
+                       WithSchema: true,
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ResponseSuccess, 
respGetAllSchema.Response.GetCode())
+               schemas = respGetAllSchema.Schemas
+               for _, schema := range schemas {
+                       switch schema.SchemaId {
+                       case schemaId1:
+                               assert.Empty(t, schema.Summary)
+                               assert.Empty(t, schema.Schema)
+                       case schemaId2:
+                               assert.Empty(t, schema.Summary)
+                               assert.Equal(t, schemaContent, schema.Schema)
+                       case schemaId3:
+                               assert.Equal(t, summary, schema.Summary)
+                               assert.Equal(t, schemaContent, schema.Schema)
+                       }
+               }
        })
-       t.Run("update rule, when request is valid, should pass", func(t 
*testing.T) {
-               resp, err := datasource.Instance().UpdateRule(getContext(), 
&pb.UpdateServiceRuleRequest{
-                       ServiceId: "update_rule_group_ms_id",
-                       RuleId:    ruleId,
-                       Rule: &pb.AddOrUpdateServiceRule{
-                               RuleType:    "BLACK",
-                               Attribute:   "AppId",
-                               Pattern:     "Test*",
-                               Description: "test white update",
-                       },
+
+       t.Run("test get when request is invalid", func(t *testing.T) {
+               log.Info("service does not exist")
+               respGetSchema, err := 
datasource.Instance().GetSchema(getContext(), &pb.GetSchemaRequest{
+                       ServiceId: "none_exist_service",
+                       SchemaId:  "com.huawei.test",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, 
respGetSchema.Response.GetCode())
+
+               respGetAllSchemas, err := 
datasource.Instance().GetAllSchemas(getContext(), &pb.GetAllSchemaRequest{
+                       ServiceId: "none_exist_service",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrServiceNotExists, 
respGetAllSchemas.Response.GetCode())
+
+               log.Info("schema id doest not exist")
+               respGetSchema, err = 
datasource.Instance().GetSchema(getContext(), &pb.GetSchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "none_exist_schema",
+               })
+               assert.NoError(t, err)
+               assert.Equal(t, pb.ErrSchemaNotExists, 
respGetSchema.Response.GetCode())
+       })
+
+       t.Run("test get when request is valid", func(t *testing.T) {
+               resp, err := datasource.Instance().GetSchema(getContext(), 
&pb.GetSchemaRequest{
+                       ServiceId: serviceId,
+                       SchemaId:  "com.huawei.test.ms",
                })
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
+               assert.Equal(t, "get schema ms", resp.Schema)
+               assert.Equal(t, "schema0summary1ms", resp.SchemaSummary)
+
        })
 }
 
diff --git a/datasource/mongo/util.go b/datasource/mongo/util.go
index 8de33ae..b7e045c 100644
--- a/datasource/mongo/util.go
+++ b/datasource/mongo/util.go
@@ -17,7 +17,16 @@
 
 package mongo
 
-import "strings"
+import (
+       "context"
+       "strings"
+
+       "github.com/apache/servicecomb-service-center/datasource"
+       "github.com/apache/servicecomb-service-center/pkg/gopool"
+       "github.com/apache/servicecomb-service-center/pkg/util"
+       pb "github.com/go-chassis/cari/discovery"
+       "go.mongodb.org/mongo-driver/bson"
+)
 
 func StringBuilder(data []string) string {
        var str strings.Builder
@@ -30,3 +39,63 @@ func StringBuilder(data []string) string {
        }
        return str.String()
 }
+
+func statistics(ctx context.Context, withShared bool) (*pb.Statistics, error) {
+       result := &pb.Statistics{
+               Services:  &pb.StService{},
+               Instances: &pb.StInstance{},
+               Apps:      &pb.StApp{},
+       }
+       domain := util.ParseDomain(ctx)
+       project := util.ParseProject(ctx)
+
+       filter := bson.M{ColumnDomain: domain, ColumnProject: project}
+
+       services, err := GetServices(ctx, filter)
+       if err != nil {
+               return nil, err
+       }
+
+       var svcIDs []string
+       var svcKeys []*pb.MicroServiceKey
+       for _, svc := range services {
+               svcIDs = append(svcIDs, svc.ServiceId)
+               svcKeys = append(svcKeys, 
datasource.TransServiceToKey(util.ParseDomainProject(ctx), svc))
+       }
+       svcIDToNonVerKey := datasource.SetStaticServices(result, svcKeys, 
svcIDs, withShared)
+
+       respGetInstanceCountByDomain := make(chan 
datasource.GetInstanceCountByDomainResponse, 1)
+       gopool.Go(func(_ context.Context) {
+               getInstanceCountByDomain(ctx, svcIDToNonVerKey, 
respGetInstanceCountByDomain)
+       })
+
+       instances, err := GetInstances(ctx, filter)
+       if err != nil {
+               return nil, err
+       }
+       var instIDs []string
+       for _, inst := range instances {
+               instIDs = append(instIDs, inst.InstanceInfo.ServiceId)
+       }
+       datasource.SetStaticInstances(result, svcIDToNonVerKey, instIDs)
+       data := <-respGetInstanceCountByDomain
+       close(respGetInstanceCountByDomain)
+       if data.Err != nil {
+               return nil, data.Err
+       }
+       result.Instances.CountByDomain = data.CountByDomain
+       return result, nil
+}
+
+func getInstanceCountByDomain(ctx context.Context, svcIDToNonVerKey 
map[string]string, resp chan datasource.GetInstanceCountByDomainResponse) {
+       ret := datasource.GetInstanceCountByDomainResponse{}
+       for _, sid := range svcIDToNonVerKey {
+               num, err := GetInstanceCountOfOneService(ctx, sid)
+               if err != nil {
+                       ret.Err = err
+                       return
+               }
+               ret.CountByDomain = ret.CountByDomain + num
+       }
+       resp <- ret
+}
diff --git a/datasource/ms_util.go b/datasource/ms_util.go
index 0090e33..769e544 100644
--- a/datasource/ms_util.go
+++ b/datasource/ms_util.go
@@ -17,7 +17,16 @@
 
 package datasource
 
-import "github.com/go-chassis/cari/discovery"
+import (
+       "github.com/apache/servicecomb-service-center/pkg/util"
+       "github.com/apache/servicecomb-service-center/server/core"
+       "github.com/go-chassis/cari/discovery"
+)
+
+type GetInstanceCountByDomainResponse struct {
+       Err           error
+       CountByDomain int64
+}
 
 func SetServiceDefaultValue(service *discovery.MicroService) {
        if len(service.AppId) == 0 {
@@ -33,3 +42,63 @@ func SetServiceDefaultValue(service *discovery.MicroService) 
{
                service.Status = discovery.MS_UP
        }
 }
+
+// SetStaticServices calculate the service/application num under a 
domainProject
+func SetStaticServices(statistics *discovery.Statistics, svcKeys 
[]*discovery.MicroServiceKey, svcIDs []string, withShared bool) 
map[string]string {
+       l := len(svcKeys)
+       app := make(map[string]struct{}, l)
+       svcWithNonVersion := make(map[string]struct{}, l)
+       svcIDToNonVerKey := make(map[string]string, l)
+       for index, svc := range svcKeys {
+               if !withShared && core.IsGlobal(svc) {
+                       continue
+               }
+               if _, ok := app[svc.AppId]; !ok {
+                       app[svc.AppId] = struct{}{}
+               }
+               svc.Version = ""
+               svcWithNonVersionKey := generateServiceKey(svc)
+               if _, ok := svcWithNonVersion[svcWithNonVersionKey]; !ok {
+                       svcWithNonVersion[svcWithNonVersionKey] = struct{}{}
+               }
+               svcIDToNonVerKey[svcIDs[index]] = svcWithNonVersionKey
+       }
+       statistics.Services.Count = int64(len(svcWithNonVersion))
+       statistics.Apps.Count = int64(len(app))
+       return svcIDToNonVerKey
+}
+
+// SetStaticInstances calculate the instance/onlineService num under a 
domainProject
+func SetStaticInstances(statistics *discovery.Statistics, svcIDToNonVerKey 
map[string]string, instServiceIDs []string) {
+       onlineServices := make(map[string]struct{}, len(instServiceIDs))
+       for _, sid := range instServiceIDs {
+               key, ok := svcIDToNonVerKey[sid]
+               if !ok {
+                       continue
+               }
+               statistics.Instances.Count++
+               if _, ok := onlineServices[key]; !ok {
+                       onlineServices[key] = struct{}{}
+               }
+       }
+       statistics.Services.OnlineCount = int64(len(onlineServices))
+}
+
+func generateServiceKey(key *discovery.MicroServiceKey) string {
+       return util.StringJoin([]string{
+               key.Environment,
+               key.AppId,
+               key.ServiceName,
+               key.Version,
+       }, "/")
+}
+
+func TransServiceToKey(domainProject string, service *discovery.MicroService) 
*discovery.MicroServiceKey {
+       return &discovery.MicroServiceKey{
+               Tenant:      domainProject,
+               Environment: service.Environment,
+               AppId:       service.AppId,
+               ServiceName: service.ServiceName,
+               Version:     service.Version,
+       }
+}

Reply via email to