github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/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/mattermost/mattermost-server/v5/einterfaces"
    12  	"github.com/mattermost/mattermost-server/v5/model"
    13  )
    14  
    15  const (
    16  	TimestampFormat = "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(TimestampFormat)}
    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(TimestampFormat),
   145  	}
   146  	return sbs.ToJson()
   147  }