mirror of
https://github.com/elyby/chrly.git
synced 2024-11-26 16:51:59 +05:30
Added new stats reporter to check suitable redis pool size
This commit is contained in:
parent
5dbe6af1d0
commit
aabf54e318
@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
Mojang API base addresses.
|
||||
- New health checker, that ensures that response for textures provider from Mojang's API is valid.
|
||||
- `dev` Docker images now have the `--cpuprofile` flag, which allows you to run the program with CPU profiling.
|
||||
- New StatsD metrics:
|
||||
- Gauges:
|
||||
- `ely.skinsystem.{hostname}.app.redis.pool.available`
|
||||
|
||||
### Fixed
|
||||
- Handle the case when there is no textures property in Mojang's response.
|
||||
|
@ -243,6 +243,10 @@ func (db *Redis) Ping() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Redis) Avail() int {
|
||||
return db.pool.Avail()
|
||||
}
|
||||
|
||||
func buildUsernameKey(username string) string {
|
||||
return "username:" + strings.ToLower(username)
|
||||
}
|
||||
|
@ -377,3 +377,8 @@ func (suite *redisTestSuite) TestPing() {
|
||||
err := suite.Redis.Ping()
|
||||
suite.Require().Nil(err)
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestAvail() {
|
||||
avail := suite.Redis.Avail()
|
||||
suite.Require().True(avail > 0)
|
||||
}
|
||||
|
9
di/db.go
9
di/db.go
@ -1,8 +1,10 @@
|
||||
package di
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/goava/di"
|
||||
"github.com/spf13/viper"
|
||||
@ -23,6 +25,7 @@ var db = di.Options(
|
||||
di.Provide(newRedis,
|
||||
di.As(new(http.SkinsRepository)),
|
||||
di.As(new(mojangtextures.UUIDsStorage)),
|
||||
di.As(new(es.RedisPoolCheckable)),
|
||||
),
|
||||
di.Provide(newFSFactory,
|
||||
di.As(new(http.CapesRepository)),
|
||||
@ -43,6 +46,12 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := container.Provide(func() es.ReporterFunc {
|
||||
return es.AvailableRedisPoolSizeReporter(conn, time.Second, context.Background())
|
||||
}, di.As(new(es.Reporter))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := container.Provide(func() *namedHealthChecker {
|
||||
return &namedHealthChecker{
|
||||
Name: "redis",
|
||||
|
@ -74,6 +74,11 @@ func newHandlerFactory(
|
||||
mount(router, "/api", apiRouter)
|
||||
}
|
||||
|
||||
err := container.Invoke(enableReporters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Resolve health checkers last, because all the services required by the application
|
||||
// must first be initialized and each of them can publish its own checkers
|
||||
var healthCheckers []*namedHealthChecker
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/mono83/slf/wd"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/elyby/chrly/eventsubscribers"
|
||||
"github.com/elyby/chrly/version"
|
||||
)
|
||||
|
||||
@ -95,3 +96,9 @@ func newStatsReporter(config *viper.Viper) (slf.StatsReporter, error) {
|
||||
|
||||
return wd.Custom("", "", dispatcher), nil
|
||||
}
|
||||
|
||||
func enableReporters(reporter slf.StatsReporter, factories []eventsubscribers.Reporter) {
|
||||
for _, factory := range factories {
|
||||
factory.Enable(reporter)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eventsubscribers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -19,6 +20,17 @@ type StatsReporter struct {
|
||||
timersMutex sync.Mutex
|
||||
}
|
||||
|
||||
type Reporter interface {
|
||||
Enable(reporter slf.StatsReporter)
|
||||
}
|
||||
|
||||
type ReporterFunc func(reporter slf.StatsReporter)
|
||||
|
||||
func (f ReporterFunc) Enable(reporter slf.StatsReporter) {
|
||||
f(reporter)
|
||||
}
|
||||
|
||||
// TODO: rework all reporters in the same style as AvailableRedisPoolSizeReporter
|
||||
func (s *StatsReporter) ConfigureWithDispatcher(d Subscriber) {
|
||||
s.timersMap = make(map[string]time.Time)
|
||||
|
||||
@ -175,3 +187,24 @@ func (s *StatsReporter) finalizeTimeRecording(timeKey string, statName string) {
|
||||
|
||||
s.RecordTimer(statName, time.Since(startedAt))
|
||||
}
|
||||
|
||||
type RedisPoolCheckable interface {
|
||||
Avail() int
|
||||
}
|
||||
|
||||
func AvailableRedisPoolSizeReporter(pool RedisPoolCheckable, d time.Duration, stop context.Context) ReporterFunc {
|
||||
return func(reporter slf.StatsReporter) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(d)
|
||||
for {
|
||||
select {
|
||||
case <-stop.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
reporter.UpdateGauge("redis.pool.available", int64(pool.Avail()))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eventsubscribers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@ -392,3 +393,30 @@ func TestStatsReporter(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type redisPoolCheckableMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (r *redisPoolCheckableMock) Avail() int {
|
||||
return r.Called().Int(0)
|
||||
}
|
||||
|
||||
func TestAvailableRedisPoolSizeReporter(t *testing.T) {
|
||||
poolMock := &redisPoolCheckableMock{}
|
||||
poolMock.On("Avail").Return(5).Times(3)
|
||||
reporterMock := &StatsReporterMock{}
|
||||
reporterMock.On("UpdateGauge", "redis.pool.available", int64(5)).Times(3)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
creator := AvailableRedisPoolSizeReporter(poolMock, 10*time.Millisecond, ctx)
|
||||
creator(reporterMock)
|
||||
|
||||
time.Sleep(35 * time.Millisecond)
|
||||
|
||||
cancel()
|
||||
|
||||
poolMock.AssertExpectations(t)
|
||||
reporterMock.AssertExpectations(t)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user