123 lines
3.1 KiB
Go
123 lines
3.1 KiB
Go
package lockprovider
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"github.com/samber/lo"
|
||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||
"gitlink.org.cn/cloudream/common/utils/lo2"
|
||
)
|
||
|
||
const (
|
||
MetadataLockPathPrefix = "Metadata"
|
||
MetadataCreateLock = "Create"
|
||
)
|
||
|
||
type metadataElementLock struct {
|
||
target StringLockTarget
|
||
requestIDs []string
|
||
}
|
||
|
||
type MetadataLock struct {
|
||
createReqIDs []*metadataElementLock
|
||
|
||
lockCompatibilityTable LockCompatibilityTable
|
||
}
|
||
|
||
func NewMetadataLock() *MetadataLock {
|
||
|
||
metadataLock := MetadataLock{
|
||
lockCompatibilityTable: LockCompatibilityTable{},
|
||
}
|
||
|
||
compTable := &metadataLock.lockCompatibilityTable
|
||
|
||
compTable.
|
||
Column(MetadataCreateLock, func() bool { return len(metadataLock.createReqIDs) > 0 })
|
||
trgt := LockSpecial(func(lock distlock.Lock, testLockName string) bool {
|
||
strTar := lock.Target.(StringLockTarget)
|
||
return lo.NoneBy(metadataLock.createReqIDs, func(other *metadataElementLock) bool { return strTar.IsConflict(&other.target) })
|
||
})
|
||
|
||
compTable.MustRow(trgt)
|
||
|
||
return &metadataLock
|
||
}
|
||
|
||
// CanLock 判断这个锁能否锁定成功
|
||
func (l *MetadataLock) CanLock(lock distlock.Lock) error {
|
||
return l.lockCompatibilityTable.Test(lock)
|
||
}
|
||
|
||
// 锁定
|
||
func (l *MetadataLock) Lock(reqID string, lock distlock.Lock) error {
|
||
switch lock.Name {
|
||
case MetadataCreateLock:
|
||
l.createReqIDs = l.addElementLock(lock, l.createReqIDs, reqID)
|
||
|
||
default:
|
||
return fmt.Errorf("unknow lock name: %s", lock.Name)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (l *MetadataLock) addElementLock(lock distlock.Lock, locks []*metadataElementLock, reqID string) []*metadataElementLock {
|
||
strTarget := lock.Target.(StringLockTarget)
|
||
lck, ok := lo.Find(locks, func(l *metadataElementLock) bool { return strTarget.IsConflict(&l.target) })
|
||
if !ok {
|
||
lck = &metadataElementLock{
|
||
target: strTarget,
|
||
}
|
||
locks = append(locks, lck)
|
||
}
|
||
|
||
lck.requestIDs = append(lck.requestIDs, reqID)
|
||
return locks
|
||
}
|
||
|
||
// 解锁
|
||
func (l *MetadataLock) Unlock(reqID string, lock distlock.Lock) error {
|
||
switch lock.Name {
|
||
case MetadataCreateLock:
|
||
l.createReqIDs = l.removeElementLock(lock, l.createReqIDs, reqID)
|
||
|
||
default:
|
||
return fmt.Errorf("unknow lock name: %s", lock.Name)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (l *MetadataLock) removeElementLock(lock distlock.Lock, locks []*metadataElementLock, reqID string) []*metadataElementLock {
|
||
strTarget := lock.Target.(StringLockTarget)
|
||
lck, index, ok := lo.FindIndexOf(locks, func(l *metadataElementLock) bool { return strTarget.IsConflict(&l.target) })
|
||
if !ok {
|
||
return locks
|
||
}
|
||
|
||
lck.requestIDs = lo2.Remove(lck.requestIDs, reqID)
|
||
|
||
if len(lck.requestIDs) == 0 {
|
||
locks = lo2.RemoveAt(locks, index)
|
||
}
|
||
|
||
return locks
|
||
}
|
||
|
||
// GetTargetString 将锁对象序列化为字符串,方便存储到ETCD
|
||
func (l *MetadataLock) GetTargetString(target any) (string, error) {
|
||
tar := target.(StringLockTarget)
|
||
return StringLockTargetToString(&tar)
|
||
}
|
||
|
||
// ParseTargetString 解析字符串格式的锁对象数据
|
||
func (l *MetadataLock) ParseTargetString(targetStr string) (any, error) {
|
||
return StringLockTargetFromString(targetStr)
|
||
}
|
||
|
||
// Clear 清除内部所有状态
|
||
func (l *MetadataLock) Clear() {
|
||
l.createReqIDs = nil
|
||
}
|