Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] add dlock service, implemented etcd's dlock mechanism #1191

Merged
merged 1 commit into from
Dec 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions datasource/dlock/dlock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 dlock provide distributed lock function
package dlock
robotLJW marked this conversation as resolved.
Show resolved Hide resolved

import (
"errors"
)

var ErrDLockNotExists = errors.New("DLock do not exist")

type DLock interface {
Lock(key string, ttl int64) error
TryLock(key string, ttl int64) error
Renew(key string) error
IsHoldLock(key string) bool
Unlock(key string) error
}
90 changes: 90 additions & 0 deletions datasource/dlock/dlock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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 dlock_test

import (
"testing"
"time"

"github.com/stretchr/testify/assert"

"github.com/apache/servicecomb-service-center/datasource/dlock"
_ "github.com/apache/servicecomb-service-center/test"
)

func TestDLock(t *testing.T) {
t.Run("test lock", func(t *testing.T) {
t.Run("lock the global key for 5s should pass", func(t *testing.T) {
err := dlock.Instance().Lock("global", 5)
assert.Nil(t, err)
isHold := dlock.Instance().IsHoldLock("global")
assert.Equal(t, true, isHold)
})
t.Run("two locks fight for the same lock 5s, one lock should pass, another lock should fail", func(t *testing.T) {
err := dlock.Instance().Lock("same-lock", 5)
assert.Nil(t, err)
isHold := dlock.Instance().IsHoldLock("same-lock")
assert.Equal(t, true, isHold)
err = dlock.Instance().TryLock("same-lock", 5)
assert.NotNil(t, err)
})
})
t.Run("test try lock", func(t *testing.T) {
t.Run("try lock the try key for 5s should pass", func(t *testing.T) {
err := dlock.Instance().TryLock("try-lock", 5)
assert.Nil(t, err)
isHold := dlock.Instance().IsHoldLock("try-lock")
assert.Equal(t, true, isHold)
err = dlock.Instance().TryLock("try-lock", 5)
assert.NotNil(t, err)
})
})
t.Run("test renew", func(t *testing.T) {
t.Run("renew the renew key for 5s should pass", func(t *testing.T) {
err := dlock.Instance().Lock("renew", 5)
assert.Nil(t, err)
isHold := dlock.Instance().IsHoldLock("renew")
assert.Equal(t, true, isHold)
time.Sleep(3 * time.Second)
err = dlock.Instance().Renew("renew")
time.Sleep(2 * time.Second)
err = dlock.Instance().TryLock("renew", 5)
assert.NotNil(t, err)
})
})
t.Run("test isHoldLock", func(t *testing.T) {
t.Run("already owns the lock should pass", func(t *testing.T) {
err := dlock.Instance().Lock("hold-lock", 5)
assert.Nil(t, err)
isHold := dlock.Instance().IsHoldLock("hold-lock")
assert.Equal(t, true, isHold)
})
t.Run("key does not exist should fail", func(t *testing.T) {
isHold := dlock.Instance().IsHoldLock("not-exist")
assert.Equal(t, false, isHold)
})
})
t.Run("test unlock", func(t *testing.T) {
t.Run("unlock the unlock key should pass", func(t *testing.T) {
err := dlock.Instance().Lock("unlock", 5)
assert.Nil(t, err)
err = dlock.Instance().Unlock("unlock")
assert.Nil(t, err)
})
})
}
56 changes: 56 additions & 0 deletions datasource/dlock/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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 dlock

import (
"fmt"

"github.com/apache/servicecomb-service-center/pkg/log"
)

type initFunc func(opts Options) (DLock, error)

var (
plugins = make(map[string]initFunc)
instance DLock
)

func Install(pluginImplName string, f initFunc) {
plugins[pluginImplName] = f
}

func Init(opts Options) error {
if opts.Kind == "" {
return nil
}
engineFunc, ok := plugins[opts.Kind]
if !ok {
return fmt.Errorf("plugin implement not supported [%s]", opts.Kind)
}
var err error
instance, err = engineFunc(opts)
if err != nil {
return err
}
log.Info(fmt.Sprintf("dlock plugin [%s] enabled", opts.Kind))
return nil
}

func Instance() DLock {
return instance
}
23 changes: 23 additions & 0 deletions datasource/dlock/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 dlock

// Options contains configuration for plugins
type Options struct {
Kind string
}
90 changes: 90 additions & 0 deletions datasource/etcd/dlock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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 etcd

import (
"sync"

"github.com/go-chassis/openlog"
"github.com/little-cui/etcdadpt"

"github.com/apache/servicecomb-service-center/datasource/dlock"
)

func init() {
dlock.Install("etcd", NewDLock)
dlock.Install("embeded_etcd", NewDLock)
dlock.Install("embedded_etcd", NewDLock)
}

func NewDLock(opts dlock.Options) (dlock.DLock, error) {
return &DB{lockMap: sync.Map{}}, nil
}

type DB struct {
lockMap sync.Map
}

func (d *DB) Lock(key string, ttl int64) error {
lock, err := etcdadpt.Lock(key, ttl)
if err == nil {
d.lockMap.Store(key, lock)
}
return err
}

