简化锁服务
This commit is contained in:
parent
58f100fa64
commit
d80f6dba06
|
@ -98,13 +98,8 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
|
|||
hubMeta := metaCacheHost.AddHubMeta()
|
||||
conMeta := metaCacheHost.AddConnectivity()
|
||||
|
||||
// 分布式锁
|
||||
distlockSvc, err := distlock.NewService(&config.Cfg().DistLock)
|
||||
if err != nil {
|
||||
logger.Warnf("new distlock service failed, err: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
go serveDistLock(distlockSvc)
|
||||
// 公共锁
|
||||
publock := distlock.NewService()
|
||||
|
||||
// 访问统计
|
||||
acStat := accessstat.NewAccessStat(accessstat.Config{
|
||||
|
@ -124,10 +119,10 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
|
|||
dlder := downloader.NewDownloader(config.Cfg().Downloader, &conCol, stgPool, strgSel, db)
|
||||
|
||||
// 上传器
|
||||
uploader := uploader.NewUploader(distlockSvc, &conCol, stgPool, stgMeta, db)
|
||||
uploader := uploader.NewUploader(publock, &conCol, stgPool, stgMeta, db)
|
||||
|
||||
// 定时任务
|
||||
tktk := ticktock.New(config.Cfg().TickTock, db, stgMeta, stgPool, evtPub)
|
||||
tktk := ticktock.New(config.Cfg().TickTock, db, stgMeta, stgPool, evtPub, publock)
|
||||
tktk.Start()
|
||||
defer tktk.Stop()
|
||||
|
||||
|
@ -148,7 +143,7 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
|
|||
mntChan := mnt.Start()
|
||||
defer mnt.Stop()
|
||||
|
||||
svc := services.NewService(distlockSvc, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt)
|
||||
svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt)
|
||||
|
||||
// HTTP接口
|
||||
httpCfg := config.Cfg().HTTP
|
||||
|
@ -250,18 +245,3 @@ loop:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serveDistLock(svc *distlock.Service) {
|
||||
logger.Info("start serving distlock")
|
||||
|
||||
err := svc.Serve()
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("distlock stopped with error: %s", err.Error())
|
||||
}
|
||||
|
||||
logger.Info("distlock stopped")
|
||||
|
||||
// TODO 仅简单结束了程序
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
@ -91,13 +91,8 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
|
|||
hubMeta := metaCacheHost.AddHubMeta()
|
||||
conMeta := metaCacheHost.AddConnectivity()
|
||||
|
||||
// 分布式锁
|
||||
distlockSvc, err := distlock.NewService(&config.Cfg().DistLock)
|
||||
if err != nil {
|
||||
logger.Warnf("new distlock service failed, err: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
go serveDistLock(distlockSvc)
|
||||
// 公共锁
|
||||
publock := distlock.NewService()
|
||||
|
||||
// 访问统计
|
||||
acStat := accessstat.NewAccessStat(accessstat.Config{
|
||||
|
@ -117,7 +112,7 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
|
|||
dlder := downloader.NewDownloader(config.Cfg().Downloader, &conCol, stgPool, strgSel, db)
|
||||
|
||||
// 上传器
|
||||
uploader := uploader.NewUploader(distlockSvc, &conCol, stgPool, stgMeta, db)
|
||||
uploader := uploader.NewUploader(publock, &conCol, stgPool, stgMeta, db)
|
||||
|
||||
// 挂载
|
||||
mntCfg := config.Cfg().Mount
|
||||
|
@ -132,7 +127,7 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
|
|||
mntChan := mnt.Start()
|
||||
defer mnt.Stop()
|
||||
|
||||
svc := services.NewService(distlockSvc, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt)
|
||||
svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt)
|
||||
|
||||
// HTTP接口
|
||||
httpCfg := config.Cfg().HTTP
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/mq"
|
||||
"gitlink.org.cn/cloudream/common/utils/config"
|
||||
|
@ -22,7 +21,6 @@ type Config struct {
|
|||
Logger logger.Config `json:"logger"`
|
||||
DB db.Config `json:"db"`
|
||||
RabbitMQ mq.Config `json:"rabbitMQ"`
|
||||
DistLock distlock.Config `json:"distlock"`
|
||||
Connectivity connectivity.Config `json:"connectivity"`
|
||||
Downloader downloader.Config `json:"downloader"`
|
||||
DownloadStrategy strategy.Config `json:"downloadStrategy"`
|
||||
|
|
|
@ -29,7 +29,7 @@ type downloadSpaceInfo struct {
|
|||
}
|
||||
|
||||
type DownloadContext struct {
|
||||
Distlock *distlock.Service
|
||||
PubLock *distlock.Service
|
||||
}
|
||||
type DownloadObjectIterator struct {
|
||||
OnClosing func()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
||||
|
@ -9,12 +8,13 @@ import (
|
|||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/metacache"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/mount"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/uploader"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/sysevent"
|
||||
)
|
||||
|
||||
// Service 结构体封装了分布锁服务和任务管理服务。
|
||||
type Service struct {
|
||||
DistLock *distlock.Service
|
||||
PubLock *distlock.Service
|
||||
Downloader *downloader.Downloader
|
||||
AccessStat *accessstat.AccessStat
|
||||
Uploader *uploader.Uploader
|
||||
|
@ -26,7 +26,7 @@ type Service struct {
|
|||
}
|
||||
|
||||
func NewService(
|
||||
distlock *distlock.Service,
|
||||
publock *distlock.Service,
|
||||
downloader *downloader.Downloader,
|
||||
accStat *accessstat.AccessStat,
|
||||
uploder *uploader.Uploader,
|
||||
|
@ -37,7 +37,7 @@ func NewService(
|
|||
mount *mount.Mount,
|
||||
) *Service {
|
||||
return &Service{
|
||||
DistLock: distlock,
|
||||
PubLock: publock,
|
||||
Downloader: downloader,
|
||||
AccessStat: accStat,
|
||||
Uploader: uploder,
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader/strategy"
|
||||
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
|
||||
hubmq "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/mq/hub"
|
||||
|
@ -95,17 +96,13 @@ func (svc *UserSpaceService) LoadPackage(packageID clitypes.PackageID, userspace
|
|||
}
|
||||
}
|
||||
|
||||
// TODO2 加锁
|
||||
// mutex, err := reqbuilder.NewBuilder().
|
||||
// // 保护在userspace目录中下载的文件
|
||||
// UserSpace().Buzy(userspaceID).
|
||||
// // 保护下载文件时同时保存到IPFS的文件
|
||||
// Shard().Buzy(userspaceID).
|
||||
// MutexLock(svc.DistLock)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("acquire locks failed, err: %w", err)
|
||||
// }
|
||||
// defer mutex.Unlock()
|
||||
mutex, err := reqbuilder.NewBuilder().
|
||||
Shard().Buzy(userspaceID).
|
||||
MutexLock(svc.PubLock)
|
||||
if err != nil {
|
||||
return fmt.Errorf("acquire locks failed, err: %w", err)
|
||||
}
|
||||
defer mutex.Unlock()
|
||||
|
||||
// 记录访问统计
|
||||
for _, obj := range details {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/models/datamap"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -119,37 +120,34 @@ func (j *ChangeRedundancy) changeOne(ctx *changeRedundancyContext, pkg clitypes.
|
|||
}
|
||||
lastObjID = objs[len(objs)-1].Object.ObjectID
|
||||
|
||||
reen := ctx.ticktock.pubLock.BeginReentrant()
|
||||
|
||||
var allUpdatings []db.UpdatingObjectRedundancy
|
||||
var allSysEvts []datamap.SysEventBody
|
||||
|
||||
ctx.mostBlockStgIDs = j.summaryRepObjectBlockUserSpaces(ctx, objs, 2)
|
||||
|
||||
// // TODO 加锁
|
||||
// builder := reqbuilder.NewBuilder()
|
||||
// for _, storage := range newRepStgs {
|
||||
// builder.Shard().Buzy(storage.Storage.Storage.StorageID)
|
||||
// }
|
||||
// for _, storage := range newECStgs {
|
||||
// builder.Shard().Buzy(storage.Storage.Storage.StorageID)
|
||||
// }
|
||||
// mutex, err := builder.MutexLock(execCtx.Args.DistLock)
|
||||
// if err != nil {
|
||||
// log.Warnf("acquiring dist lock: %s", err.Error())
|
||||
// return
|
||||
// }
|
||||
// defer mutex.Unlock()
|
||||
|
||||
var willShrinks []clitypes.ObjectDetail
|
||||
|
||||
for _, obj := range objs {
|
||||
newRed, selectedStorages := j.chooseRedundancy(ctx, obj)
|
||||
newRed, selectedSpaces := j.chooseRedundancy(ctx, obj)
|
||||
// 冗余策略不需要调整,就检查是否需要收缩
|
||||
if newRed == nil {
|
||||
willShrinks = append(willShrinks, obj)
|
||||
continue
|
||||
}
|
||||
|
||||
updating, evt, err := j.doChangeRedundancy(ctx, obj, newRed, selectedStorages)
|
||||
reqBlder := reqbuilder.NewBuilder()
|
||||
for _, space := range selectedSpaces {
|
||||
reqBlder.Shard().Buzy(space.UserSpace.UserSpace.UserSpaceID)
|
||||
}
|
||||
err := reen.Lock(reqBlder.Build())
|
||||
if err != nil {
|
||||
log.WithField("ObjectID", obj.Object.ObjectID).Warnf("acquire lock: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
updating, evt, err := j.doChangeRedundancy(ctx, obj, newRed, selectedSpaces)
|
||||
if updating != nil {
|
||||
allUpdatings = append(allUpdatings, *updating)
|
||||
}
|
||||
|
@ -158,24 +156,27 @@ func (j *ChangeRedundancy) changeOne(ctx *changeRedundancyContext, pkg clitypes.
|
|||
}
|
||||
if err != nil {
|
||||
log.WithField("ObjectID", obj.Object.ObjectID).Warnf("%s, its redundancy wont be changed", err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
udpatings, sysEvts, err := j.doRedundancyShrink(ctx, pkg, willShrinks)
|
||||
udpatings, sysEvts, err := j.doRedundancyShrink(ctx, pkg, willShrinks, reen)
|
||||
if err != nil {
|
||||
log.Warnf("redundancy shrink: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
allUpdatings = append(allUpdatings, udpatings...)
|
||||
allSysEvts = append(allSysEvts, sysEvts...)
|
||||
}
|
||||
|
||||
if len(allUpdatings) > 0 {
|
||||
err := db.DoTx10(db2, db2.Object().BatchUpdateRedundancy, allUpdatings)
|
||||
if err != nil {
|
||||
reen.Unlock()
|
||||
log.Warnf("update object redundancy: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
reen.Unlock()
|
||||
|
||||
for _, e := range allSysEvts {
|
||||
ctx.ticktock.evtPub.Publish(e)
|
||||
|
|
|
@ -18,12 +18,14 @@ import (
|
|||
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/consts"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/models/datamap"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
|
||||
)
|
||||
|
||||
func (t *ChangeRedundancy) doRedundancyShrink(execCtx *changeRedundancyContext, pkg clitypes.PackageDetail, objs []clitypes.ObjectDetail) ([]db.UpdatingObjectRedundancy, []datamap.SysEventBody, error) {
|
||||
func (t *ChangeRedundancy) doRedundancyShrink(execCtx *changeRedundancyContext, pkg clitypes.PackageDetail, objs []clitypes.ObjectDetail, reen *distlock.Reentrant) ([]db.UpdatingObjectRedundancy, []datamap.SysEventBody, error) {
|
||||
log := logger.WithType[ChangeRedundancy]("TickTock")
|
||||
|
||||
var readerStgIDs []clitypes.UserSpaceID
|
||||
|
@ -78,7 +80,7 @@ func (t *ChangeRedundancy) doRedundancyShrink(execCtx *changeRedundancyContext,
|
|||
sysEvents = append(sysEvents, t.generateSysEventForECObject(solu, obj)...)
|
||||
}
|
||||
|
||||
ioSwRets, err := t.executePlans(execCtx, planBld, planningStgIDs)
|
||||
ioSwRets, err := t.executePlans(execCtx, planBld, planningStgIDs, reen)
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
return nil, nil, fmt.Errorf("execute plans: %w", err)
|
||||
|
@ -904,17 +906,15 @@ func (t *ChangeRedundancy) generateSysEventForECObject(solu annealingSolution, o
|
|||
return []datamap.SysEventBody{transEvt, distEvt}
|
||||
}
|
||||
|
||||
func (t *ChangeRedundancy) executePlans(ctx *changeRedundancyContext, planBld *exec.PlanBuilder, planningStgIDs map[clitypes.UserSpaceID]bool) (map[string]exec.VarValue, error) {
|
||||
// TODO 统一加锁,有重复也没关系
|
||||
// lockBld := reqbuilder.NewBuilder()
|
||||
// for id := range planningStgIDs {
|
||||
// lockBld.Shard().Buzy(id)
|
||||
// }
|
||||
// lock, err := lockBld.MutexLock(ctx.Args.DistLock)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("acquiring distlock: %w", err)
|
||||
// }
|
||||
// defer lock.Unlock()
|
||||
func (t *ChangeRedundancy) executePlans(ctx *changeRedundancyContext, planBld *exec.PlanBuilder, planningSpaceIDs map[clitypes.UserSpaceID]bool, reen *distlock.Reentrant) (map[string]exec.VarValue, error) {
|
||||
reqBlder := reqbuilder.NewBuilder()
|
||||
for id, _ := range planningSpaceIDs {
|
||||
reqBlder.Shard().Buzy(id)
|
||||
}
|
||||
err := reen.Lock(reqBlder.Build())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("locking shard resources: %w", err)
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder"
|
||||
hubmq "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/mq/hub"
|
||||
)
|
||||
|
||||
|
@ -30,18 +31,6 @@ func (j *ShardStoreGC) Execute(t *TickTock) {
|
|||
log.Debugf("job end, time: %v", time.Since(startTime))
|
||||
}()
|
||||
|
||||
// TODO 加锁
|
||||
// // 使用分布式锁进行资源锁定
|
||||
// mutex, err := reqbuilder.NewBuilder().
|
||||
// // 执行IPFS垃圾回收
|
||||
// Shard().GC(j.StorageID).
|
||||
// MutexLock(execCtx.Args.DistLock)
|
||||
// if err != nil {
|
||||
// log.Warnf("acquire locks failed, err: %s", err.Error())
|
||||
// return
|
||||
// }
|
||||
// defer mutex.Unlock()
|
||||
|
||||
spaceIDs, err := t.db.UserSpace().GetAllIDs(t.db.DefCtx())
|
||||
if err != nil {
|
||||
log.Warnf("getting user space ids: %v", err)
|
||||
|
@ -63,11 +52,17 @@ func (j *ShardStoreGC) Execute(t *TickTock) {
|
|||
}
|
||||
|
||||
func (j *ShardStoreGC) gcOne(t *TickTock, space *types.UserSpaceDetail) error {
|
||||
mutex, err := reqbuilder.NewBuilder().Shard().GC(space.UserSpace.UserSpaceID).MutexLock(t.pubLock)
|
||||
if err != nil {
|
||||
return fmt.Errorf("acquire lock: %w", err)
|
||||
}
|
||||
defer mutex.Unlock()
|
||||
|
||||
db2 := t.db
|
||||
|
||||
// 收集需要进行垃圾回收的文件哈希值
|
||||
var allFileHashes []types.FileHash
|
||||
err := db2.DoTx(func(tx db.SQLContext) error {
|
||||
err = db2.DoTx(func(tx db.SQLContext) error {
|
||||
blocks, err := db2.ObjectBlock().GetByUserSpaceID(tx, space.UserSpace.UserSpaceID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting object blocks by hub id: %w", err)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/metacache"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/sysevent"
|
||||
)
|
||||
|
@ -29,9 +30,10 @@ type TickTock struct {
|
|||
spaceMeta *metacache.UserSpaceMeta
|
||||
stgPool *pool.Pool
|
||||
evtPub *sysevent.Publisher
|
||||
pubLock *distlock.Service
|
||||
}
|
||||
|
||||
func New(cfg Config, db *db.DB, spaceMeta *metacache.UserSpaceMeta, stgPool *pool.Pool, evtPub *sysevent.Publisher) *TickTock {
|
||||
func New(cfg Config, db *db.DB, spaceMeta *metacache.UserSpaceMeta, stgPool *pool.Pool, evtPub *sysevent.Publisher, pubLock *distlock.Service) *TickTock {
|
||||
sch, _ := gocron.NewScheduler()
|
||||
t := &TickTock{
|
||||
cfg: cfg,
|
||||
|
@ -41,6 +43,7 @@ func New(cfg Config, db *db.DB, spaceMeta *metacache.UserSpaceMeta, stgPool *poo
|
|||
spaceMeta: spaceMeta,
|
||||
stgPool: stgPool,
|
||||
evtPub: evtPub,
|
||||
pubLock: pubLock,
|
||||
}
|
||||
t.initJobs()
|
||||
return t
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
|
||||
|
@ -21,7 +22,7 @@ type CreateLoadUploader struct {
|
|||
targetSpaces []types.UserSpaceDetail
|
||||
loadRoots []string
|
||||
uploader *Uploader
|
||||
// distlock *distlock.Mutex
|
||||
pubLock *distlock.Mutex
|
||||
successes []db.AddObjectEntry
|
||||
lock sync.Mutex
|
||||
commited bool
|
||||
|
@ -92,7 +93,7 @@ func (u *CreateLoadUploader) Commit() (CreateLoadResult, error) {
|
|||
}
|
||||
u.commited = true
|
||||
|
||||
// defer u.distlock.Unlock()
|
||||
defer u.pubLock.Unlock()
|
||||
|
||||
var addedObjs []types.Object
|
||||
err := u.uploader.db.DoTx(func(tx db.SQLContext) error {
|
||||
|
@ -125,7 +126,8 @@ func (u *CreateLoadUploader) Abort() {
|
|||
}
|
||||
u.commited = true
|
||||
|
||||
// u.distlock.Unlock()
|
||||
u.pubLock.Unlock()
|
||||
|
||||
// TODO 可以考虑删除PackageID
|
||||
db2 := u.uploader.db
|
||||
db.DoTx10(db2, db2.Package().DeleteComplete, u.pkg.PackageID)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
|
||||
|
@ -21,7 +22,7 @@ type UpdateUploader struct {
|
|||
uploader *Uploader
|
||||
pkgID types.PackageID
|
||||
targetSpace types.UserSpaceDetail
|
||||
// distMutex *distlock.Mutex
|
||||
pubLock *distlock.Mutex
|
||||
loadToSpaces []types.UserSpaceDetail
|
||||
loadToPath []string
|
||||
successes []db.AddObjectEntry
|
||||
|
@ -125,7 +126,7 @@ func (w *UpdateUploader) Commit() (UpdateResult, error) {
|
|||
}
|
||||
w.commited = true
|
||||
|
||||
// defer w.distMutex.Unlock()
|
||||
defer w.pubLock.Unlock()
|
||||
|
||||
var addedObjs []types.Object
|
||||
err := w.uploader.db.DoTx(func(tx db.SQLContext) error {
|
||||
|
@ -157,5 +158,5 @@ func (w *UpdateUploader) Abort() {
|
|||
}
|
||||
|
||||
w.commited = true
|
||||
// w.distMutex.Unlock()
|
||||
w.pubLock.Unlock()
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/connectivity"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
|
||||
|
@ -25,16 +26,16 @@ import (
|
|||
)
|
||||
|
||||
type Uploader struct {
|
||||
distlock *distlock.Service
|
||||
pubLock *distlock.Service
|
||||
connectivity *connectivity.Collector
|
||||
stgPool *pool.Pool
|
||||
spaceMeta *metacache.UserSpaceMeta
|
||||
db *db.DB
|
||||
}
|
||||
|
||||
func NewUploader(distlock *distlock.Service, connectivity *connectivity.Collector, stgPool *pool.Pool, spaceMeta *metacache.UserSpaceMeta, db *db.DB) *Uploader {
|
||||
func NewUploader(pubLock *distlock.Service, connectivity *connectivity.Collector, stgPool *pool.Pool, spaceMeta *metacache.UserSpaceMeta, db *db.DB) *Uploader {
|
||||
return &Uploader{
|
||||
distlock: distlock,
|
||||
pubLock: pubLock,
|
||||
connectivity: connectivity,
|
||||
stgPool: stgPool,
|
||||
spaceMeta: spaceMeta,
|
||||
|
@ -93,20 +94,17 @@ func (u *Uploader) BeginUpdate(pkgID clitypes.PackageID, affinity clitypes.UserS
|
|||
|
||||
target := u.chooseUploadStorage(uploadSpaces, affinity)
|
||||
|
||||
// TODO2 加锁
|
||||
// 给上传节点的IPFS加锁
|
||||
// TODO 考虑加Object的Create锁
|
||||
// 防止上传的副本被清除
|
||||
// distMutex, err := reqbuilder.NewBuilder().Shard().Buzy(target.Space.Storage.StorageID).MutexLock(u.distlock)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("acquire distlock: %w", err)
|
||||
// }
|
||||
pubLock, err := reqbuilder.NewBuilder().Shard().Buzy(target.Space.UserSpace.UserSpaceID).MutexLock(u.pubLock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("acquire lock: %w", err)
|
||||
}
|
||||
|
||||
return &UpdateUploader{
|
||||
uploader: u,
|
||||
pkgID: pkgID,
|
||||
targetSpace: target.Space,
|
||||
// distMutex: distMutex,
|
||||
pubLock: pubLock,
|
||||
loadToSpaces: loadToSpaces,
|
||||
loadToPath: loadToPath,
|
||||
}, nil
|
||||
|
@ -158,23 +156,21 @@ func (u *Uploader) BeginCreateLoad(bktID clitypes.BucketID, pkgName string, load
|
|||
return nil, fmt.Errorf("create package: %w", err)
|
||||
}
|
||||
|
||||
// TODO2 加锁
|
||||
// reqBld := reqbuilder.NewBuilder()
|
||||
// for _, stg := range spacesStgs {
|
||||
// reqBld.Shard().Buzy(stg.Storage.StorageID)
|
||||
// reqBld.Storage().Buzy(stg.Storage.StorageID)
|
||||
// }
|
||||
// lock, err := reqBld.MutexLock(u.distlock)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("acquire distlock: %w", err)
|
||||
// }
|
||||
reqBld := reqbuilder.NewBuilder()
|
||||
for _, stg := range spacesStgs {
|
||||
reqBld.Shard().Buzy(stg.UserSpace.UserSpaceID)
|
||||
}
|
||||
lock, err := reqBld.MutexLock(u.pubLock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("acquire lock: %w", err)
|
||||
}
|
||||
|
||||
return &CreateLoadUploader{
|
||||
pkg: pkg,
|
||||
targetSpaces: spacesStgs,
|
||||
loadRoots: loadToPath,
|
||||
uploader: u,
|
||||
// distlock: lock,
|
||||
pubLock: lock,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -236,12 +232,11 @@ func (u *Uploader) UploadPart(objID clitypes.ObjectID, index int, stream io.Read
|
|||
space = u.chooseUploadStorage(userStgs, 0).Space
|
||||
}
|
||||
|
||||
// TODO2 加锁
|
||||
// lock, err := reqbuilder.NewBuilder().Shard().Buzy(space.Storage.StorageID).MutexLock(u.distlock)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("acquire distlock: %w", err)
|
||||
// }
|
||||
// defer lock.Unlock()
|
||||
lock, err := reqbuilder.NewBuilder().Shard().Buzy(space.UserSpace.UserSpaceID).MutexLock(u.pubLock)
|
||||
if err != nil {
|
||||
return fmt.Errorf("acquire lock: %w", err)
|
||||
}
|
||||
defer lock.Unlock()
|
||||
|
||||
ft := ioswitch2.NewFromTo()
|
||||
fromDrv, hd := ioswitch2.NewFromDriver(ioswitch2.RawStream())
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
- `pkgs`:一些相对独立的功能模块。
|
||||
- `cmd`:公用的业务逻辑,比如上传Package和下载Package。
|
||||
- `db`:数据库的数据结构和操作函数。
|
||||
- `distlock`:分布式锁服务,核心机制使用的是`common/pkgs/distlock`,增加了根据存储系统的业务需求设计的锁。
|
||||
- `ec`:纠删码的库。
|
||||
- `grpc`:存放proto文件,以及使用protogen工具生成的代码文件。
|
||||
- `ioswitch`:IOSwitch模块。
|
||||
|
|
|
@ -28,14 +28,6 @@
|
|||
"retryInterval": 5000
|
||||
}
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a client"
|
||||
},
|
||||
"connectivity": {
|
||||
"testInterval": 300
|
||||
},
|
||||
|
|
|
@ -25,14 +25,6 @@
|
|||
"retryInterval": 5000
|
||||
}
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a hub"
|
||||
},
|
||||
"connectivity": {
|
||||
"testInterval": 300
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"accessStatHistoryAmount": 0.8,
|
||||
"ecFileSizeThreshold": 104857600,
|
||||
"hubUnavailableSeconds": 300,
|
||||
"logger": {
|
||||
"output": "file",
|
||||
"outputFileName": "scanner",
|
||||
"outputDirectory": "log",
|
||||
"level": "debug"
|
||||
},
|
||||
"db": {
|
||||
"address": "127.0.0.1:3306",
|
||||
"account": "",
|
||||
"password": "",
|
||||
"databaseName": "cloudream"
|
||||
},
|
||||
"rabbitMQ": {
|
||||
"address": "127.0.0.1:5672",
|
||||
"account": "",
|
||||
"password": "",
|
||||
"vhost": "/",
|
||||
"param": {
|
||||
"retryNum": 5,
|
||||
"retryInterval": 5000
|
||||
}
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a scanner"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package lockprovider
|
||||
|
||||
import "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
|
||||
type EmptyTarget struct{}
|
||||
|
||||
func NewEmptyTarget() *EmptyTarget {
|
||||
return &EmptyTarget{}
|
||||
}
|
||||
|
||||
func (e *EmptyTarget) Equals(other types.LockTarget) bool {
|
||||
_, ok := other.(*EmptyTarget)
|
||||
return ok
|
||||
}
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,7 +16,7 @@ const (
|
|||
type HasSuchLockFn = func() bool
|
||||
|
||||
// LockCompatibilitySpecialFn 判断锁与指定的锁名是否兼容
|
||||
type LockCompatibilitySpecialFn func(lock distlock.Lock, testLockName string) bool
|
||||
type LockCompatibilitySpecialFn func(lock types.Lock, testLockName string) bool
|
||||
|
||||
type LockCompatibilityType string
|
||||
|
||||
|
@ -95,7 +95,7 @@ func (t *LockCompatibilityTable) Row(comps ...LockCompatibility) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *LockCompatibilityTable) Test(lock distlock.Lock) error {
|
||||
func (t *LockCompatibilityTable) Test(lock types.Lock) error {
|
||||
row, ok := lo.Find(t.rows, func(row LockCompatibilityTableRow) bool { return lock.Name == row.LockName })
|
||||
if !ok {
|
||||
return fmt.Errorf("unknow lock name %s", lock.Name)
|
||||
|
@ -108,13 +108,13 @@ func (t *LockCompatibilityTable) Test(lock distlock.Lock) error {
|
|||
|
||||
if c.Type == LOCK_COMPATIBILITY_UNCOMPATIBLE {
|
||||
if t.rows[i].HasSuchLockFn() {
|
||||
return distlock.NewLockTargetBusyError(t.rows[i].LockName)
|
||||
return types.NewLockTargetBusyError(t.rows[i].LockName)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Type == LOCK_COMPATIBILITY_SPECIAL {
|
||||
if !c.SpecialFn(lock, t.rows[i].LockName) {
|
||||
return distlock.NewLockTargetBusyError(t.rows[i].LockName)
|
||||
return types.NewLockTargetBusyError(t.rows[i].LockName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
func Test_LockCompatibilityTable(t *testing.T) {
|
||||
|
@ -18,22 +18,22 @@ func Test_LockCompatibilityTable(t *testing.T) {
|
|||
|
||||
comp := LockCompatible()
|
||||
uncp := LockUncompatible()
|
||||
spcl := LockSpecial(func(lock distlock.Lock, testLockName string) bool { return true })
|
||||
spcl := LockSpecial(func(lock types.Lock, testLockName string) bool { return true })
|
||||
table.Row(comp, comp, comp)
|
||||
table.Row(comp, uncp, comp)
|
||||
table.Row(comp, comp, spcl)
|
||||
|
||||
err := table.Test(distlock.Lock{
|
||||
err := table.Test(types.Lock{
|
||||
Name: "l1",
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = table.Test(distlock.Lock{
|
||||
err = table.Test(types.Lock{
|
||||
Name: "l2",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = table.Test(distlock.Lock{
|
||||
err = table.Test(types.Lock{
|
||||
Name: "l3",
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -3,8 +3,8 @@ package lockprovider
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/common/utils/lo2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -27,7 +27,7 @@ func NewShardStoreLock() *ShardStoreLock {
|
|||
}
|
||||
|
||||
// CanLock 判断这个锁能否锁定成功
|
||||
func (l *ShardStoreLock) CanLock(lock distlock.Lock) error {
|
||||
func (l *ShardStoreLock) CanLock(lock types.Lock) error {
|
||||
nodeLock, ok := l.stgLocks[lock.Path[ShardStoreStorageIDPathIndex]]
|
||||
if !ok {
|
||||
// 不能直接返回nil,因为如果锁数据的格式不对,也不能获取锁。
|
||||
|
@ -39,7 +39,7 @@ func (l *ShardStoreLock) CanLock(lock distlock.Lock) error {
|
|||
}
|
||||
|
||||
// 锁定。在内部可以不用判断能否加锁,外部需要保证调用此函数前调用了CanLock进行检查
|
||||
func (l *ShardStoreLock) Lock(reqID string, lock distlock.Lock) error {
|
||||
func (l *ShardStoreLock) Lock(reqID types.RequestID, lock types.Lock) error {
|
||||
stgID := lock.Path[ShardStoreStorageIDPathIndex]
|
||||
|
||||
nodeLock, ok := l.stgLocks[stgID]
|
||||
|
@ -52,7 +52,7 @@ func (l *ShardStoreLock) Lock(reqID string, lock distlock.Lock) error {
|
|||
}
|
||||
|
||||
// 解锁
|
||||
func (l *ShardStoreLock) Unlock(reqID string, lock distlock.Lock) error {
|
||||
func (l *ShardStoreLock) Unlock(reqID types.RequestID, lock types.Lock) error {
|
||||
stgID := lock.Path[ShardStoreStorageIDPathIndex]
|
||||
|
||||
nodeLock, ok := l.stgLocks[stgID]
|
||||
|
@ -63,25 +63,14 @@ func (l *ShardStoreLock) Unlock(reqID string, lock distlock.Lock) error {
|
|||
return nodeLock.Unlock(reqID, lock)
|
||||
}
|
||||
|
||||
// GetTargetString 将锁对象序列化为字符串,方便存储到ETCD
|
||||
func (l *ShardStoreLock) GetTargetString(target any) (string, error) {
|
||||
tar := target.(StringLockTarget)
|
||||
return StringLockTargetToString(&tar)
|
||||
}
|
||||
|
||||
// ParseTargetString 解析字符串格式的锁对象数据
|
||||
func (l *ShardStoreLock) ParseTargetString(targetStr string) (any, error) {
|
||||
return StringLockTargetFromString(targetStr)
|
||||
}
|
||||
|
||||
// Clear 清除内部所有状态
|
||||
func (l *ShardStoreLock) Clear() {
|
||||
l.stgLocks = make(map[string]*ShardStoreStorageLock)
|
||||
}
|
||||
|
||||
type ShardStoreStorageLock struct {
|
||||
buzyReqIDs []string
|
||||
gcReqIDs []string
|
||||
buzyReqIDs []types.RequestID
|
||||
gcReqIDs []types.RequestID
|
||||
|
||||
lockCompatibilityTable *LockCompatibilityTable
|
||||
}
|
||||
|
@ -107,12 +96,12 @@ func NewShardStoreStorageLock() *ShardStoreStorageLock {
|
|||
}
|
||||
|
||||
// CanLock 判断这个锁能否锁定成功
|
||||
func (l *ShardStoreStorageLock) CanLock(lock distlock.Lock) error {
|
||||
func (l *ShardStoreStorageLock) CanLock(lock types.Lock) error {
|
||||
return l.lockCompatibilityTable.Test(lock)
|
||||
}
|
||||
|
||||
// 锁定
|
||||
func (l *ShardStoreStorageLock) Lock(reqID string, lock distlock.Lock) error {
|
||||
func (l *ShardStoreStorageLock) Lock(reqID types.RequestID, lock types.Lock) error {
|
||||
switch lock.Name {
|
||||
case ShardStoreBuzyLock:
|
||||
l.buzyReqIDs = append(l.buzyReqIDs, reqID)
|
||||
|
@ -126,7 +115,7 @@ func (l *ShardStoreStorageLock) Lock(reqID string, lock distlock.Lock) error {
|
|||
}
|
||||
|
||||
// 解锁
|
||||
func (l *ShardStoreStorageLock) Unlock(reqID string, lock distlock.Lock) error {
|
||||
func (l *ShardStoreStorageLock) Unlock(reqID types.RequestID, lock types.Lock) error {
|
||||
switch lock.Name {
|
||||
case ShardStoreBuzyLock:
|
||||
l.buzyReqIDs = lo2.Remove(l.buzyReqIDs, reqID)
|
||||
|
|
|
@ -4,25 +4,25 @@ import (
|
|||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
func Test_ShardStoreLock(t *testing.T) {
|
||||
cases := []struct {
|
||||
title string
|
||||
initLocks []distlock.Lock
|
||||
doLock distlock.Lock
|
||||
initLocks []types.Lock
|
||||
doLock types.Lock
|
||||
wantOK bool
|
||||
}{
|
||||
{
|
||||
title: "同节点,同一个Buzy锁",
|
||||
initLocks: []distlock.Lock{
|
||||
initLocks: []types.Lock{
|
||||
{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreBuzyLock,
|
||||
},
|
||||
},
|
||||
doLock: distlock.Lock{
|
||||
doLock: types.Lock{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreBuzyLock,
|
||||
},
|
||||
|
@ -30,13 +30,13 @@ func Test_ShardStoreLock(t *testing.T) {
|
|||
},
|
||||
{
|
||||
title: "同节点,同一个GC锁",
|
||||
initLocks: []distlock.Lock{
|
||||
initLocks: []types.Lock{
|
||||
{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreGCLock,
|
||||
},
|
||||
},
|
||||
doLock: distlock.Lock{
|
||||
doLock: types.Lock{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreGCLock,
|
||||
},
|
||||
|
@ -44,17 +44,17 @@ func Test_ShardStoreLock(t *testing.T) {
|
|||
},
|
||||
{
|
||||
title: "同时设置Buzy和GC",
|
||||
initLocks: []distlock.Lock{
|
||||
initLocks: []types.Lock{
|
||||
{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreBuzyLock,
|
||||
Target: *NewStringLockTarget(),
|
||||
Target: NewStringLockTarget(),
|
||||
},
|
||||
},
|
||||
doLock: distlock.Lock{
|
||||
doLock: types.Lock{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreGCLock,
|
||||
Target: *NewStringLockTarget(),
|
||||
Target: NewStringLockTarget(),
|
||||
},
|
||||
wantOK: false,
|
||||
},
|
||||
|
@ -80,7 +80,7 @@ func Test_ShardStoreLock(t *testing.T) {
|
|||
Convey("解锁", t, func() {
|
||||
ipfsLock := NewShardStoreLock()
|
||||
|
||||
lock := distlock.Lock{
|
||||
lock := types.Lock{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreBuzyLock,
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func Test_ShardStoreLock(t *testing.T) {
|
|||
|
||||
ipfsLock.Unlock("req1", lock)
|
||||
|
||||
lock = distlock.Lock{
|
||||
lock = types.Lock{
|
||||
Path: []string{ShardStoreLockPathPrefix, "hub1"},
|
||||
Name: ShardStoreGCLock,
|
||||
}
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
package lockprovider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/common/utils/lo2"
|
||||
)
|
||||
|
||||
const (
|
||||
StorageLockPathPrefix = "Storage"
|
||||
StorageHubIDPathIndex = 1
|
||||
StorageBuzyLock = "Buzy"
|
||||
StorageGCLock = "GC"
|
||||
)
|
||||
|
||||
type StorageLock struct {
|
||||
nodeLocks map[string]*StorageNodeLock
|
||||
dummyLock *StorageNodeLock
|
||||
}
|
||||
|
||||
func NewStorageLock() *StorageLock {
|
||||
return &StorageLock{
|
||||
nodeLocks: make(map[string]*StorageNodeLock),
|
||||
dummyLock: NewStorageNodeLock(),
|
||||
}
|
||||
}
|
||||
|
||||
// CanLock 判断这个锁能否锁定成功
|
||||
func (l *StorageLock) CanLock(lock distlock.Lock) error {
|
||||
nodeLock, ok := l.nodeLocks[lock.Path[StorageHubIDPathIndex]]
|
||||
if !ok {
|
||||
// 不能直接返回nil,因为如果锁数据的格式不对,也不能获取锁。
|
||||
// 这里使用一个空Provider来进行检查。
|
||||
return l.dummyLock.CanLock(lock)
|
||||
}
|
||||
|
||||
return nodeLock.CanLock(lock)
|
||||
}
|
||||
|
||||
// 锁定。在内部可以不用判断能否加锁,外部需要保证调用此函数前调用了CanLock进行检查
|
||||
func (l *StorageLock) Lock(reqID string, lock distlock.Lock) error {
|
||||
hubID := lock.Path[StorageHubIDPathIndex]
|
||||
|
||||
nodeLock, ok := l.nodeLocks[hubID]
|
||||
if !ok {
|
||||
nodeLock = NewStorageNodeLock()
|
||||
l.nodeLocks[hubID] = nodeLock
|
||||
}
|
||||
|
||||
return nodeLock.Lock(reqID, lock)
|
||||
}
|
||||
|
||||
// 解锁
|
||||
func (l *StorageLock) Unlock(reqID string, lock distlock.Lock) error {
|
||||
hubID := lock.Path[StorageHubIDPathIndex]
|
||||
|
||||
nodeLock, ok := l.nodeLocks[hubID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nodeLock.Unlock(reqID, lock)
|
||||
}
|
||||
|
||||
// GetTargetString 将锁对象序列化为字符串,方便存储到ETCD
|
||||
func (l *StorageLock) GetTargetString(target any) (string, error) {
|
||||
tar := target.(StringLockTarget)
|
||||
return StringLockTargetToString(&tar)
|
||||
}
|
||||
|
||||
// ParseTargetString 解析字符串格式的锁对象数据
|
||||
func (l *StorageLock) ParseTargetString(targetStr string) (any, error) {
|
||||
return StringLockTargetFromString(targetStr)
|
||||
}
|
||||
|
||||
// Clear 清除内部所有状态
|
||||
func (l *StorageLock) Clear() {
|
||||
l.nodeLocks = make(map[string]*StorageNodeLock)
|
||||
}
|
||||
|
||||
type StorageNodeLock struct {
|
||||
buzyReqIDs []string
|
||||
gcReqIDs []string
|
||||
|
||||
lockCompatibilityTable *LockCompatibilityTable
|
||||
}
|
||||
|
||||
func NewStorageNodeLock() *StorageNodeLock {
|
||||
compTable := &LockCompatibilityTable{}
|
||||
|
||||
StorageLock := StorageNodeLock{
|
||||
lockCompatibilityTable: compTable,
|
||||
}
|
||||
|
||||
compTable.
|
||||
Column(StorageBuzyLock, func() bool { return len(StorageLock.buzyReqIDs) > 0 }).
|
||||
Column(StorageGCLock, func() bool { return len(StorageLock.gcReqIDs) > 0 })
|
||||
|
||||
comp := LockCompatible()
|
||||
uncp := LockUncompatible()
|
||||
|
||||
compTable.MustRow(comp, uncp)
|
||||
compTable.MustRow(uncp, comp)
|
||||
|
||||
return &StorageLock
|
||||
}
|
||||
|
||||
// CanLock 判断这个锁能否锁定成功
|
||||
func (l *StorageNodeLock) CanLock(lock distlock.Lock) error {
|
||||
return l.lockCompatibilityTable.Test(lock)
|
||||
}
|
||||
|
||||
// 锁定
|
||||
func (l *StorageNodeLock) Lock(reqID string, lock distlock.Lock) error {
|
||||
switch lock.Name {
|
||||
case StorageBuzyLock:
|
||||
l.buzyReqIDs = append(l.buzyReqIDs, reqID)
|
||||
case StorageGCLock:
|
||||
l.gcReqIDs = append(l.gcReqIDs, reqID)
|
||||
default:
|
||||
return fmt.Errorf("unknow lock name: %s", lock.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解锁
|
||||
func (l *StorageNodeLock) Unlock(reqID string, lock distlock.Lock) error {
|
||||
switch lock.Name {
|
||||
case StorageBuzyLock:
|
||||
l.buzyReqIDs = lo2.Remove(l.buzyReqIDs, reqID)
|
||||
case StorageGCLock:
|
||||
l.gcReqIDs = lo2.Remove(l.gcReqIDs, reqID)
|
||||
default:
|
||||
return fmt.Errorf("unknow lock name: %s", lock.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/samber/lo"
|
||||
"gitlink.org.cn/cloudream/common/utils/serder"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
type StringLockTarget struct {
|
||||
|
@ -43,6 +44,25 @@ func (t *StringLockTarget) IsConflict(other *StringLockTarget) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (t *StringLockTarget) Equals(other types.LockTarget) bool {
|
||||
st, ok := other.(*StringLockTarget)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(t.Components) != len(st.Components) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(t.Components); i++ {
|
||||
if !t.Components[i].IsEquals(&st.Components[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type StringLockTargetComponet struct {
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package distlock
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
type Mutex struct {
|
||||
svc *Service
|
||||
lockReq types.LockRequest
|
||||
lockReqID types.RequestID
|
||||
}
|
||||
|
||||
func (m *Mutex) Unlock() {
|
||||
m.svc.release(m.lockReqID, m.lockReq)
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package distlock
|
||||
|
||||
import "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
|
||||
type Reentrant struct {
|
||||
svc *Service
|
||||
reqs []types.LockRequest
|
||||
locked []*Mutex
|
||||
}
|
||||
|
||||
func (r *Reentrant) Lock(req types.LockRequest, opt ...AcquireOptionFn) error {
|
||||
var willLock []types.Lock
|
||||
|
||||
loop:
|
||||
for _, lock := range req.Locks {
|
||||
for _, req := range r.reqs {
|
||||
for _, locked := range req.Locks {
|
||||
if locked.Equals(lock) {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
willLock = append(willLock, lock)
|
||||
}
|
||||
|
||||
if len(willLock) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
newReq := types.LockRequest{
|
||||
Reason: req.Reason,
|
||||
Locks: willLock,
|
||||
}
|
||||
|
||||
m, err := r.svc.Acquire(newReq, opt...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.reqs = append(r.reqs, newReq)
|
||||
r.locked = append(r.locked, m)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reentrant) Unlock() {
|
||||
for i := len(r.reqs) - 1; i >= 0; i-- {
|
||||
r.locked[i].Unlock()
|
||||
}
|
||||
r.locked = nil
|
||||
r.reqs = nil
|
||||
}
|
|
@ -1,30 +1,29 @@
|
|||
package reqbuilder
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/common/utils/lo2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
type LockRequestBuilder struct {
|
||||
locks []distlock.Lock
|
||||
locks []types.Lock
|
||||
}
|
||||
|
||||
func NewBuilder() *LockRequestBuilder {
|
||||
return &LockRequestBuilder{}
|
||||
}
|
||||
|
||||
func (b *LockRequestBuilder) Build() distlock.LockRequest {
|
||||
return distlock.LockRequest{
|
||||
func (b *LockRequestBuilder) IsEmpty() bool {
|
||||
return len(b.locks) == 0
|
||||
}
|
||||
|
||||
func (b *LockRequestBuilder) Build() types.LockRequest {
|
||||
return types.LockRequest{
|
||||
Locks: lo2.ArrayClone(b.locks),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LockRequestBuilder) MutexLock(svc *distlock.Service) (*distlock.Mutex, error) {
|
||||
mutex := distlock.NewMutex(svc, b.Build())
|
||||
err := mutex.Lock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mutex, nil
|
||||
func (b *LockRequestBuilder) MutexLock(svc *distlock.Service, opt ...distlock.AcquireOptionFn) (*distlock.Mutex, error) {
|
||||
return svc.Acquire(b.Build(), opt...)
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package reqbuilder
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/lockprovider"
|
||||
)
|
||||
|
||||
type MetadataLockReqBuilder struct {
|
||||
*LockRequestBuilder
|
||||
}
|
||||
|
||||
func (b *LockRequestBuilder) Metadata() *MetadataLockReqBuilder {
|
||||
return &MetadataLockReqBuilder{LockRequestBuilder: b}
|
||||
}
|
||||
|
||||
func (b *MetadataLockReqBuilder) makePath(tableName string) []string {
|
||||
return []string{lockprovider.MetadataLockPathPrefix, tableName}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package reqbuilder
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/lockprovider"
|
||||
)
|
||||
|
||||
type MetadataObjectLockReqBuilder struct {
|
||||
*MetadataLockReqBuilder
|
||||
}
|
||||
|
||||
func (b *MetadataLockReqBuilder) Object() *MetadataObjectLockReqBuilder {
|
||||
return &MetadataObjectLockReqBuilder{MetadataLockReqBuilder: b}
|
||||
}
|
||||
|
||||
func (b *MetadataObjectLockReqBuilder) CreateOne(packageID clitypes.PackageID, objectPath string) *MetadataObjectLockReqBuilder {
|
||||
b.locks = append(b.locks, distlock.Lock{
|
||||
Path: b.makePath("Object"),
|
||||
Name: lockprovider.MetadataCreateLock,
|
||||
Target: *lockprovider.NewStringLockTarget().Add(packageID, objectPath),
|
||||
})
|
||||
return b
|
||||
}
|
|
@ -3,9 +3,9 @@ package reqbuilder
|
|||
import (
|
||||
"strconv"
|
||||
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/lockprovider"
|
||||
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
type ShardStoreLockReqBuilder struct {
|
||||
|
@ -15,24 +15,24 @@ type ShardStoreLockReqBuilder struct {
|
|||
func (b *LockRequestBuilder) Shard() *ShardStoreLockReqBuilder {
|
||||
return &ShardStoreLockReqBuilder{LockRequestBuilder: b}
|
||||
}
|
||||
func (b *ShardStoreLockReqBuilder) Buzy(stgID cortypes.StorageID) *ShardStoreLockReqBuilder {
|
||||
b.locks = append(b.locks, distlock.Lock{
|
||||
Path: b.makePath(stgID),
|
||||
func (b *ShardStoreLockReqBuilder) Buzy(spaceID clitypes.UserSpaceID) *ShardStoreLockReqBuilder {
|
||||
b.locks = append(b.locks, types.Lock{
|
||||
Path: b.makePath(spaceID),
|
||||
Name: lockprovider.ShardStoreBuzyLock,
|
||||
Target: *lockprovider.NewStringLockTarget(),
|
||||
Target: lockprovider.NewEmptyTarget(),
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ShardStoreLockReqBuilder) GC(stgID cortypes.StorageID) *ShardStoreLockReqBuilder {
|
||||
b.locks = append(b.locks, distlock.Lock{
|
||||
Path: b.makePath(stgID),
|
||||
func (b *ShardStoreLockReqBuilder) GC(spaceID clitypes.UserSpaceID) *ShardStoreLockReqBuilder {
|
||||
b.locks = append(b.locks, types.Lock{
|
||||
Path: b.makePath(spaceID),
|
||||
Name: lockprovider.ShardStoreGCLock,
|
||||
Target: *lockprovider.NewStringLockTarget(),
|
||||
Target: lockprovider.NewEmptyTarget(),
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ShardStoreLockReqBuilder) makePath(hubID cortypes.StorageID) []string {
|
||||
func (b *ShardStoreLockReqBuilder) makePath(hubID clitypes.UserSpaceID) []string {
|
||||
return []string{lockprovider.ShardStoreLockPathPrefix, strconv.FormatInt(int64(hubID), 10)}
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
package reqbuilder
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/lockprovider"
|
||||
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||
)
|
||||
|
||||
type StorageLockReqBuilder struct {
|
||||
*LockRequestBuilder
|
||||
}
|
||||
|
||||
func (b *LockRequestBuilder) Storage() *StorageLockReqBuilder {
|
||||
return &StorageLockReqBuilder{LockRequestBuilder: b}
|
||||
}
|
||||
|
||||
func (b *StorageLockReqBuilder) Buzy(storageID cortypes.StorageID) *StorageLockReqBuilder {
|
||||
b.locks = append(b.locks, distlock.Lock{
|
||||
Path: b.makePath(storageID),
|
||||
Name: lockprovider.StorageBuzyLock,
|
||||
Target: *lockprovider.NewStringLockTarget(),
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *StorageLockReqBuilder) GC(storageID cortypes.StorageID) *StorageLockReqBuilder {
|
||||
b.locks = append(b.locks, distlock.Lock{
|
||||
Path: b.makePath(storageID),
|
||||
Name: lockprovider.StorageGCLock,
|
||||
Target: *lockprovider.NewStringLockTarget(),
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *StorageLockReqBuilder) makePath(storageID cortypes.StorageID) []string {
|
||||
return []string{lockprovider.StorageLockPathPrefix, strconv.FormatInt(int64(storageID), 10)}
|
||||
}
|
|
@ -1,62 +1,194 @@
|
|||
package distlock
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitlink.org.cn/cloudream/common/pkgs/future"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/trie"
|
||||
"gitlink.org.cn/cloudream/common/utils/lo2"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/lockprovider"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/types"
|
||||
)
|
||||
|
||||
type Service = distlock.Service
|
||||
type AcquireOption struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
type Mutex = distlock.Mutex
|
||||
type AcquireOptionFn func(opt *AcquireOption)
|
||||
|
||||
func NewService(cfg *distlock.Config) (*distlock.Service, error) {
|
||||
srv, err := distlock.NewService(cfg, initProviders())
|
||||
func WithTimeout(timeout time.Duration) AcquireOptionFn {
|
||||
return func(opt *AcquireOption) {
|
||||
opt.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
lock *sync.Mutex
|
||||
provdersTrie *trie.Trie[types.LockProvider]
|
||||
acquirings []*acquireInfo
|
||||
nextReqID int64
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
svc := &Service{
|
||||
lock: &sync.Mutex{},
|
||||
provdersTrie: trie.NewTrie[types.LockProvider](),
|
||||
}
|
||||
|
||||
svc.provdersTrie.Create([]any{lockprovider.ShardStoreLockPathPrefix, trie.WORD_ANY}).Value = lockprovider.NewShardStoreLock()
|
||||
return svc
|
||||
}
|
||||
|
||||
type acquireInfo struct {
|
||||
Request types.LockRequest
|
||||
Callback *future.SetValueFuture[types.RequestID]
|
||||
LastErr error
|
||||
}
|
||||
|
||||
func (svc *Service) Acquire(req types.LockRequest, opts ...AcquireOptionFn) (*Mutex, error) {
|
||||
var opt = AcquireOption{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
for _, fn := range opts {
|
||||
fn(&opt)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if opt.Timeout != 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, opt.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// 就地检测锁是否可用
|
||||
svc.lock.Lock()
|
||||
defer svc.lock.Unlock()
|
||||
|
||||
reqID, err := svc.tryAcquireOne(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return srv, nil
|
||||
if reqID != "" {
|
||||
return &Mutex{
|
||||
svc: svc,
|
||||
lockReq: req,
|
||||
lockReqID: reqID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initProviders() []distlock.PathProvider {
|
||||
var provs []distlock.PathProvider
|
||||
// 就地检测失败,那么就需要异步等待锁可用
|
||||
info := &acquireInfo{
|
||||
Request: req,
|
||||
Callback: future.NewSetValue[types.RequestID](),
|
||||
}
|
||||
svc.acquirings = append(svc.acquirings, info)
|
||||
|
||||
provs = append(provs, initMetadataLockProviders()...)
|
||||
// 等待的时候不加锁
|
||||
svc.lock.Unlock()
|
||||
reqID, err = info.Callback.Wait(ctx)
|
||||
svc.lock.Lock()
|
||||
|
||||
provs = append(provs, initShardLockProviders()...)
|
||||
|
||||
provs = append(provs, initStorageLockProviders()...)
|
||||
|
||||
return provs
|
||||
if err == nil {
|
||||
return &Mutex{
|
||||
svc: svc,
|
||||
lockReq: req,
|
||||
lockReqID: reqID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initMetadataLockProviders() []distlock.PathProvider {
|
||||
return []distlock.PathProvider{
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Hub"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Storage"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "User"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "UserBucket"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "UserHub"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "UserStorage"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Bucket"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Object"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Package"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "ObjectRep"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "ObjectBlock"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Cache"),
|
||||
distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Location"),
|
||||
if err != future.ErrCanceled {
|
||||
lo2.Remove(svc.acquirings, info)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 如果第一次等待是超时错误,那么在锁里再尝试获取一次结果
|
||||
reqID, err = info.Callback.TryGetValue()
|
||||
if err == nil {
|
||||
return &Mutex{
|
||||
svc: svc,
|
||||
lockReq: req,
|
||||
lockReqID: reqID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
lo2.Remove(svc.acquirings, info)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *Service) BeginReentrant() *Reentrant {
|
||||
return &Reentrant{
|
||||
svc: s,
|
||||
}
|
||||
}
|
||||
|
||||
func initShardLockProviders() []distlock.PathProvider {
|
||||
return []distlock.PathProvider{
|
||||
distlock.NewPathProvider(lockprovider.NewShardStoreLock(), lockprovider.ShardStoreLockPathPrefix, trie.WORD_ANY),
|
||||
func (s *Service) release(reqID types.RequestID, req types.LockRequest) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.releaseRequest(reqID, req)
|
||||
s.tryAcquirings()
|
||||
}
|
||||
|
||||
func (a *Service) tryAcquirings() {
|
||||
for i := 0; i < len(a.acquirings); i++ {
|
||||
req := a.acquirings[i]
|
||||
|
||||
reqID, err := a.tryAcquireOne(req.Request)
|
||||
if err != nil {
|
||||
req.LastErr = err
|
||||
continue
|
||||
}
|
||||
|
||||
req.Callback.SetValue(reqID)
|
||||
a.acquirings[i] = nil
|
||||
}
|
||||
|
||||
a.acquirings = lo2.RemoveAllDefault(a.acquirings)
|
||||
}
|
||||
|
||||
func (s *Service) tryAcquireOne(req types.LockRequest) (types.RequestID, error) {
|
||||
err := s.testOneRequest(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
reqID := types.RequestID(fmt.Sprintf("%d", s.nextReqID))
|
||||
s.nextReqID++
|
||||
|
||||
s.applyRequest(reqID, req)
|
||||
return reqID, nil
|
||||
}
|
||||
|
||||
func (s *Service) testOneRequest(req types.LockRequest) error {
|
||||
for _, lock := range req.Locks {
|
||||
n, ok := s.provdersTrie.WalkEnd(lock.Path)
|
||||
if !ok || n.Value == nil {
|
||||
return fmt.Errorf("lock provider not found for path %v", lock.Path)
|
||||
}
|
||||
|
||||
err := n.Value.CanLock(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func initStorageLockProviders() []distlock.PathProvider {
|
||||
return []distlock.PathProvider{
|
||||
distlock.NewPathProvider(lockprovider.NewStorageLock(), lockprovider.StorageLockPathPrefix, trie.WORD_ANY),
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) applyRequest(reqID types.RequestID, req types.LockRequest) {
|
||||
for _, lock := range req.Locks {
|
||||
p, _ := s.provdersTrie.WalkEnd(lock.Path)
|
||||
p.Value.Lock(reqID, lock)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) releaseRequest(reqID types.RequestID, req types.LockRequest) {
|
||||
for _, lock := range req.Locks {
|
||||
p, _ := s.provdersTrie.WalkEnd(lock.Path)
|
||||
p.Value.Unlock(reqID, lock)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlink.org.cn/cloudream/common/utils/lo2"
|
||||
)
|
||||
|
||||
type RequestID string
|
||||
|
||||
type Lock struct {
|
||||
Path []string // 锁路径,存储的是路径的每一部分
|
||||
Name string // 锁名
|
||||
Target LockTarget // 锁对象,由具体的Provider去解析
|
||||
}
|
||||
|
||||
func (b *Lock) Equals(other Lock) bool {
|
||||
return lo2.ArrayEquals(b.Path, other.Path) && b.Name == other.Name && b.Target.Equals(other.Target)
|
||||
}
|
||||
|
||||
type LockTarget interface {
|
||||
Equals(other LockTarget) bool
|
||||
}
|
||||
|
||||
type LockRequest struct {
|
||||
Reason string
|
||||
Locks []Lock
|
||||
}
|
||||
|
||||
func (b *LockRequest) Add(lock Lock) {
|
||||
b.Locks = append(b.Locks, lock)
|
||||
}
|
||||
|
||||
type LockProvider interface {
|
||||
// CanLock 判断这个锁能否锁定成功
|
||||
CanLock(lock Lock) error
|
||||
|
||||
// Lock 锁定。由于同一个锁请求内的锁不检查冲突,因此这个函数必须支持有冲突的锁进行锁定。
|
||||
Lock(reqID RequestID, lock Lock) error
|
||||
|
||||
// 解锁
|
||||
Unlock(reqID RequestID, lock Lock) error
|
||||
|
||||
// Clear 清除内部所有状态
|
||||
Clear()
|
||||
}
|
||||
|
||||
type LockTargetBusyError struct {
|
||||
lockName string
|
||||
}
|
||||
|
||||
func (e *LockTargetBusyError) Error() string {
|
||||
return fmt.Sprintf("the lock object is locked by %s", e.lockName)
|
||||
}
|
||||
|
||||
func NewLockTargetBusyError(lockName string) *LockTargetBusyError {
|
||||
return &LockTargetBusyError{
|
||||
lockName: lockName,
|
||||
}
|
||||
}
|
|
@ -17,15 +17,6 @@
|
|||
"password": "123456",
|
||||
"vhost": "/"
|
||||
},
|
||||
"ipfs": null,
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a client"
|
||||
},
|
||||
"connectivity": {
|
||||
"testInterval": 300
|
||||
},
|
||||
|
|
|
@ -25,14 +25,6 @@
|
|||
"ipfs": {
|
||||
"address": "127.0.0.1:5001"
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a agent"
|
||||
},
|
||||
"connectivity": {
|
||||
"testInterval": 300
|
||||
},
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"ecFileSizeThreshold": 104857600,
|
||||
"nodeUnavailableSeconds": 300,
|
||||
"logger": {
|
||||
"output": "file",
|
||||
"outputFileName": "scanner",
|
||||
"outputDirectory": "log",
|
||||
"level": "debug"
|
||||
},
|
||||
"db": {
|
||||
"address": "127.0.0.1:3306",
|
||||
"account": "root",
|
||||
"password": "123456",
|
||||
"databaseName": "cloudream"
|
||||
},
|
||||
"rabbitMQ": {
|
||||
"address": "127.0.0.1:5672",
|
||||
"account": "cloudream",
|
||||
"password": "123456",
|
||||
"vhost": "/"
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a scanner"
|
||||
}
|
||||
}
|
|
@ -17,15 +17,6 @@
|
|||
"password": "123456",
|
||||
"vhost": "/"
|
||||
},
|
||||
"ipfs": null,
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a client"
|
||||
},
|
||||
"connectivity": {
|
||||
"testInterval": 300
|
||||
},
|
||||
|
|
|
@ -22,17 +22,6 @@
|
|||
"password": "123456",
|
||||
"vhost": "/"
|
||||
},
|
||||
"ipfs": {
|
||||
"address": "127.0.0.1:5001"
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a agent"
|
||||
},
|
||||
"connectivity": {
|
||||
"testInterval": 300
|
||||
},
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"ecFileSizeThreshold": 104857600,
|
||||
"nodeUnavailableSeconds": 300,
|
||||
"logger": {
|
||||
"output": "file",
|
||||
"outputFileName": "scanner",
|
||||
"outputDirectory": "log",
|
||||
"level": "debug"
|
||||
},
|
||||
"db": {
|
||||
"address": "127.0.0.1:3306",
|
||||
"account": "root",
|
||||
"password": "123456",
|
||||
"databaseName": "cloudream"
|
||||
},
|
||||
"rabbitMQ": {
|
||||
"address": "127.0.0.1:5672",
|
||||
"account": "cloudream",
|
||||
"password": "123456",
|
||||
"vhost": "/"
|
||||
},
|
||||
"distlock": {
|
||||
"etcdAddress": "127.0.0.1:2379",
|
||||
"etcdUsername": "",
|
||||
"etcdPassword": "",
|
||||
"etcdLockLeaseTimeSec": 5,
|
||||
"randomReleasingDelayMs": 3000,
|
||||
"serviceDescription": "I am a scanner"
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@ import (
|
|||
"gitlink.org.cn/cloudream/common/pkgs/mq"
|
||||
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/models/datamap"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock"
|
||||
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/grpc/hub"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/sysevent"
|
||||
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||
|
@ -130,12 +129,6 @@ func serve(configPath string, httpAddr string) {
|
|||
// })
|
||||
// go serveAccessStat(acStat)
|
||||
|
||||
// 初始化分布式锁服务
|
||||
distlock, err := distlock.NewService(&config.Cfg().DistLock)
|
||||
if err != nil {
|
||||
logger.Fatalf("new ipfs failed, err: %s", err.Error())
|
||||
}
|
||||
|
||||
// 初始化系统事件发布器
|
||||
evtPub, err := sysevent.NewPublisher(sysevent.ConfigFromMQConfig(config.Cfg().RabbitMQ), &datamap.SourceHub{
|
||||
HubID: hubCfg.Hub.HubID,
|
||||
|
@ -173,8 +166,6 @@ func serve(configPath string, httpAddr string) {
|
|||
hubrpc.RegisterHubServer(s, grpcsvc.NewService(&worker, stgPool))
|
||||
go serveGRPC(s, lis)
|
||||
|
||||
go serveDistLock(distlock)
|
||||
|
||||
foever := make(chan struct{})
|
||||
<-foever
|
||||
}
|
||||
|
@ -312,42 +303,3 @@ func serveHTTP(server *http.Server) {
|
|||
// TODO 仅简单结束了程序
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func serveDistLock(svc *distlock.Service) {
|
||||
logger.Info("start serving distlock")
|
||||
|
||||
err := svc.Serve()
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("distlock stopped with error: %s", err.Error())
|
||||
}
|
||||
|
||||
logger.Info("distlock stopped")
|
||||
|
||||
// TODO 仅简单结束了程序
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// func serveAccessStat(svc *accessstat.AccessStat) {
|
||||
// logger.Info("start serving access stat")
|
||||
|
||||
// ch := svc.Start()
|
||||
// loop:
|
||||
// for {
|
||||
// val, err := ch.Receive()
|
||||
// if err != nil {
|
||||
// logger.Errorf("access stat stopped with error: %v", err)
|
||||
// break
|
||||
// }
|
||||
|
||||
// switch val := val.(type) {
|
||||
// case error:
|
||||
// logger.Errorf("access stat stopped with error: %v", val)
|
||||
// break loop
|
||||
// }
|
||||
// }
|
||||
// logger.Info("access stat stopped")
|
||||
|
||||
// // TODO 仅简单结束了程序
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/cloudream/common/pkgs/distlock"
|
||||
log "gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/mq"
|
||||
c "gitlink.org.cn/cloudream/common/utils/config"
|
||||
|
@ -17,7 +16,6 @@ type Config struct {
|
|||
GRPC *grpc.Config `json:"grpc"`
|
||||
Logger log.Config `json:"logger"`
|
||||
RabbitMQ mq.Config `json:"rabbitMQ"`
|
||||
DistLock distlock.Config `json:"distlock"`
|
||||
Connectivity connectivity.Config `json:"connectivity"`
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue