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

[servicecomb-kie] branch master updated: SCB-1700 support kv revision management (#61)

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-kie.git


The following commit(s) were added to refs/heads/master by this push:
     new ada262a  SCB-1700 support kv revision management (#61)
ada262a is described below

commit ada262a5377fac2da2cb50acc5b32e6f433c2d9b
Author: Shawn <xi...@gmail.com>
AuthorDate: Sun Jan 5 11:41:29 2020 +0800

    SCB-1700 support kv revision management (#61)
    
    * SCB-1700 support kv revision management
    
    * SCB-1700 fix wrong query param parsing
---
 client/client_test.go                       |   2 +-
 pkg/common/common.go                        |   1 +
 pkg/model/mongodb_doc.go                    |  27 +--
 server/resource/v1/common.go                |   5 +-
 server/resource/v1/common_test.go           |  65 +++---
 server/resource/v1/doc_struct.go            |   8 +-
 server/resource/v1/history_resource.go      |  14 +-
 server/resource/v1/history_resource_test.go | 111 +++++----
 server/resource/v1/kv_resource.go           |  25 +--
 server/resource/v1/kv_resource_test.go      |  39 +++-
 server/service/mongo/history/dao.go         |  82 ++-----
 server/service/mongo/history/service.go     |  15 +-
 server/service/mongo/history/tool.go        |  31 ---
 server/service/mongo/kv/kv_dao.go           |  70 ++----
 server/service/mongo/kv/kv_service.go       |  89 ++------
 server/service/mongo/kv/kv_suite_test.go    |  14 +-
 server/service/mongo/kv/kv_test.go          | 335 +++++++++-------------------
 server/service/mongo/label/label_dao.go     |  41 +---
 server/service/mongo/session/session.go     |   9 +-
 server/service/service.go                   |   6 +-
 20 files changed, 335 insertions(+), 654 deletions(-)

diff --git a/client/client_test.go b/client/client_test.go
index 7c58b8c..bdf5e22 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -132,7 +132,7 @@ var _ = Describe("Client", func() {
 				Endpoint: "http://127.0.0.1:30110",
 			})
 			It("should be 204", func() {
-				err := client3.Delete(context.TODO(), kv.ID.String(), "", WithProject("test"))
+				err := client3.Delete(context.TODO(), kv.ID, "", WithProject("test"))
 				Ω(err).ShouldNot(HaveOccurred())
 			})
 		})
diff --git a/pkg/common/common.go b/pkg/common/common.go
index 22ab5a3..fad017d 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -23,6 +23,7 @@ const (
 	QueryByLabelsCon = "&"
 	QueryParamWait   = "wait"
 	QueryParamMatch  = "match"
+	QueryParamKeyID  = "kvID"
 )
 
 //http headers
diff --git a/pkg/model/mongodb_doc.go b/pkg/model/mongodb_doc.go
index 7307cc2..5d6d9b6 100644
--- a/pkg/model/mongodb_doc.go
+++ b/pkg/model/mongodb_doc.go
@@ -17,22 +17,17 @@
 
 package model
 
-import (
-	"github.com/apache/servicecomb-kie/server/id"
-)
-
 //LabelDoc is database struct to store labels
 type LabelDoc struct {
-	ID       id.ID             `json:"_id,omitempty" bson:"_id,omitempty" yaml:"_id,omitempty" swag:"string"`
-	Labels   map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
-	Revision int               `json:"revision,omitempty" yaml:"revision,omitempty"`
-	Domain   string            `json:"domain,omitempty" yaml:"domain,omitempty"` //tenant info
-	Project  string            `json:"project,omitempty" yaml:"project,omitempty"`
+	ID      string            `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" swag:"string"`
+	Labels  map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
+	Domain  string            `json:"domain,omitempty" yaml:"domain,omitempty"` //tenant info
+	Project string            `json:"project,omitempty" yaml:"project,omitempty"`
 }
 
 //KVDoc is database struct to store kv
 type KVDoc struct {
-	ID        id.ID  `json:"_id,omitempty" bson:"_id,omitempty" yaml:"_id,omitempty" swag:"string"`
+	ID        string `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" swag:"string"`
 	LabelID   string `json:"label_id,omitempty" bson:"label_id,omitempty" yaml:"label_id,omitempty"`
 	Key       string `json:"key" yaml:"key"`
 	Value     string `json:"value,omitempty" yaml:"value,omitempty"`
@@ -41,16 +36,6 @@ type KVDoc struct {
 
 	Labels   map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` //redundant
 	Domain   string            `json:"domain,omitempty" yaml:"domain,omitempty"` //redundant
-	Revision int               `json:"revision,omitempty" bson:"-" yaml:"revision,omitempty"`
+	Revision int               `json:"revision,omitempty" bson:"revision," yaml:"revision,omitempty"`
 	Project  string            `json:"project,omitempty" yaml:"project,omitempty"`
 }
-
-//LabelRevisionDoc is database struct to store label history stats
-type LabelRevisionDoc struct {
-	ID       id.ID             `json:"_id,omitempty" bson:"_id,omitempty" yaml:"_id,omitempty" swag:"string"`
-	LabelID  string            `json:"label_id,omitempty"  bson:"label_id,omitempty" yaml:"label_id,omitempty"`
-	Labels   map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
-	Domain   string            `json:"-" yaml:"-"`
-	KVs      []*KVDoc          `json:"data,omitempty" bson:"data,omitempty" yaml:"data,omitempty"`
-	Revision int               `json:"revision" yaml:"revision"`
-}
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index 2db72c9..538a2bd 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -137,8 +137,9 @@ func writeResponse(ctx *restful.Context, v interface{}) error {
 	}
 	return ctx.WriteJSON(v, goRestful.MIME_JSON) // json is default
 }
-func getLabels(labelStr string) (map[string]string, error) {
-	labelsSlice := strings.Split(labelStr, ",")
+
+//GetLabels parse labels
+func GetLabels(labelsSlice []string) (map[string]string, error) {
 	labels := make(map[string]string, len(labelsSlice))
 	for _, v := range labelsSlice {
 		v := strings.Split(v, ":")
diff --git a/server/resource/v1/common_test.go b/server/resource/v1/common_test.go
index 792383c..d1ba7a7 100644
--- a/server/resource/v1/common_test.go
+++ b/server/resource/v1/common_test.go
@@ -18,47 +18,36 @@
 package v1_test
 
 import (
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
-	. "github.com/apache/servicecomb-kie/server/resource/v1"
+	v1 "github.com/apache/servicecomb-kie/server/resource/v1"
 	"github.com/emicklei/go-restful"
+	"github.com/stretchr/testify/assert"
 	"net/http"
+	"testing"
 )
 
-var _ = Describe("Common", func() {
-	Describe("set query combination", func() {
-		Context("valid param", func() {
-			r, err := http.NewRequest("GET",
-				"/kv?q=app:mall+service:payment&q=app:mall+service:payment+version:1.0.0",
-				nil)
-			It("should not return err ", func() {
-				Expect(err).Should(BeNil())
-			})
-			c, err := ReadLabelCombinations(restful.NewRequest(r))
-			It("should not return err ", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 2 combinations", func() {
-				Expect(len(c)).Should(Equal(2))
-			})
+func TestGetLabels(t *testing.T) {
+	r, err := http.NewRequest("GET",
+		"/kv?q=app:mall+service:payment&q=app:mall+service:payment+version:1.0.0",
+		nil)
+	assert.NoError(t, err)
+	c, err := v1.ReadLabelCombinations(restful.NewRequest(r))
+	assert.NoError(t, err)
+	assert.Equal(t, 2, len(c))
 
-		})
-		Context("find default", func() {
-			r, err := http.NewRequest("GET",
-				"/kv",
-				nil)
-			It("should not return err ", func() {
-				Expect(err).Should(BeNil())
-			})
-			c, err := ReadLabelCombinations(restful.NewRequest(r))
-			It("should not return err ", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 1 combinations", func() {
-				Expect(len(c)).Should(Equal(1))
-			})
+	r, err = http.NewRequest("GET",
+		"/kv",
+		nil)
+	assert.NoError(t, err)
+	c, err = v1.ReadLabelCombinations(restful.NewRequest(r))
+	assert.NoError(t, err)
+	assert.Equal(t, 1, len(c))
 
-		})
-	})
-})
+	r, err = http.NewRequest("GET",
+		"/kv?label=app:mall&label=service:payment",
+		nil)
+	assert.NoError(t, err)
+	req := restful.NewRequest(r)
+	m, err := v1.GetLabels(req.QueryParameters("label"))
+	assert.NoError(t, err)
+	assert.Equal(t, 2, len(m))
+}
diff --git a/server/resource/v1/doc_struct.go b/server/resource/v1/doc_struct.go
index 6a00529..00e2135 100644
--- a/server/resource/v1/doc_struct.go
+++ b/server/resource/v1/doc_struct.go
@@ -62,9 +62,9 @@ var (
 			"if it is empty, server will return kv which's labels partial match the label query param. " +
 			"uf it is exact, server will return kv which's labels exact match the label query param",
 	}
-	DocQueryKVIDParameters = &restful.Parameters{
+	DocQueryKeyIDParameters = &restful.Parameters{
 		DataType:  "string",
-		Name:      "kvID",
+		Name:      common.QueryParamKeyID,
 		ParamType: goRestful.QueryParameterKind,
 		Required:  true,
 	}
@@ -101,9 +101,9 @@ var (
 		ParamType: goRestful.PathParameterKind,
 		Required:  true,
 	}
-	DocPathLabelID = &restful.Parameters{
+	DocPathKeyID = &restful.Parameters{
 		DataType:  "string",
-		Name:      "label_id",
+		Name:      "key_id",
 		ParamType: goRestful.PathParameterKind,
 		Required:  true,
 	}
diff --git a/server/resource/v1/history_resource.go b/server/resource/v1/history_resource.go
index 5f408a2..44c21fc 100644
--- a/server/resource/v1/history_resource.go
+++ b/server/resource/v1/history_resource.go
@@ -36,10 +36,10 @@ type HistoryResource struct {
 //GetRevisions search key only by label
 func (r *HistoryResource) GetRevisions(context *restful.Context) {
 	var err error
-	labelID := context.ReadPathParameter("label_id")
+	labelID := context.ReadPathParameter("key_id")
 	if labelID == "" {
-		openlogging.Debug("label id is null")
-		WriteErrResponse(context, http.StatusForbidden, "label_id must not be empty", common.ContentTypeText)
+		openlogging.Error("key id is nil")
+		WriteErrResponse(context, http.StatusForbidden, "key_id must not be empty", common.ContentTypeText)
 		return
 	}
 	key := context.ReadQueryParameter("key")
@@ -67,17 +67,17 @@ func (r *HistoryResource) URLPatterns() []restful.Route {
 	return []restful.Route{
 		{
 			Method:       http.MethodGet,
-			Path:         "/v1/{project}/kie/revision/{label_id}",
+			Path:         "/v1/{project}/kie/revision/{key_id}",
 			ResourceFunc: r.GetRevisions,
-			FuncDesc:     "get all revisions by label id",
+			FuncDesc:     "get all revisions by key id",
 			Parameters: []*restful.Parameters{
-				DocPathProject, DocPathLabelID, DocQueryKeyParameters,
+				DocPathProject, DocPathKeyID,
 			},
 			Returns: []*restful.Returns{
 				{
 					Code:    http.StatusOK,
 					Message: "true",
-					Model:   []model.LabelHistoryResponse{},
+					Model:   []model.KVDoc{},
 				},
 			},
 			Consumes: []string{goRestful.MIME_JSON, common.ContentTypeYaml},
diff --git a/server/resource/v1/history_resource_test.go b/server/resource/v1/history_resource_test.go
index c088f09..07004c6 100644
--- a/server/resource/v1/history_resource_test.go
+++ b/server/resource/v1/history_resource_test.go
@@ -19,74 +19,67 @@ package v1_test
 import (
 	"context"
 	"encoding/json"
+	"fmt"
+	"github.com/apache/servicecomb-kie/pkg/model"
+	v1 "github.com/apache/servicecomb-kie/server/resource/v1"
 	"github.com/apache/servicecomb-kie/server/service"
-	"io/ioutil"
-
 	"github.com/go-chassis/go-chassis/core/common"
 	"github.com/go-chassis/go-chassis/core/handler"
-
-	"fmt"
+	"github.com/go-chassis/go-chassis/server/restful/restfultest"
+	"github.com/stretchr/testify/assert"
+	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
+	"testing"
 
-	"github.com/apache/servicecomb-kie/pkg/model"
-	"github.com/apache/servicecomb-kie/server/config"
-	v1 "github.com/apache/servicecomb-kie/server/resource/v1"
 	_ "github.com/apache/servicecomb-kie/server/service/mongo"
-	"github.com/go-chassis/go-chassis/server/restful/restfultest"
-
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 )
 
-var _ = Describe("v1 history resource", func() {
-
-	config.Configurations = &config.Config{
-		DB: config.DB{},
+func TestHistoryResource_GetRevisions(t *testing.T) {
+	kv := &model.KVDoc{
+		Key:   "test",
+		Value: "revisions",
+		Labels: map[string]string{
+			"test": "revisions",
+		},
+		Domain:  "default",
+		Project: "test",
 	}
+	kv, _ = service.KVService.CreateOrUpdate(context.Background(), kv)
+	path := fmt.Sprintf("/v1/test/kie/revision/%s", kv.ID)
+	r, _ := http.NewRequest("GET", path, nil)
+	revision := &v1.HistoryResource{}
+	chain, _ := handler.GetChain(common.Provider, "")
+	c, err := restfultest.New(revision, chain)
+	assert.NoError(t, err)
+	resp := httptest.NewRecorder()
+	c.ServeHTTP(resp, r)
+	body, err := ioutil.ReadAll(resp.Body)
+	assert.NoError(t, err)
+	data := make([]*model.KVDoc, 0)
+	err = json.Unmarshal(body, &data)
+	assert.NoError(t, err)
+	before := len(data)
+	assert.GreaterOrEqual(t, before, 1)
 
-	Describe("get history revisions", func() {
-		config.Configurations.DB.URI = "mongodb://kie:123@127.0.0.1:27017"
-		err := service.DBInit()
-		It("should not return err", func() {
-			Expect(err).Should(BeNil())
-		})
-		Context("valid param", func() {
-			kv := &model.KVDoc{
-				Key:   "test",
-				Value: "revisions",
-				Labels: map[string]string{
-					"test": "revisions",
-				},
-				Domain:  "default",
-				Project: "test",
-			}
-			kv, _ = service.KVService.CreateOrUpdate(context.Background(), kv)
-			path := fmt.Sprintf("/v1/%s/kie/revision/%s", "test", kv.LabelID)
-			r, _ := http.NewRequest("GET", path, nil)
-			revision := &v1.HistoryResource{}
-			chain, _ := handler.GetChain(common.Provider, "")
-			c, err := restfultest.New(revision, chain)
-			It("should not return err or nil", func() {
-				Expect(err).Should(BeNil())
-			})
-			resp := httptest.NewRecorder()
-			c.ServeHTTP(resp, r)
-
-			body, err := ioutil.ReadAll(resp.Body)
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			data := make([]*model.LabelRevisionDoc, 0)
-			err = json.Unmarshal(body, &data)
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-
-			It("should return all revisions with the same label ID", func() {
-				Expect(len(data) > 0).Should(Equal(true))
-				Expect((data[0]).LabelID).Should(Equal(kv.LabelID))
-			})
-		})
+	t.Run("put again, should has 2 revision", func(t *testing.T) {
+		kv.Domain = "default"
+		kv.Project = "test"
+		kv, err = service.KVService.CreateOrUpdate(context.Background(), kv)
+		assert.NoError(t, err)
+		path := fmt.Sprintf("/v1/test/kie/revision/%s", kv.ID)
+		r, _ := http.NewRequest("GET", path, nil)
+		revision := &v1.HistoryResource{}
+		chain, _ := handler.GetChain(common.Provider, "")
+		c, err := restfultest.New(revision, chain)
+		assert.NoError(t, err)
+		resp := httptest.NewRecorder()
+		c.ServeHTTP(resp, r)
+		body, err := ioutil.ReadAll(resp.Body)
+		assert.NoError(t, err)
+		data := make([]*model.KVDoc, 0)
+		err = json.Unmarshal(body, &data)
+		assert.Equal(t, before+1, len(data))
 	})
-})
+
+}
diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go
index c56b356..f38c9eb 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -85,10 +85,10 @@ func (r *KVResource) GetByKey(rctx *restful.Context) {
 		return
 	}
 	project := rctx.ReadPathParameter("project")
-	labelStr := rctx.ReadQueryParameter("label")
+	labelSlice := rctx.Req.QueryParameters("label")
 	var labels map[string]string
-	if labelStr != "" {
-		labels, err = getLabels(labelStr)
+	if len(labelSlice) != 0 {
+		labels, err = GetLabels(labelSlice)
 		if err != nil {
 			WriteErrResponse(rctx, http.StatusBadRequest, MsgIllegalLabels, common.ContentTypeText)
 			return
@@ -131,10 +131,10 @@ func (r *KVResource) List(rctx *restful.Context) {
 		WriteErrResponse(rctx, http.StatusInternalServerError, MsgDomainMustNotBeEmpty, common.ContentTypeText)
 		return
 	}
-	labelStr := rctx.ReadQueryParameter("label")
+	labelSlice := rctx.Req.QueryParameters("label")
 	var labels map[string]string
-	if labelStr != "" {
-		labels, err = getLabels(labelStr)
+	if len(labelSlice) != 0 {
+		labels, err = GetLabels(labelSlice)
 		if err != nil {
 			WriteErrResponse(rctx, http.StatusBadRequest, MsgIllegalLabels, common.ContentTypeText)
 			return
@@ -234,18 +234,16 @@ func (r *KVResource) Delete(context *restful.Context) {
 	if domain == nil {
 		WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty, common.ContentTypeText)
 	}
-	kvID := context.ReadQueryParameter("kvID")
+	kvID := context.ReadQueryParameter(common.QueryParamKeyID)
 	if kvID == "" {
 		WriteErrResponse(context, http.StatusBadRequest, ErrKvIDMustNotEmpty, common.ContentTypeText)
 		return
 	}
-	labelID := context.ReadQueryParameter("labelID")
-	err := service.KVService.Delete(kvID, labelID, domain.(string), project)
+	err := service.KVService.Delete(context.Ctx, kvID, domain.(string), project)
 	if err != nil {
 		openlogging.Error("delete failed ,", openlogging.WithTags(openlogging.Tags{
-			"kvID":    kvID,
-			"labelID": labelID,
-			"error":   err.Error(),
+			"kvID":  kvID,
+			"error": err.Error(),
 		}))
 		WriteErrResponse(context, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
 		return
@@ -334,8 +332,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 			FuncDesc:     "delete key by kvID and labelID. Want better performance, give labelID",
 			Parameters: []*restful.Parameters{
 				DocPathProject,
-				DocQueryKVIDParameters,
-				DocQueryLabelIDParameters,
+				DocQueryKeyIDParameters,
 			},
 			Returns: []*restful.Returns{
 				{
diff --git a/server/resource/v1/kv_resource_test.go b/server/resource/v1/kv_resource_test.go
index 4787753..382e2de 100644
--- a/server/resource/v1/kv_resource_test.go
+++ b/server/resource/v1/kv_resource_test.go
@@ -40,7 +40,7 @@ import (
 	_ "github.com/apache/servicecomb-kie/server/service/mongo"
 )
 
-func TestKVResource_List(t *testing.T) {
+func init() {
 	log.Init(log.Config{
 		Writers:       []string{"stdout"},
 		LoggerLevel:   "DEBUG",
@@ -59,6 +59,9 @@ func TestKVResource_List(t *testing.T) {
 	if err != nil {
 		panic(err)
 	}
+}
+func TestKVResource_List(t *testing.T) {
+
 	pubsub.Init()
 	pubsub.Start()
 	t.Run("put kv, label is service", func(t *testing.T) {
@@ -142,4 +145,38 @@ func TestKVResource_List(t *testing.T) {
 		assert.NoError(t, err)
 		assert.Equal(t, 1, len(result.Data))
 	})
+	t.Run("get one key by label, exact match,should return 1 kv", func(t *testing.T) {
+		r, _ := http.NewRequest("GET", "/v1/test/kie/kv/timeout?label=service:utService&match=exact", nil)
+		noopH := &handler2.NoopAuthHandler{}
+		chain, _ := handler.CreateChain(common.Provider, "testchain1", noopH.Name())
+		r.Header.Set("Content-Type", "application/json")
+		kvr := &v1.KVResource{}
+		c, err := restfultest.New(kvr, chain)
+		assert.NoError(t, err)
+		resp := httptest.NewRecorder()
+		c.ServeHTTP(resp, r)
+		body, err := ioutil.ReadAll(resp.Body)
+		assert.NoError(t, err)
+		result := &model.KVResponse{}
+		err = json.Unmarshal(body, result)
+		assert.NoError(t, err)
+		assert.Equal(t, 1, len(result.Data))
+	})
+	t.Run("get one key by service label should return 2 kv", func(t *testing.T) {
+		r, _ := http.NewRequest("GET", "/v1/test/kie/kv/timeout?label=service:utService", nil)
+		noopH := &handler2.NoopAuthHandler{}
+		chain, _ := handler.CreateChain(common.Provider, "testchain1", noopH.Name())
+		r.Header.Set("Content-Type", "application/json")
+		kvr := &v1.KVResource{}
+		c, err := restfultest.New(kvr, chain)
+		assert.NoError(t, err)
+		resp := httptest.NewRecorder()
+		c.ServeHTTP(resp, r)
+		body, err := ioutil.ReadAll(resp.Body)
+		assert.NoError(t, err)
+		result := &model.KVResponse{}
+		err = json.Unmarshal(body, result)
+		assert.NoError(t, err)
+		assert.Equal(t, 2, len(result.Data))
+	})
 }
diff --git a/server/service/mongo/history/dao.go b/server/service/mongo/history/dao.go
index 8533ec0..d9021c4 100644
--- a/server/service/mongo/history/dao.go
+++ b/server/service/mongo/history/dao.go
@@ -19,101 +19,49 @@ package history
 
 import (
 	"context"
-	"fmt"
 	"github.com/apache/servicecomb-kie/pkg/model"
 	"github.com/apache/servicecomb-kie/server/service"
-	"github.com/apache/servicecomb-kie/server/service/mongo/label"
 	"github.com/apache/servicecomb-kie/server/service/mongo/session"
 	"github.com/go-mesh/openlogging"
 	"go.mongodb.org/mongo-driver/bson"
-	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/mongo/options"
 )
 
-//AddHistory increment labels revision and save current label stats to history, then update current revision to db
-func AddHistory(ctx context.Context,
-	labelRevision *model.LabelRevisionDoc, labelID string, kvs []*model.KVDoc) (int, error) {
-	labelRevision.Revision = labelRevision.Revision + 1
-
-	//save current kv states
-	labelRevision.KVs = kvs
-	//clear prev id
-	labelRevision.ID = ""
-	collection := session.GetDB().Collection(session.CollectionLabelRevision)
-	_, err := collection.InsertOne(ctx, labelRevision)
-	if err != nil {
-		openlogging.Error(err.Error())
-		return 0, err
-	}
-	hex, err := primitive.ObjectIDFromHex(labelID)
-	if err != nil {
-		openlogging.Error(fmt.Sprintf("convert %s,err:%s", labelID, err))
-		return 0, err
-	}
-	labelCollection := session.GetDB().Collection(session.CollectionLabel)
-	_, err = labelCollection.UpdateOne(ctx, bson.M{"_id": hex}, bson.D{
-		{"$set", bson.D{
-			{"revision", labelRevision.Revision},
-		}},
-	})
-	if err != nil {
-		return 0, err
-	}
-	openlogging.Debug(fmt.Sprintf("update revision to %d", labelRevision.Revision))
-	return labelRevision.Revision, nil
-}
-
-func getHistoryByLabelID(ctx context.Context, filter bson.M) ([]*model.LabelRevisionDoc, error) {
-	collection := session.GetDB().Collection(session.CollectionLabelRevision)
+func getHistoryByKeyID(ctx context.Context, filter bson.M) ([]*model.KVDoc, error) {
+	collection := session.GetDB().Collection(session.CollectionKVRevision)
 	cur, err := collection.Find(ctx, filter, options.Find().SetSort(map[string]interface{}{
-		"revisions": -1,
+		"revision": -1,
 	}))
 	if err != nil {
 		return nil, err
 	}
-	rvs := make([]*model.LabelRevisionDoc, 0)
+	kvs := make([]*model.KVDoc, 0)
 	var exist bool
 	for cur.Next(ctx) {
-		var elem model.LabelRevisionDoc
+		var elem model.KVDoc
 		err := cur.Decode(&elem)
 		if err != nil {
-			openlogging.Error("decode to LabelRevisionDoc error: " + err.Error())
+			openlogging.Error("decode error: " + err.Error())
 			return nil, err
 		}
 		exist = true
-		rvs = append(rvs, &elem)
+		kvs = append(kvs, &elem)
 	}
 	if !exist {
 		return nil, service.ErrRevisionNotExist
 	}
-	return rvs, nil
+	return kvs, nil
 }
 
-//GetAndAddHistory get latest labels revision and call AddHistory
-func GetAndAddHistory(ctx context.Context,
-	labelID string, labels map[string]string, kvs []*model.KVDoc, domain string, project string) (int, error) {
+//AddHistory add kv history
+func AddHistory(ctx context.Context, kv *model.KVDoc) error {
 	ctx, cancel := context.WithTimeout(ctx, session.Timeout)
 	defer cancel()
-	r, err := label.GetLatestLabel(ctx, labelID)
+	collection := session.GetDB().Collection(session.CollectionKVRevision)
+	_, err := collection.InsertOne(ctx, kv)
 	if err != nil {
-		if err == service.ErrRevisionNotExist {
-			openlogging.Warn(fmt.Sprintf("label revision not exists, create first label revision"))
-			r = &model.LabelRevisionDoc{
-				LabelID:  labelID,
-				Labels:   labels,
-				Domain:   domain,
-				Revision: 0,
-			}
-		} else {
-			openlogging.Error(fmt.Sprintf("get latest [%s] in [%s],err: %s",
-				labelID, domain, err.Error()))
-			return 0, err
-		}
-
-	}
-	r.Revision, err = AddHistory(ctx, r, labelID, kvs)
-	if err != nil {
-		return 0, err
+		openlogging.Error(err.Error())
+		return err
 	}
-	return r.Revision, nil
+	return nil
 }
diff --git a/server/service/mongo/history/service.go b/server/service/mongo/history/service.go
index 85f7bdb..19f5969 100644
--- a/server/service/mongo/history/service.go
+++ b/server/service/mongo/history/service.go
@@ -30,20 +30,15 @@ type Service struct {
 }
 
 //GetHistory get all history by label id
-func (s *Service) GetHistory(ctx context.Context, labelID string, options ...service.FindOption) ([]*model.LabelRevisionDoc, error) {
+func (s *Service) GetHistory(ctx context.Context, kvID string, options ...service.FindOption) ([]*model.KVDoc, error) {
 	var filter primitive.M
 	opts := service.FindOptions{}
 	for _, o := range options {
 		o(&opts)
 	}
-	if opts.Key != "" {
-		filter = bson.M{
-			"label_id": labelID,
-			"data.key": opts.Key,
-		}
-
-	} else {
-		filter = bson.M{"label_id": labelID}
+	filter = bson.M{
+		"id": kvID,
 	}
-	return getHistoryByLabelID(ctx, filter)
+
+	return getHistoryByKeyID(ctx, filter)
 }
diff --git a/server/service/mongo/history/tool.go b/server/service/mongo/history/tool.go
deleted file mode 100644
index f72013d..0000000
--- a/server/service/mongo/history/tool.go
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package history
-
-import (
-	"github.com/apache/servicecomb-kie/pkg/model"
-)
-
-//clearRevisionKV clean attr which don't need to return to client side
-func clearRevisionKV(revision *model.LabelRevisionDoc) {
-	for _, v := range revision.KVs {
-		v.Domain = ""
-		v.Project = ""
-		v.LabelID = ""
-	}
-}
diff --git a/server/service/mongo/kv/kv_dao.go b/server/service/mongo/kv/kv_dao.go
index b61b88b..f8eadd1 100644
--- a/server/service/mongo/kv/kv_dao.go
+++ b/server/service/mongo/kv/kv_dao.go
@@ -20,16 +20,13 @@ package kv
 import (
 	"context"
 	"fmt"
-	"github.com/apache/servicecomb-kie/server/id"
-	"github.com/apache/servicecomb-kie/server/service"
-	"github.com/apache/servicecomb-kie/server/service/mongo/label"
-	"github.com/apache/servicecomb-kie/server/service/mongo/session"
-
 	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/apache/servicecomb-kie/server/service"
 	"github.com/apache/servicecomb-kie/server/service/mongo/history"
+	"github.com/apache/servicecomb-kie/server/service/mongo/session"
 	"github.com/go-mesh/openlogging"
+	uuid "github.com/satori/go.uuid"
 	"go.mongodb.org/mongo-driver/bson"
-	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/mongo"
 )
 
@@ -37,76 +34,51 @@ import (
 //and increase revision of label
 //and insert key
 func createKey(ctx context.Context, kv *model.KVDoc) (*model.KVDoc, error) {
-	r, err := label.GetLatestLabel(ctx, kv.LabelID)
-	if err != nil {
-		if err != service.ErrRevisionNotExist {
-			openlogging.Error(fmt.Sprintf("get latest [%s][%s] in [%s],err: %s",
-				kv.Key, kv.Labels, kv.Domain, err.Error()))
-			return nil, err
-		}
-		//the first time labels is created, at this time, labels has no revision yet
-		//after first key created, labels got revision 1
-		r = &model.LabelRevisionDoc{Revision: 0}
-	}
-	if r != nil {
-		r.Revision = r.Revision + 1
-	}
 	collection := session.GetDB().Collection(session.CollectionKV)
-	res, err := collection.InsertOne(ctx, kv)
+	kv.ID = uuid.NewV4().String()
+	kv.Revision = 1
+	_, err := collection.InsertOne(ctx, kv)
 	if err != nil {
 		return nil, err
 	}
-	objectID, _ := res.InsertedID.(primitive.ObjectID)
-	kv.ID = id.ID(objectID.Hex())
-	kvs, err := findKeys(ctx, bson.M{"label_id": kv.LabelID}, true)
-	//Key may be empty When delete
-	if err != nil && err != service.ErrKeyNotExists {
-		return nil, err
-	}
-	revision, err := history.GetAndAddHistory(ctx, kv.LabelID, kv.Labels, kvs, kv.Domain, kv.Project)
+	err = history.AddHistory(ctx, kv)
 	if err != nil {
 		openlogging.Warn(
 			fmt.Sprintf("can not updateKeyValue version for [%s] [%s] in [%s]",
 				kv.Key, kv.Labels, kv.Domain))
 	}
 	openlogging.Debug(fmt.Sprintf("create %s with labels %s value [%s]", kv.Key, kv.Labels, kv.Value))
-	kv.Revision = revision
+
 	return kv, nil
 
 }
 
-//updateKeyValue get latest revision from history
-//and increase revision of label
-//and updateKeyValue and them add new revision
-func updateKeyValue(ctx context.Context, kv *model.KVDoc) (int, error) {
+//updateKeyValue update key value and add new revision
+func updateKeyValue(ctx context.Context, kv *model.KVDoc) error {
 	collection := session.GetDB().Collection(session.CollectionKV)
 	ur, err := collection.UpdateOne(ctx, bson.M{"key": kv.Key, "label_id": kv.LabelID}, bson.D{
 		{"$set", bson.D{
 			{"value", kv.Value},
 			{"checker", kv.Checker},
+			{"revision", kv.Revision},
 		}},
 	})
 	if err != nil {
-		return 0, err
+		return err
 	}
 	openlogging.Debug(
 		fmt.Sprintf("updateKeyValue %s with labels %s value [%s] %d ",
 			kv.Key, kv.Labels, kv.Value, ur.ModifiedCount))
-	kvs, err := findKeys(ctx, bson.M{"label_id": kv.LabelID}, true)
-	//Key may be empty When delete
-	if err != nil && err != service.ErrKeyNotExists {
-		return 0, err
-	}
-	revision, err := history.GetAndAddHistory(ctx, kv.LabelID, kv.Labels, kvs, kv.Domain, kv.Project)
+	err = history.AddHistory(ctx, kv)
 	if err != nil {
-		openlogging.Warn(
-			fmt.Sprintf("can not label revision for [%s] [%s] in [%s],err: %s",
+		openlogging.Error(
+			fmt.Sprintf("can not add revision for [%s] [%s] in [%s],err: %s",
 				kv.Key, kv.Labels, kv.Domain, err))
 	}
 	openlogging.Debug(
 		fmt.Sprintf("add history %s with labels %s value [%s] %d ",
 			kv.Key, kv.Labels, kv.Value, ur.ModifiedCount))
-	return revision, nil
+	return nil
 
 }
 
@@ -153,18 +125,18 @@ func findOneKey(ctx context.Context, filter bson.M) ([]*model.KVDoc, error) {
 }
 
 //deleteKV by kvID
-func deleteKV(ctx context.Context, hexID primitive.ObjectID, project string) error {
+func deleteKV(ctx context.Context, kvID, project, domain string) error {
 	collection := session.GetDB().Collection(session.CollectionKV)
-	dr, err := collection.DeleteOne(ctx, bson.M{"_id": hexID, "project": project})
+	dr, err := collection.DeleteOne(ctx, bson.M{"id": kvID, "project": project, "domain": domain})
 	//check error and delete number
 	if err != nil {
-		openlogging.Error(fmt.Sprintf("delete [%s] failed : [%s]", hexID, err))
+		openlogging.Error(fmt.Sprintf("delete [%s] failed : [%s]", kvID, err))
 		return err
 	}
 	if dr.DeletedCount != 1 {
-		openlogging.Warn(fmt.Sprintf("Failed,May have been deleted,kvID=%s", hexID))
+		openlogging.Warn(fmt.Sprintf("failed, may have been deleted,kvID=%s", kvID))
 	} else {
-		openlogging.Info(fmt.Sprintf("delete success,kvID=%s", hexID))
+		openlogging.Info(fmt.Sprintf("delete success,kvID=%s", kvID))
 	}
 	return err
 }
diff --git a/server/service/mongo/kv/kv_service.go b/server/service/mongo/kv/kv_service.go
index 1be9160..76d1f3a 100644
--- a/server/service/mongo/kv/kv_service.go
+++ b/server/service/mongo/kv/kv_service.go
@@ -19,19 +19,16 @@ package kv
 
 import (
 	"context"
+	"errors"
 	"fmt"
-	"github.com/apache/servicecomb-kie/server/id"
 	"reflect"
 	"time"
 
 	"github.com/apache/servicecomb-kie/pkg/model"
 	"github.com/apache/servicecomb-kie/server/service"
-	"github.com/apache/servicecomb-kie/server/service/mongo/history"
 	"github.com/apache/servicecomb-kie/server/service/mongo/label"
 	"github.com/apache/servicecomb-kie/server/service/mongo/session"
 	"github.com/go-mesh/openlogging"
-	"go.mongodb.org/mongo-driver/bson"
-	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
 //Service operate data in mongodb
@@ -49,7 +46,6 @@ func (s *Service) CreateOrUpdate(ctx context.Context, kv *model.KVDoc) (*model.K
 	if kv.Domain == "" {
 		return nil, session.ErrMissingDomain
 	}
-
 	//check whether the project has certain labels or not
 	labelID, err := label.Exist(ctx, kv.Domain, kv.Project, kv.Labels)
 	if err != nil {
@@ -68,11 +64,11 @@ func (s *Service) CreateOrUpdate(ctx context.Context, kv *model.KVDoc) (*model.K
 			return nil, err
 		}
 	}
-	kv.LabelID = string(labelID)
+	kv.LabelID = labelID
 	if kv.ValueType == "" {
 		kv.ValueType = session.DefaultValueType
 	}
-	keyID, err := s.Exist(ctx, kv.Domain, kv.Key, kv.Project, service.WithLabelID(kv.LabelID))
+	oldKV, err := s.Exist(ctx, kv.Domain, kv.Key, kv.Project, service.WithLabelID(kv.LabelID))
 	if err != nil {
 		if err == service.ErrKeyNotExists {
 			kv, err := createKey(ctx, kv)
@@ -85,20 +81,20 @@ func (s *Service) CreateOrUpdate(ctx context.Context, kv *model.KVDoc) (*model.K
 		}
 		return nil, err
 	}
-	kv.ID = keyID
-	revision, err := updateKeyValue(ctx, kv)
+	kv.ID = oldKV.ID
+	kv.Revision = oldKV.Revision + 1
+	err = updateKeyValue(ctx, kv)
 	if err != nil {
 		return nil, err
 	}
-	kv.Revision = revision
 	kv.Domain = ""
 	kv.Project = ""
 	return kv, nil
 
 }
 
-//Exist supports you query by label map or labels id
-func (s *Service) Exist(ctx context.Context, domain, key string, project string, options ...service.FindOption) (id.ID, error) {
+//Exist supports you query a key value by label map or labels id
+func (s *Service) Exist(ctx context.Context, domain, key string, project string, options ...service.FindOption) (*model.KVDoc, error) {
 	ctx, _ = context.WithTimeout(context.Background(), session.Timeout)
 	opts := service.FindOptions{}
 	for _, o := range options {
@@ -107,82 +103,41 @@ func (s *Service) Exist(ctx context.Context, domain, key string, project string,
 	if opts.LabelID != "" {
 		kvs, err := findKVByLabelID(ctx, domain, opts.LabelID, key, project)
 		if err != nil {
-			return "", err
+			return nil, err
 		}
-		return kvs[0].ID, nil
+		return kvs[0], nil
 	}
-	kvs, err := s.FindKV(ctx, domain, project, service.WithExactLabels(), service.WithLabels(opts.Labels), service.WithKey(key))
+	kvs, err := s.FindKV(ctx, domain, project,
+		service.WithExactLabels(), service.WithLabels(opts.Labels), service.WithKey(key))
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 	if len(kvs) != 1 {
-		return "", session.ErrTooMany
+		return nil, session.ErrTooMany
 	}
 
-	return kvs[0].Data[0].ID, nil
+	return kvs[0].Data[0], nil
 
 }
 
 //Delete delete kv,If the labelID is "", query the collection kv to get it
 //domain=tenant
-//1.delete kv;2.add history
-func (s *Service) Delete(kvID string, labelID string, domain string, project string) error {
-	ctx, _ := context.WithTimeout(context.Background(), session.Timeout)
+func (s *Service) Delete(ctx context.Context, kvID string, domain string, project string) error {
+	ctx, _ = context.WithTimeout(context.Background(), session.Timeout)
 	if domain == "" {
 		return session.ErrMissingDomain
 	}
 	if project == "" {
 		return session.ErrMissingProject
 	}
-	hex, err := primitive.ObjectIDFromHex(kvID)
-	if err != nil {
-		return err
-	}
-	//if labelID == "",get labelID by kvID
-	var kv *model.KVDoc
-	if labelID == "" {
-		kvArray, err := findOneKey(ctx, bson.M{"_id": hex, "project": project})
-		if err != nil {
-			return err
-		}
-		if len(kvArray) > 0 {
-			kv = kvArray[0]
-			labelID = kv.LabelID
-		}
-	}
-	//get Label and check labelID
-	r, err := label.GetLatestLabel(ctx, labelID)
-	if err != nil {
-		if err == service.ErrRevisionNotExist {
-			openlogging.Warn(fmt.Sprintf("failed,kvID and labelID do not match"))
-			return session.ErrKvIDAndLabelIDNotMatch
-		}
-		return err
+	if kvID == "" {
+		return errors.New("key id is empty")
 	}
 	//delete kv
-	err = deleteKV(ctx, hex, project)
-	if err != nil {
-		return err
-	}
-	kvs, err := findKeys(ctx, bson.M{"label_id": labelID, "project": project}, true)
-	//Key may be empty When delete
-	if err != nil && err != service.ErrKeyNotExists {
-		return err
-	}
-	//Labels will not be empty when deleted
-	revision, err := history.AddHistory(ctx, r, labelID, kvs)
+	err := deleteKV(ctx, kvID, project, domain)
 	if err != nil {
-		openlogging.Warn("add history failed ,", openlogging.WithTags(openlogging.Tags{
-			"kvID":    kvID,
-			"labelID": labelID,
-			"error":   err.Error(),
-		}))
-	} else {
-		openlogging.Info("add history success,", openlogging.WithTags(openlogging.Tags{
-			"kvID":     kvID,
-			"labelID":  labelID,
-			"revision": revision,
-		}))
+		openlogging.Error("can not delete key, err:" + err.Error())
+		return errors.New("can not delete key")
 	}
 	return nil
 }
diff --git a/server/service/mongo/kv/kv_suite_test.go b/server/service/mongo/kv/kv_suite_test.go
index 3eb253f..e744c46 100644
--- a/server/service/mongo/kv/kv_suite_test.go
+++ b/server/service/mongo/kv/kv_suite_test.go
@@ -18,22 +18,12 @@
 package kv_test
 
 import (
-	"testing"
-
 	"github.com/go-chassis/paas-lager"
 	"github.com/go-mesh/openlogging"
-	. "github.com/onsi/ginkgo"
-	"github.com/onsi/ginkgo/reporters"
-	. "github.com/onsi/gomega"
 )
 
-func TestModel(t *testing.T) {
-	RegisterFailHandler(Fail)
-	junitReporter := reporters.NewJUnitReporter("junit.xml")
-	RunSpecsWithDefaultAndCustomReporters(t, "kv Suite", []Reporter{junitReporter})
-}
+func init() {
 
-var _ = BeforeSuite(func() {
 	log.Init(log.Config{
 		Writers:     []string{"stdout"},
 		LoggerLevel: "DEBUG",
@@ -41,4 +31,4 @@ var _ = BeforeSuite(func() {
 
 	logger := log.NewLogger("ut")
 	openlogging.SetLogger(logger)
-})
+}
diff --git a/server/service/mongo/kv/kv_test.go b/server/service/mongo/kv/kv_test.go
index b816aa4..710ad58 100644
--- a/server/service/mongo/kv/kv_test.go
+++ b/server/service/mongo/kv/kv_test.go
@@ -24,251 +24,134 @@ import (
 	"github.com/apache/servicecomb-kie/server/service"
 	"github.com/apache/servicecomb-kie/server/service/mongo/kv"
 	"github.com/apache/servicecomb-kie/server/service/mongo/session"
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/assert"
+	"testing"
 )
 
-var _ = Describe("Kv mongodb service", func() {
+func TestService_CreateOrUpdate(t *testing.T) {
 	var err error
-	Describe("connecting db", func() {
-		config.Configurations = &config.Config{DB: config.DB{URI: "mongodb://kie:123@127.0.0.1:27017"}}
-		err = session.Init()
-		It("should not return err", func() {
-			Expect(err).Should(BeNil())
-		})
-	})
+	config.Configurations = &config.Config{DB: config.DB{URI: "mongodb://kie:123@127.0.0.1:27017"}}
+	err = session.Init()
+	assert.NoError(t, err)
 	kvsvc := &kv.Service{}
-	Describe("put kv timeout", func() {
-
-		Context("with labels app and service", func() {
-			kv, err := kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
-				Key:   "timeout",
-				Value: "2s",
-				Labels: map[string]string{
-					"app":     "mall",
-					"service": "cart",
-				},
-				Domain:  "default",
-				Project: "test",
-			})
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has ID", func() {
-				Expect(kv.ID.String()).ShouldNot(BeEmpty())
-			})
-
+	t.Run("put kv timeout,with labels app and service", func(t *testing.T) {
+		kv, err := kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
+			Key:   "timeout",
+			Value: "2s",
+			Labels: map[string]string{
+				"app":     "mall",
+				"service": "cart",
+			},
+			Domain:  "default",
+			Project: "test",
 		})
-		Context("with labels app, service and version", func() {
-			kv, err := kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
-				Key:   "timeout",
-				Value: "2s",
-				Labels: map[string]string{
-					"app":     "mall",
-					"service": "cart",
-					"version": "1.0.0",
-				},
-				Domain:  "default",
-				Project: "test",
-			})
-			oid, err := kvsvc.Exist(context.TODO(), "default", "timeout", "test", service.WithLabels(map[string]string{
+		assert.NoError(t, err)
+		assert.NotEmpty(t, kv.ID)
+	})
+	t.Run("put kv timeout,with labels app, service and version", func(t *testing.T) {
+		kv, err := kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
+			Key:   "timeout",
+			Value: "2s",
+			Labels: map[string]string{
 				"app":     "mall",
 				"service": "cart",
 				"version": "1.0.0",
-			}))
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has ID", func() {
-				Expect(kv.ID.String()).ShouldNot(BeEmpty())
-			})
-			It("should exist", func() {
-				Expect(oid).ShouldNot(BeEmpty())
-			})
+			},
+			Domain:  "default",
+			Project: "test",
 		})
-		Context("with labels app,and update value", func() {
-			beforeKV, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
-				Key:   "timeout",
-				Value: "1s",
-				Labels: map[string]string{
-					"app": "mall",
-				},
-				Domain:  "default",
-				Project: "test",
-			})
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			kvs1, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithKey("timeout"), service.WithLabels(map[string]string{
-				"app": "mall",
-			}), service.WithExactLabels())
-			It("should be 1s", func() {
-				Expect(kvs1[0].Data[0].Value).Should(Equal(beforeKV.Value))
-			})
-			afterKV, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
-				Key:   "timeout",
-				Value: "3s",
-				Labels: map[string]string{
-					"app": "mall",
-				},
-				Domain:  "default",
-				Project: "test",
-			})
-			It("should has same id", func() {
-				Expect(afterKV.ID.String()).Should(Equal(beforeKV.ID.String()))
-			})
-			oid, err := kvsvc.Exist(context.Background(), "default", "timeout", "test", service.WithLabels(map[string]string{
+		oid, err := kvsvc.Exist(context.TODO(), "default", "timeout", "test", service.WithLabels(map[string]string{
+			"app":     "mall",
+			"service": "cart",
+			"version": "1.0.0",
+		}))
+		assert.NoError(t, err)
+		assert.NotEmpty(t, kv.ID)
+		assert.NoError(t, err)
+		assert.NotEmpty(t, oid)
+	})
+	t.Run("put kv timeout,with labels app,and update value", func(t *testing.T) {
+		beforeKV, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
+			Key:   "timeout",
+			Value: "1s",
+			Labels: map[string]string{
 				"app": "mall",
-			}))
-			It("should exists", func() {
-				Expect(oid.String()).Should(Equal(beforeKV.ID.String()))
-			})
-			kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithKey("timeout"), service.WithLabels(map[string]string{
+			},
+			Domain:  "default",
+			Project: "test",
+		})
+		assert.NoError(t, err)
+		kvs1, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithKey("timeout"), service.WithLabels(map[string]string{
+			"app": "mall",
+		}), service.WithExactLabels())
+		assert.Equal(t, beforeKV.Value, kvs1[0].Data[0].Value)
+		afterKV, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
+			Key:   "timeout",
+			Value: "3s",
+			Labels: map[string]string{
 				"app": "mall",
-			}), service.WithExactLabels())
-			It("should be 3s", func() {
-				Expect(kvs[0].Data[0].Value).Should(Equal(afterKV.Value))
-			})
+			},
+			Domain:  "default",
+			Project: "test",
 		})
+		assert.Equal(t, beforeKV.ID, afterKV.ID)
+		savedKV, err := kvsvc.Exist(context.Background(), "default", "timeout", "test", service.WithLabels(map[string]string{
+			"app": "mall",
+		}))
+		assert.Equal(t, beforeKV.ID, savedKV.ID)
+		kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithKey("timeout"), service.WithLabels(map[string]string{
+			"app": "mall",
+		}), service.WithExactLabels())
+		assert.Equal(t, afterKV.Value, kvs[0].Data[0].Value)
 	})
 
-	Describe("greedy find by kv and labels", func() {
-		Context("with labels app,depth is 1 ", func() {
-			kvs, err := kvsvc.FindKV(context.Background(), "default", "test",
-				service.WithKey("timeout"), service.WithLabels(map[string]string{
-					"app": "mall",
-				}))
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 1 records", func() {
-				Expect(len(kvs)).Should(Equal(1))
-			})
-
-		})
-		Context("with labels app,depth is 2 ", func() {
-			kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithKey("timeout"),
-				service.WithLabels(map[string]string{
-					"app": "mall",
-				}),
-				service.WithDepth(2))
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 3 records", func() {
-				Expect(len(kvs)).Should(Equal(3))
-			})
+}
 
-		})
-	})
-	Describe("exact find by kv and labels", func() {
-		Context("with labels app ", func() {
-			kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithKey("timeout"), service.WithLabels(map[string]string{
-				"app": "mall",
-			}), service.WithExactLabels())
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 1 records", func() {
-				Expect(len(kvs)).Should(Equal(1))
-			})
-
-		})
-	})
-	Describe("exact find by labels", func() {
-		Context("with labels app ", func() {
-			kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithLabels(map[string]string{
+func TestService_FindKV(t *testing.T) {
+	kvsvc := &kv.Service{}
+	t.Run("exact find by kv and labels with label app", func(t *testing.T) {
+		kvs, err := kvsvc.FindKV(context.Background(), "default", "test",
+			service.WithKey("timeout"),
+			service.WithLabels(map[string]string{
 				"app": "mall",
-			}), service.WithExactLabels())
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 1 records", func() {
-				Expect(len(kvs)).Should(Equal(1))
-			})
-
-		})
+			}),
+			service.WithExactLabels())
+		assert.NoError(t, err)
+		assert.Equal(t, 1, len(kvs))
 	})
-	Describe("greedy find by labels", func() {
-		Context("with labels app ans service ", func() {
-			kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithLabels(map[string]string{
-				"app":     "mall",
-				"service": "cart",
-			}))
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should has 1 records", func() {
-				Expect(len(kvs)).Should(Equal(1))
-			})
-
-		})
+	t.Run("greedy find by labels,with labels app ans service ", func(t *testing.T) {
+		kvs, err := kvsvc.FindKV(context.Background(), "default", "test", service.WithLabels(map[string]string{
+			"app":     "mall",
+			"service": "cart",
+		}))
+		assert.NoError(t, err)
+		assert.Equal(t, 1, len(kvs))
 	})
-
-	Describe("delete key", func() {
-		Context("delete key by kvID", func() {
-			kv1, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
-				Key:   "timeout",
-				Value: "20s",
-				Labels: map[string]string{
-					"env": "test",
-				},
-				Domain:  "default",
-				Project: "test",
-			})
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-
-			err = kvsvc.Delete(kv1.ID.String(), "", "default", "test")
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-
+}
+func TestService_Delete(t *testing.T) {
+	kvsvc := &kv.Service{}
+	t.Run("delete key by kvID", func(t *testing.T) {
+		kv1, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
+			Key:   "timeout",
+			Value: "20s",
+			Labels: map[string]string{
+				"env": "test",
+			},
+			Domain:  "default",
+			Project: "test",
 		})
-		Context("delete key by kvID and labelID", func() {
-			kv1, err := kvsvc.CreateOrUpdate(context.Background(), &model.KVDoc{
-				Key:   "timeout",
-				Value: "20s",
-				Labels: map[string]string{
-					"env": "test",
-				},
-				Domain:  "default",
-				Project: "test",
-			})
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
+		assert.NoError(t, err)
 
-			err = kvsvc.Delete(kv1.ID.String(), kv1.LabelID, "default", "test")
-			It("should not return err", func() {
-				Expect(err).Should(BeNil())
-			})
+		err = kvsvc.Delete(context.TODO(), kv1.ID, "default", "test")
+		assert.NoError(t, err)
 
-		})
-		Context("test miss kvID, no panic", func() {
-			err := kvsvc.Delete("", "", "default", "test")
-			It("should not return err", func() {
-				Expect(err).Should(HaveOccurred())
-			})
-		})
-		Context("Test encode error ", func() {
-			err := kvsvc.Delete("12312312321", "", "default", "test")
-			It("should return err", func() {
-				Expect(err).To(HaveOccurred())
-			})
-		})
-		Context("Test miss domain error ", func() {
-			err := kvsvc.Delete("12312312321", "", "", "test")
-			It("should return err", func() {
-				Expect(err).Should(Equal(session.ErrMissingDomain))
-			})
-		})
 	})
-})
+	t.Run("miss id", func(t *testing.T) {
+		err := kvsvc.Delete(context.TODO(), "", "default", "test")
+		assert.Error(t, err)
+	})
+	t.Run("miss domain", func(t *testing.T) {
+		err := kvsvc.Delete(context.TODO(), "2", "", "test")
+		assert.Equal(t, session.ErrMissingDomain, err)
+	})
+}
diff --git a/server/service/mongo/label/label_dao.go b/server/service/mongo/label/label_dao.go
index 87149fc..ff1f40d 100644
--- a/server/service/mongo/label/label_dao.go
+++ b/server/service/mongo/label/label_dao.go
@@ -21,13 +21,10 @@ import (
 	"context"
 	"fmt"
 	"github.com/apache/servicecomb-kie/pkg/model"
-	"github.com/apache/servicecomb-kie/server/id"
-	"github.com/apache/servicecomb-kie/server/service"
 	"github.com/apache/servicecomb-kie/server/service/mongo/session"
 	"github.com/go-mesh/openlogging"
+	uuid "github.com/satori/go.uuid"
 	"go.mongodb.org/mongo-driver/bson"
-	"go.mongodb.org/mongo-driver/bson/primitive"
-	"go.mongodb.org/mongo-driver/mongo/options"
 )
 
 const (
@@ -75,37 +72,8 @@ func FindLabels(ctx context.Context, domain, project string, labels map[string]s
 	return nil, session.ErrLabelNotExists
 }
 
-//GetLatestLabel query revision table and find maximum revision number
-func GetLatestLabel(ctx context.Context, labelID string) (*model.LabelRevisionDoc, error) {
-	collection := session.GetDB().Collection(session.CollectionLabelRevision)
-
-	filter := bson.M{"label_id": labelID}
-
-	cur, err := collection.Find(ctx, filter,
-		options.Find().SetSort(map[string]interface{}{
-			"revision": -1,
-		}), options.Find().SetLimit(1))
-	if err != nil {
-		return nil, err
-	}
-	h := &model.LabelRevisionDoc{}
-	var exist bool
-	for cur.Next(ctx) {
-		if err := cur.Decode(h); err != nil {
-			openlogging.Error("decode to KVs error: " + err.Error())
-			return nil, err
-		}
-		exist = true
-		break
-	}
-	if !exist {
-		return nil, service.ErrRevisionNotExist
-	}
-	return h, nil
-}
-
 //Exist check whether the project has certain label or not and return label ID
-func Exist(ctx context.Context, domain string, project string, labels map[string]string) (id.ID, error) {
+func Exist(ctx context.Context, domain string, project string, labels map[string]string) (string, error) {
 	l, err := FindLabels(ctx, domain, project, labels)
 	if err != nil {
 		if err.Error() == context.DeadlineExceeded.Error() {
@@ -127,13 +95,12 @@ func CreateLabel(ctx context.Context, domain string, labels map[string]string, p
 		Domain:  domain,
 		Labels:  labels,
 		Project: project,
+		ID:      uuid.NewV4().String(),
 	}
 	collection := session.GetDB().Collection(session.CollectionLabel)
-	res, err := collection.InsertOne(ctx, l)
+	_, err := collection.InsertOne(ctx, l)
 	if err != nil {
 		return nil, err
 	}
-	objectID, _ := res.InsertedID.(primitive.ObjectID)
-	l.ID = id.ID(objectID.Hex())
 	return l, nil
 }
diff --git a/server/service/mongo/session/session.go b/server/service/mongo/session/session.go
index 1eb43e2..2e4720e 100644
--- a/server/service/mongo/session/session.go
+++ b/server/service/mongo/session/session.go
@@ -40,10 +40,10 @@ import (
 
 //const for db name and collection name
 const (
-	DBName                  = "kie"
-	CollectionLabel         = "label"
-	CollectionKV            = "kv"
-	CollectionLabelRevision = "label_revision"
+	DBName               = "kie"
+	CollectionLabel      = "label"
+	CollectionKV         = "kv"
+	CollectionKVRevision = "kv_revision"
 
 	DefaultTimeout   = 5 * time.Second
 	DefaultValueType = "text"
@@ -86,7 +86,6 @@ func Init() error {
 		reg := bson.NewRegistryBuilder().
 			RegisterEncoder(reflect.TypeOf(model.LabelDoc{}), sc).
 			RegisterEncoder(reflect.TypeOf(model.KVDoc{}), sc).
-			RegisterEncoder(reflect.TypeOf(model.LabelRevisionDoc{}), sc).
 			Build()
 		clientOps := []*options.ClientOptions{options.Client().ApplyURI(config.GetDB().URI)}
 		if config.GetDB().SSLEnabled {
diff --git a/server/service/service.go b/server/service/service.go
index 4262b2a..1585a8d 100644
--- a/server/service/service.go
+++ b/server/service/service.go
@@ -33,7 +33,7 @@ var (
 //db errors
 var (
 	ErrKeyNotExists     = errors.New("key with labels does not exits")
-	ErrRevisionNotExist = errors.New("label revision not exist")
+	ErrRevisionNotExist = errors.New("revision does not exist")
 )
 
 //KV provide api of KV entity
@@ -41,14 +41,14 @@ type KV interface {
 	//below 3 methods is usually for admin console
 	CreateOrUpdate(ctx context.Context, kv *model.KVDoc) (*model.KVDoc, error)
 	List(ctx context.Context, domain, project string, limit, offset int, options ...FindOption) (*model.KVResponse, error)
-	Delete(kvID string, labelID string, domain, project string) error
+	Delete(ctx context.Context, kvID string, domain, project string) error
 	//FindKV is usually for service to pull configs
 	FindKV(ctx context.Context, domain, project string, options ...FindOption) ([]*model.KVResponse, error)
 }
 
 //History provide api of History entity
 type History interface {
-	GetHistory(ctx context.Context, labelID string, options ...FindOption) ([]*model.LabelRevisionDoc, error)
+	GetHistory(ctx context.Context, keyID string, options ...FindOption) ([]*model.KVDoc, error)
 }
 
 //Init init db session