github.com/adacta-ru/mattermost-server/v6@v6.0.0/app/busy.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "sync" 8 "sync/atomic" 9 "time" 10 11 "github.com/adacta-ru/mattermost-server/v6/einterfaces" 12 "github.com/adacta-ru/mattermost-server/v6/model" 13 ) 14 15 const ( 16 TIMESTAMP_FORMAT = "Mon Jan 2 15:04:05 -0700 MST 2006" 17 ) 18 19 // Busy represents the busy state of the server. A server marked busy 20 // will have non-critical services disabled. If a Cluster is provided 21 // any changes will be propagated to each node. 22 type Busy struct { 23 busy int32 // protected via atomic for fast IsBusy calls 24 mux sync.RWMutex 25 timer *time.Timer 26 expires time.Time 27 28 cluster einterfaces.ClusterInterface 29 } 30 31 // NewBusy creates a new Busy instance with optional cluster which will 32 // be notified of busy state changes. 33 func NewBusy(cluster einterfaces.ClusterInterface) *Busy { 34 return &Busy{cluster: cluster} 35 } 36 37 // IsBusy returns true if the server has been marked as busy. 38 func (b *Busy) IsBusy() bool { 39 if b == nil { 40 return false 41 } 42 return atomic.LoadInt32(&b.busy) != 0 43 } 44 45 // Set marks the server as busy for dur duration and notifies cluster nodes. 46 func (b *Busy) Set(dur time.Duration) { 47 b.mux.Lock() 48 defer b.mux.Unlock() 49 50 // minimum 1 second 51 if dur < (time.Second * 1) { 52 dur = time.Second * 1 53 } 54 55 b.setWithoutNotify(dur) 56 57 if b.cluster != nil { 58 sbs := &model.ServerBusyState{Busy: true, Expires: b.expires.Unix(), Expires_ts: b.expires.UTC().Format(TIMESTAMP_FORMAT)} 59 b.notifyServerBusyChange(sbs) 60 } 61 } 62 63 // must hold mutex 64 func (b *Busy) setWithoutNotify(dur time.Duration) { 65 b.clearWithoutNotify() 66 atomic.StoreInt32(&b.busy, 1) 67 b.expires = time.Now().Add(dur) 68 b.timer = time.AfterFunc(dur, func() { 69 b.mux.Lock() 70 b.clearWithoutNotify() 71 b.mux.Unlock() 72 }) 73 } 74 75 // ClearBusy marks the server as not busy and notifies cluster nodes. 76 func (b *Busy) Clear() { 77 b.mux.Lock() 78 defer b.mux.Unlock() 79 80 b.clearWithoutNotify() 81 82 if b.cluster != nil { 83 sbs := &model.ServerBusyState{Busy: false, Expires: time.Time{}.Unix(), Expires_ts: ""} 84 b.notifyServerBusyChange(sbs) 85 } 86 } 87 88 // must hold mutex 89 func (b *Busy) clearWithoutNotify() { 90 if b.timer != nil { 91 b.timer.Stop() // don't drain timer.C channel for AfterFunc timers. 92 } 93 b.timer = nil 94 b.expires = time.Time{} 95 atomic.StoreInt32(&b.busy, 0) 96 } 97 98 // Expires returns the expected time that the server 99 // will be marked not busy. This expiry can be extended 100 // via additional calls to SetBusy. 101 func (b *Busy) Expires() time.Time { 102 b.mux.RLock() 103 defer b.mux.RUnlock() 104 return b.expires 105 } 106 107 // notifyServerBusyChange informs all cluster members of a server busy state change. 108 func (b *Busy) notifyServerBusyChange(sbs *model.ServerBusyState) { 109 if b.cluster == nil { 110 return 111 } 112 msg := &model.ClusterMessage{ 113 Event: model.CLUSTER_EVENT_BUSY_STATE_CHANGED, 114 SendType: model.CLUSTER_SEND_RELIABLE, 115 WaitForAllToSend: true, 116 Data: sbs.ToJson(), 117 } 118 b.cluster.SendClusterMessage(msg) 119 } 120 121 // ClusterEventChanged is called when a CLUSTER_EVENT_BUSY_STATE_CHANGED is received. 122 func (b *Busy) ClusterEventChanged(sbs *model.ServerBusyState) { 123 b.mux.Lock() 124 defer b.mux.Unlock() 125 126 if sbs.Busy { 127 expires := time.Unix(sbs.Expires, 0) 128 dur := time.Until(expires) 129 if dur > 0 { 130 b.setWithoutNotify(dur) 131 } 132 } else { 133 b.clearWithoutNotify() 134 } 135 } 136 137 func (b *Busy) ToJson() string { 138 b.mux.RLock() 139 defer b.mux.RUnlock() 140 141 sbs := &model.ServerBusyState{ 142 Busy: atomic.LoadInt32(&b.busy) != 0, 143 Expires: b.expires.Unix(), 144 Expires_ts: b.expires.UTC().Format(TIMESTAMP_FORMAT), 145 } 146 return sbs.ToJson() 147 }