func (d *DB) TryLock(key string, ttl int64) error {
lock, err := etcdadpt.TryLock(key, ttl)
if err == nil {
d.lockMap.Store(key, lock)
}
return err
}

func (d *DB) Renew(key string) error {
if lock, ok := d.lockMap.Load(key); ok {
err := lock.(*etcdadpt.DLock).Refresh()
if err != nil {
openlog.Error("fail to renew key")
d.lockMap.Delete(key)
}
return err
}
return dlock.ErrDLockNotExists
}

func (d *DB) IsHoldLock(key string) bool {
if lock, ok := d.lockMap.Load(key); ok {
if lock != nil {
return true
}
}
return false
}

func (d *DB) Unlock(key string) error {
if lock, ok := d.lockMap.Load(key); ok {
err := lock.(*etcdadpt.DLock).Unlock()
if err != nil {
openlog.Error("fail to unlock")
}
d.lockMap.Delete(key)
return err
}
return dlock.ErrDLockNotExists
}
9 changes: 9 additions & 0 deletions datasource/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package datasource
import (
"fmt"

"github.com/apache/servicecomb-service-center/datasource/dlock"
"github.com/apache/servicecomb-service-center/datasource/rbac"
"github.com/apache/servicecomb-service-center/datasource/schema"
"github.com/apache/servicecomb-service-center/pkg/log"
Expand Down Expand Up @@ -56,6 +57,14 @@ func Init(opts Options) error {
if err != nil {
return err
}

err = dlock.Init(dlock.Options{
Kind: opts.Kind,
})
if err != nil {
return err
}

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ replace (
)

require (
github.com/robfig/cron/v3 v3.0.1
github.com/NYTimes/gziphandler v1.1.1
github.com/apache/servicecomb-service-center/api v0.0.0
github.com/astaxie/beego v1.12.2
Expand Down Expand Up @@ -38,6 +37,7 @@ require (
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/client_model v0.2.0
github.com/prometheus/procfs v0.6.0
github.com/robfig/cron/v3 v3.0.1
github.com/rs/cors v1.7.0 // v1.1
github.com/satori/go.uuid v1.1.0
github.com/spf13/cobra v1.1.3
Expand Down
9 changes: 0 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,6 @@ github.com/go-chassis/cari v0.5.1-0.20211208092532-78a52aa9d52e/go.mod h1:av/19f
github.com/go-chassis/foundation v0.2.2-0.20201210043510-9f6d3de40234/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
github.com/go-chassis/foundation v0.2.2/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
github.com/go-chassis/foundation v0.3.0/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
github.com/go-chassis/foundation v0.3.1-0.20210806081520-3bd92d1ef787/go.mod h1:6NsIUaHghTFRGfCBcZN011zl196F6OR5QvD9N+P4oWU=
github.com/go-chassis/foundation v0.3.1-0.20210811025651-7f4d2b2b906c h1:6ooUyysnayGgoJHV++NLEcnnnXzsw3ud4VydjISGBqI=
github.com/go-chassis/foundation v0.3.1-0.20210811025651-7f4d2b2b906c/go.mod h1:6NsIUaHghTFRGfCBcZN011zl196F6OR5QvD9N+P4oWU=
github.com/go-chassis/foundation v0.4.0 h1:z0xETnSxF+vRXWjoIhOdzt6rywjZ4sB++utEl4YgWEY=
github.com/go-chassis/foundation v0.4.0/go.mod h1:6NsIUaHghTFRGfCBcZN011zl196F6OR5QvD9N+P4oWU=
github.com/go-chassis/go-archaius v1.5.1 h1:1FrNyzzmD6o6BIjPF8uQ4Cc+u7qYIgQTpDk8uopBqfo=
Expand Down Expand Up @@ -436,12 +433,6 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/little-cui/etcdadpt v0.2.1 h1:eT1A+BV1/2/dmmZA2Nl+cc7uTMuwd6T6DD+JrXr8xcA=
github.com/little-cui/etcdadpt v0.2.1/go.mod h1:727wftF2FS4vfkgFLmIvQue1XH+9u4lK2/hd6L7OAC8=
github.com/little-cui/etcdadpt v0.2.2-0.20211218040008-804e734f410b h1:GeNmlkSJKu1B8pNM18g+u8O/7BJNd9TP6KrfePy7Z6w=
github.com/little-cui/etcdadpt v0.2.2-0.20211218040008-804e734f410b/go.mod h1:HnRRpIrVEVNWobkiCvG2EHLWKKZ+L047EcI29ma2zA4=
github.com/little-cui/etcdadpt v0.2.2-0.20211222115540-fc5b1296d8b5 h1:SlD/2mPGjzkg2oj4ndoR9u/yadPcyE8om3njQEI65bE=
github.com/little-cui/etcdadpt v0.2.2-0.20211222115540-fc5b1296d8b5/go.mod h1:HnRRpIrVEVNWobkiCvG2EHLWKKZ+L047EcI29ma2zA4=
github.com/little-cui/etcdadpt v0.3.1 h1:lAPIffcOR6jROu/mWf+zHscV8urIu1qbsJvwvziLWDY=
github.com/little-cui/etcdadpt v0.3.1/go.mod h1:HnRRpIrVEVNWobkiCvG2EHLWKKZ+L047EcI29ma2zA4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
Expand Down
Loading