|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- package publock
-
- import (
- "context"
- "sync"
- "time"
-
- "github.com/google/uuid"
- "gitlink.org.cn/cloudream/common/pkgs/future"
- "gitlink.org.cn/cloudream/common/pkgs/logger"
- "gitlink.org.cn/cloudream/common/utils/lo2"
- "gitlink.org.cn/cloudream/common/utils/serder"
- "gitlink.org.cn/cloudream/jcs-pub/client/internal/cluster"
- "gitlink.org.cn/cloudream/jcs-pub/client/internal/publock/types"
- "gitlink.org.cn/cloudream/jcs-pub/common/ecode"
- clirpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/client"
- )
-
- type AcquireOptionFn func(opt *types.AcquireOption)
-
- func WithTimeout(timeout time.Duration) AcquireOptionFn {
- return func(opt *types.AcquireOption) {
- opt.Timeout = timeout
- }
- }
-
- func WithReason(reason string) AcquireOptionFn {
- return func(opt *types.AcquireOption) {
- opt.Reason = reason
- }
- }
-
- type PubLock struct {
- core *Core
- clster *cluster.Cluster
- lock sync.Mutex
- acquirings map[types.RequestID]*svcAcquiring
- acquired []types.RequestID
- releasing map[types.RequestID]*svcReleasing
- }
-
- func New(cfg Config, clster *cluster.Cluster) *PubLock {
- return &PubLock{
- core: NewCore(cfg, clster),
- clster: clster,
- acquirings: make(map[types.RequestID]*svcAcquiring),
- releasing: make(map[types.RequestID]*svcReleasing),
- }
- }
-
- func (s *PubLock) BeginReentrant() *Reentrant {
- r := &Reentrant{
- p: s,
- }
- r.T = r
- return r
- }
-
- func (p *PubLock) BeginMutex() *MutexBuilder {
- m := &MutexBuilder{
- pub: p,
- }
- m.T = m
- return m
- }
-
- func (p *PubLock) Core() *Core {
- return p.core
- }
-
- func (p *PubLock) Start() {
- log := logger.WithField("Mod", "PubLock")
-
- go func() {
- ch := p.core.EventChan()
- evt := ch.Receive()
-
- ticker := time.NewTicker(time.Second)
- defer ticker.Stop()
-
- loop:
- for {
- select {
- case <-ticker.C:
- p.lock.Lock()
- reqIDs := make([]types.RequestID, len(p.acquired))
- copy(reqIDs, p.acquired)
- p.lock.Unlock()
-
- if len(reqIDs) == 0 {
- continue
- }
-
- cmd := Renew{
- IDs: reqIDs,
- }
- if p.clster == nil {
- p.core.Apply(&cmd)
- } else {
- data, err := serder.ObjectToJSONEx(cmd)
- if err != nil {
- log.Warnf("cmd %T to json: %v", cmd, err)
- continue
- }
-
- _, cerr := p.clster.MasterClient().ClusterApplyLog(context.Background(), &clirpc.ClusterApplyLog{
- FSMID: p.core.FSM().ID(),
- Data: data,
- Timeout: time.Second * 3,
- })
- if cerr != nil {
- log.Errorf("apply renew: %v", cerr)
- }
- }
-
- case ret := <-evt.Chan():
- if ret.Err != nil {
- break loop
- }
-
- p.lock.Lock()
- switch e := ret.Value.(type) {
- case *AcquireResult:
- a, ok := p.acquirings[e.Raw.ID]
- if !ok {
- break
- }
-
- if e.Error == nil {
- a.Callback.SetVoid()
- p.acquired = append(p.acquired, e.Raw.ID)
- } else {
- a.Callback.SetError(e.Error)
- }
- delete(p.acquirings, e.Raw.ID)
-
- case *Released:
- r, ok := p.releasing[e.ID]
- if !ok {
- break
- }
-
- r.Callback.SetVoid()
- p.acquired = lo2.Remove(p.acquired, e.ID)
- delete(p.releasing, e.ID)
-
- }
- p.lock.Unlock()
-
- evt = ch.Receive()
- }
- }
- }()
-
- p.core.Start()
- }
-
- func (p *PubLock) Stop() {
- p.core.Stop()
- }
-
- func (p *PubLock) Acquire(req types.LockRequest, opt types.AcquireOption) (LockedRequest, *ecode.CodeError) {
- p.lock.Lock()
-
- cmd := Acquire{
- ID: types.RequestID(uuid.NewString()),
- Request: req,
- Timeout: opt.Timeout,
- Reason: opt.Reason,
- }
-
- ac := &svcAcquiring{
- RequestID: cmd.ID,
- Request: cmd.Request,
- Callback: future.NewSetVoid(),
- }
- p.acquirings[cmd.ID] = ac
- p.lock.Unlock()
-
- if p.clster == nil {
- p.core.Apply(&cmd)
- } else {
- data, err := serder.ObjectToJSONEx(cmd)
- if err != nil {
- return LockedRequest{}, ecode.Newf(ecode.OperationFailed, "cmd %T to json: %v", cmd, err)
- }
-
- _, cerr := p.clster.MasterClient().ClusterApplyLog(context.Background(), &clirpc.ClusterApplyLog{
- FSMID: p.core.FSM().ID(),
- Data: data,
- Timeout: opt.Timeout,
- })
- if cerr != nil {
- return LockedRequest{}, ecode.New(ecode.ErrorCode(cerr.Code), cerr.Message)
- }
- }
-
- err := ac.Callback.Wait(context.Background())
- if err != nil {
- return LockedRequest{}, ecode.Newf(ecode.OperationFailed, "wait acquire: %v", err)
- }
-
- return LockedRequest{
- Req: req,
- ReqID: ac.RequestID,
- }, nil
- }
-
- func (p *PubLock) Release(reqID types.RequestID) {
- log := logger.WithField("Mod", "PubLock")
-
- p.lock.Lock()
- cmd := Release{
- ID: reqID,
- }
- r := &svcReleasing{
- RequestID: cmd.ID,
- Callback: future.NewSetVoid(),
- }
- p.releasing[cmd.ID] = r
- p.lock.Unlock()
-
- if p.clster == nil {
- p.core.Apply(&cmd)
- } else {
- data, err := serder.ObjectToJSONEx(cmd)
- if err != nil {
- log.Warnf("cmd %T to json: %v", cmd, err)
- return
- }
-
- _, cerr := p.clster.MasterClient().ClusterApplyLog(context.Background(), &clirpc.ClusterApplyLog{
- FSMID: p.core.FSM().ID(),
- Data: data,
- Timeout: 0,
- })
- if cerr != nil {
- log.Errorf("apply release: %v", cerr)
- }
- }
-
- err := r.Callback.Wait(context.Background())
- if err != nil {
- log.Errorf("wait release: %v", err)
- } else {
- log.Tracef("unlock %v", reqID)
- }
- }
-
- type svcAcquiring struct {
- RequestID types.RequestID
- Request types.LockRequest
- Callback *future.SetVoidFuture
- }
-
- type svcReleasing struct {
- RequestID types.RequestID
- Callback *future.SetVoidFuture
- }
|