mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-19 22:21:24 +01:00
add redis lock, it will be used to lock digest in the quota scenario
Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
fa51ac6406
commit
ef14f0cf35
114
src/common/utils/redis/helper.go
Normal file
114
src/common/utils/redis/helper.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// 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 redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/garyburd/redigo/redis"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrUnLock ...
|
||||||
|
ErrUnLock = errors.New("error to release the redis lock")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
unlockScript = `
|
||||||
|
if redis.call("get",KEYS[1]) == ARGV[1] then
|
||||||
|
return redis.call("del",KEYS[1])
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
`
|
||||||
|
defaultDelay = 5 * time.Second
|
||||||
|
defaultMaxRetry = 5
|
||||||
|
defaultExpiry = 600 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mutex ...
|
||||||
|
type Mutex struct {
|
||||||
|
Conn redis.Conn
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// New ...
|
||||||
|
func New(conn redis.Conn, key, value string) *Mutex {
|
||||||
|
o := *DefaultOptions()
|
||||||
|
if value == "" {
|
||||||
|
value = utils.GenerateRandomString()
|
||||||
|
}
|
||||||
|
return &Mutex{conn, key, value, o}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require retry to require the lock
|
||||||
|
func (rm *Mutex) Require() (bool, error) {
|
||||||
|
var isRequired bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; i < rm.opts.maxRetry; i++ {
|
||||||
|
isRequired, err = rm.require()
|
||||||
|
if isRequired {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil || !isRequired {
|
||||||
|
time.Sleep(rm.opts.retryDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isRequired, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// require get the redis lock, for details, just refer to https://redis.io/topics/distlock
|
||||||
|
func (rm *Mutex) require() (bool, error) {
|
||||||
|
reply, err := redis.String(rm.Conn.Do("SET", rm.key, rm.value, "NX", "PX", int(rm.opts.expiry/time.Millisecond)))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return reply == "OK", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free releases the lock, for details, just refer to https://redis.io/topics/distlock
|
||||||
|
func (rm *Mutex) Free() (bool, error) {
|
||||||
|
script := redis.NewScript(1, unlockScript)
|
||||||
|
resp, err := redis.Int(script.Do(rm.Conn, rm.key, rm.value))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if resp == 0 {
|
||||||
|
return false, ErrUnLock
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options ...
|
||||||
|
type Options struct {
|
||||||
|
retryDelay time.Duration
|
||||||
|
expiry time.Duration
|
||||||
|
maxRetry int
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultOptions ...
|
||||||
|
func DefaultOptions() *Options {
|
||||||
|
opt := &Options{
|
||||||
|
retryDelay: defaultDelay,
|
||||||
|
expiry: defaultExpiry,
|
||||||
|
maxRetry: defaultMaxRetry,
|
||||||
|
}
|
||||||
|
return opt
|
||||||
|
}
|
62
src/common/utils/redis/helper_test.go
Normal file
62
src/common/utils/redis/helper_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// 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 redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/garyburd/redigo/redis"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testingRedisHost = "REDIS_HOST"
|
||||||
|
|
||||||
|
func TestRedisLock(t *testing.T) {
|
||||||
|
con, err := redis.Dial(
|
||||||
|
"tcp",
|
||||||
|
fmt.Sprintf("%s:%d", getRedisHost(), 6379),
|
||||||
|
redis.DialConnectTimeout(30*time.Second),
|
||||||
|
redis.DialReadTimeout(time.Minute+10*time.Second),
|
||||||
|
redis.DialWriteTimeout(10*time.Second),
|
||||||
|
)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer con.Close()
|
||||||
|
|
||||||
|
rm := New(con, "test-redis-lock", "test-value")
|
||||||
|
|
||||||
|
successLock, err := rm.Require()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, successLock)
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
_, err = rm.Require()
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
successUnLock, err := rm.Free()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, successUnLock)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRedisHost() string {
|
||||||
|
redisHost := os.Getenv(testingRedisHost)
|
||||||
|
if redisHost == "" {
|
||||||
|
redisHost = "127.0.0.1" // for local test
|
||||||
|
}
|
||||||
|
|
||||||
|
return redisHost
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user