eintopf.info@v0.13.16/service/status/status.go (about)

     1  // Copyright (C) 2022 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package status
    17  
    18  import (
    19  	"sync"
    20  	"time"
    21  )
    22  
    23  // Status contains information about the status of a server.
    24  type Status struct {
    25  	Uptime          time.Duration   `json:"uptime"`
    26  	Requests        uint            `json:"requests"`
    27  	RequestDuration RequestDuration `json:"requestDuration"`
    28  }
    29  
    30  // RequestDuration tracks the duration of requests in different metrics.
    31  type RequestDuration struct {
    32  	Min time.Duration `json:"min"`
    33  	Max time.Duration `json:"max"`
    34  	Avg time.Duration `json:"avg"`
    35  }
    36  
    37  func addDurations(r RequestDuration, durations []time.Duration) RequestDuration {
    38  	if len(durations) == 0 {
    39  		return r
    40  	}
    41  	avg := time.Duration(0)
    42  
    43  	for _, d := range durations {
    44  		if d < r.Min {
    45  			r.Min = d
    46  		}
    47  		if d > r.Max {
    48  			r.Max = d
    49  		}
    50  		avg += d
    51  	}
    52  
    53  	avg /= time.Duration(len(durations))
    54  	if r.Avg == 0 {
    55  		r.Avg = avg
    56  	} else {
    57  		r.Avg = (r.Avg + avg) / 2
    58  	}
    59  
    60  	return r
    61  }
    62  
    63  // Service defines a a servies, that collects and provides status information
    64  // about a server.
    65  //
    66  // -go:generate go run github.com/petergtz/pegomock/pegomock generate eintopf.info/service/status Service --output=../../internal/mock/status_service.go --package=mock --mock-name=StatusService
    67  type Service interface {
    68  	// Status returns the current status of the server.
    69  	Status() Status
    70  
    71  	// RequestServed tells the service, that a request was served, with a given
    72  	// request duration.
    73  	RequestServed(duration time.Duration)
    74  }
    75  
    76  // NewService returns a new status service.
    77  func NewService() Service {
    78  	return &service{
    79  		started:          time.Now(),
    80  		requests:         0,
    81  		requestDurations: make([]time.Duration, 0),
    82  		m:                &sync.Mutex{},
    83  		requestDuration: RequestDuration{
    84  			Min: 99999999,
    85  		},
    86  	}
    87  }
    88  
    89  type service struct {
    90  	started time.Time
    91  
    92  	requests         uint
    93  	requestDurations []time.Duration
    94  	m                *sync.Mutex // protects request/response information
    95  
    96  	requestDuration RequestDuration
    97  }
    98  
    99  func (s *service) Status() Status {
   100  	return Status{
   101  		Uptime:          time.Now().Sub(s.started),
   102  		Requests:        s.requests,
   103  		RequestDuration: addDurations(s.requestDuration, s.requestDurations),
   104  	}
   105  }
   106  
   107  func (s *service) RequestServed(duration time.Duration) {
   108  	s.m.Lock()
   109  	defer s.m.Unlock()
   110  
   111  	if len(s.requestDurations) > 1000 {
   112  		s.requestDuration = addDurations(s.requestDuration, s.requestDurations)
   113  		s.requestDurations = make([]time.Duration, 0)
   114  	}
   115  	s.requests += 1
   116  	s.requestDurations = append(s.requestDurations, duration)
   117  }