You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

service.go 5.4 kB

5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package publock
  2. import (
  3. "context"
  4. "sync"
  5. "time"
  6. "github.com/google/uuid"
  7. "gitlink.org.cn/cloudream/common/pkgs/future"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. "gitlink.org.cn/cloudream/common/utils/lo2"
  10. "gitlink.org.cn/cloudream/common/utils/serder"
  11. "gitlink.org.cn/cloudream/jcs-pub/client/internal/cluster"
  12. "gitlink.org.cn/cloudream/jcs-pub/client/internal/publock/types"
  13. "gitlink.org.cn/cloudream/jcs-pub/common/ecode"
  14. clirpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/client"
  15. )
  16. type AcquireOptionFn func(opt *types.AcquireOption)
  17. func WithTimeout(timeout time.Duration) AcquireOptionFn {
  18. return func(opt *types.AcquireOption) {
  19. opt.Timeout = timeout
  20. }
  21. }
  22. func WithReason(reason string) AcquireOptionFn {
  23. return func(opt *types.AcquireOption) {
  24. opt.Reason = reason
  25. }
  26. }
  27. type PubLock struct {
  28. core *Core
  29. clster *cluster.Cluster
  30. lock sync.Mutex
  31. acquirings map[types.RequestID]*svcAcquiring
  32. acquired []types.RequestID
  33. releasing map[types.RequestID]*svcReleasing
  34. }
  35. func New(cfg Config, clster *cluster.Cluster) *PubLock {
  36. return &PubLock{
  37. core: NewCore(cfg, clster),
  38. clster: clster,
  39. acquirings: make(map[types.RequestID]*svcAcquiring),
  40. releasing: make(map[types.RequestID]*svcReleasing),
  41. }
  42. }
  43. func (s *PubLock) BeginReentrant() *Reentrant {
  44. r := &Reentrant{
  45. p: s,
  46. }
  47. r.T = r
  48. return r
  49. }
  50. func (p *PubLock) BeginMutex() *MutexBuilder {
  51. m := &MutexBuilder{
  52. pub: p,
  53. }
  54. m.T = m
  55. return m
  56. }
  57. func (p *PubLock) Core() *Core {
  58. return p.core
  59. }
  60. func (p *PubLock) Start() {
  61. log := logger.WithField("Mod", "PubLock")
  62. go func() {
  63. ch := p.core.EventChan()
  64. evt := ch.Receive()
  65. ticker := time.NewTicker(time.Second)
  66. defer ticker.Stop()
  67. loop:
  68. for {
  69. select {
  70. case <-ticker.C:
  71. p.lock.Lock()
  72. reqIDs := make([]types.RequestID, len(p.acquired))
  73. copy(reqIDs, p.acquired)
  74. p.lock.Unlock()
  75. if len(reqIDs) == 0 {
  76. continue
  77. }
  78. cmd := Renew{
  79. IDs: reqIDs,
  80. }
  81. if p.clster == nil {
  82. p.core.Apply(&cmd)
  83. } else {
  84. data, err := serder.ObjectToJSONEx(cmd)
  85. if err != nil {
  86. log.Warnf("cmd %T to json: %v", cmd, err)
  87. continue
  88. }
  89. _, cerr := p.clster.MasterClient().ClusterApplyLog(context.Background(), &clirpc.ClusterApplyLog{
  90. FSMID: p.core.FSM().ID(),
  91. Data: data,
  92. Timeout: time.Second * 3,
  93. })
  94. if cerr != nil {
  95. log.Errorf("apply renew: %v", cerr)
  96. }
  97. }
  98. case ret := <-evt.Chan():
  99. if ret.Err != nil {
  100. break loop
  101. }
  102. p.lock.Lock()
  103. switch e := ret.Value.(type) {
  104. case *AcquireResult:
  105. a, ok := p.acquirings[e.Raw.ID]
  106. if !ok {
  107. break
  108. }
  109. if e.Error == nil {
  110. a.Callback.SetVoid()
  111. p.acquired = append(p.acquired, e.Raw.ID)
  112. } else {
  113. a.Callback.SetError(e.Error)
  114. }
  115. delete(p.acquirings, e.Raw.ID)
  116. case *Released:
  117. r, ok := p.releasing[e.ID]
  118. if !ok {
  119. break
  120. }
  121. r.Callback.SetVoid()
  122. p.acquired = lo2.Remove(p.acquired, e.ID)
  123. delete(p.releasing, e.ID)
  124. }
  125. p.lock.Unlock()
  126. evt = ch.Receive()
  127. }
  128. }
  129. }()
  130. p.core.Start()
  131. }
  132. func (p *PubLock) Stop() {
  133. p.core.Stop()
  134. }
  135. func (p *PubLock) Acquire(req types.LockRequest, opt types.AcquireOption) (LockedRequest, *ecode.CodeError) {
  136. p.lock.Lock()
  137. cmd := Acquire{
  138. ID: types.RequestID(uuid.NewString()),
  139. Request: req,
  140. Timeout: opt.Timeout,
  141. Reason: opt.Reason,
  142. }
  143. ac := &svcAcquiring{
  144. RequestID: cmd.ID,
  145. Request: cmd.Request,
  146. Callback: future.NewSetVoid(),
  147. }
  148. p.acquirings[cmd.ID] = ac
  149. p.lock.Unlock()
  150. if p.clster == nil {
  151. p.core.Apply(&cmd)
  152. } else {
  153. data, err := serder.ObjectToJSONEx(cmd)
  154. if err != nil {
  155. return LockedRequest{}, ecode.Newf(ecode.OperationFailed, "cmd %T to json: %v", cmd, err)
  156. }
  157. _, cerr := p.clster.MasterClient().ClusterApplyLog(context.Background(), &clirpc.ClusterApplyLog{
  158. FSMID: p.core.FSM().ID(),
  159. Data: data,
  160. Timeout: opt.Timeout,
  161. })
  162. if cerr != nil {
  163. return LockedRequest{}, ecode.New(ecode.ErrorCode(cerr.Code), cerr.Message)
  164. }
  165. }
  166. err := ac.Callback.Wait(context.Background())
  167. if err != nil {
  168. return LockedRequest{}, ecode.Newf(ecode.OperationFailed, "wait acquire: %v", err)
  169. }
  170. return LockedRequest{
  171. Req: req,
  172. ReqID: ac.RequestID,
  173. }, nil
  174. }
  175. func (p *PubLock) Release(reqID types.RequestID) {
  176. log := logger.WithField("Mod", "PubLock")
  177. p.lock.Lock()
  178. cmd := Release{
  179. ID: reqID,
  180. }
  181. r := &svcReleasing{
  182. RequestID: cmd.ID,
  183. Callback: future.NewSetVoid(),
  184. }
  185. p.releasing[cmd.ID] = r
  186. p.lock.Unlock()
  187. if p.clster == nil {
  188. p.core.Apply(&cmd)
  189. } else {
  190. data, err := serder.ObjectToJSONEx(cmd)
  191. if err != nil {
  192. log.Warnf("cmd %T to json: %v", cmd, err)
  193. return
  194. }
  195. _, cerr := p.clster.MasterClient().ClusterApplyLog(context.Background(), &clirpc.ClusterApplyLog{
  196. FSMID: p.core.FSM().ID(),
  197. Data: data,
  198. Timeout: 0,
  199. })
  200. if cerr != nil {
  201. log.Errorf("apply release: %v", cerr)
  202. }
  203. }
  204. err := r.Callback.Wait(context.Background())
  205. if err != nil {
  206. log.Errorf("wait release: %v", err)
  207. } else {
  208. log.Tracef("unlock %v", reqID)
  209. }
  210. }
  211. type svcAcquiring struct {
  212. RequestID types.RequestID
  213. Request types.LockRequest
  214. Callback *future.SetVoidFuture
  215. }
  216. type svcReleasing struct {
  217. RequestID types.RequestID
  218. Callback *future.SetVoidFuture
  219. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。