Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flag to skip checkKeys if role == master #985

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ jobs:
LOG_LEVEL: "info"
run: |
sleep 15
make checks
make test


Expand Down Expand Up @@ -102,6 +101,9 @@ jobs:
version: v1.64
args: "--tests=false"

- name: Run checks
run: make checks


build-stuff:
runs-on: ubuntu-latest
Expand Down
27 changes: 14 additions & 13 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"net/http"
"net/url"
"runtime"
"runtime/debug"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -85,6 +86,7 @@
BuildInfo BuildInfo
BasicAuthUsername string
BasicAuthPassword string
SkipCheckKeysForRoleMaster bool
}

// NewRedisExporter returns a new exporter of Redis metrics.
Expand Down Expand Up @@ -719,31 +721,30 @@

log.Debugf("dbCount: %d", dbCount)

e.extractInfoMetrics(ch, infoAll, dbCount)
role := e.extractInfoMetrics(ch, infoAll, dbCount)

if !e.options.ExcludeLatencyHistogramMetrics {
e.extractLatencyMetrics(ch, infoAll, c)
}

if e.options.IsCluster {
clusterClient, err := e.connectToRedisCluster()
if err != nil {
log.Errorf("Couldn't connect to redis cluster")
return err
// skip these metrics for master if SkipCheckKeysForRoleMaster is set
// (can help with reducing work load on the master node)
log.Infof("checkKeys metric collection for role: %s flag: %#v", role, e.options.SkipCheckKeysForRoleMaster)
debug.PrintStack()
if role == InstanceRoleSlave || !e.options.SkipCheckKeysForRoleMaster {
if err := e.extractCheckKeyMetrics(ch, c); err != nil {
log.Errorf("extractCheckKeyMetrics() err: %s", err)

Check warning on line 736 in exporter/exporter.go

View check run for this annotation

Codecov / codecov/patch

exporter/exporter.go#L736

Added line #L736 was not covered by tests
}
defer clusterClient.Close()

e.extractCheckKeyMetrics(ch, clusterClient)
e.extractCountKeysMetrics(ch, c)

e.extractStreamMetrics(ch, c)
} else {
e.extractCheckKeyMetrics(ch, c)
log.Infof("skipping checkKeys metrics, role: %s flag: %#v", role, e.options.SkipCheckKeysForRoleMaster)
}

e.extractSlowLogMetrics(ch, c)

e.extractStreamMetrics(ch, c)

e.extractCountKeysMetrics(ch, c)

e.extractKeyGroupMetrics(ch, c, dbCount)

if strings.Contains(infoAll, "# Sentinel") {
Expand Down
69 changes: 33 additions & 36 deletions exporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,25 @@ import (
log "github.com/sirupsen/logrus"
)

var (
keys []string
keysExpiring []string
listKeys []string
singleStringKey string

const (
dbNumStr = "11"
altDBNumStr = "12"
invalidDBNumStr = "16"
dbNumStrFull = fmt.Sprintf("db%s", dbNumStr)
)

const (
TestKeysSetName = "test-set"
TestKeysStreamName = "test-stream"
TestKeysHllName = "test-hll"
var (
keys []string
keysExpiring []string
listKeys []string

dbNumStrFull = fmt.Sprintf("db%s", dbNumStr)
)

var (
TestKeyNameSingleString = "" // initialized with a timestamp at runtime
TestKeyNameSet = "test-set"
TestKeyNameStream = "test-stream"
TestKeyNameHll = "test-hll"
)

func getTestExporter() *Exporter {
Expand Down Expand Up @@ -95,43 +98,43 @@ func setupKeys(t *testing.T, c redis.Conn, dbNumStr string) error {
}
}

if _, err := c.Do("PFADD", TestKeysHllName, "val1"); err != nil {
if _, err := c.Do("PFADD", TestKeyNameHll, "val1"); err != nil {
t.Errorf("PFADD err: %s", err)
return err
}
if _, err := c.Do("PFADD", TestKeysHllName, "val22"); err != nil {
if _, err := c.Do("PFADD", TestKeyNameHll, "val22"); err != nil {
t.Errorf("PFADD err: %s", err)
return err
}
if _, err := c.Do("PFADD", TestKeysHllName, "val333"); err != nil {
if _, err := c.Do("PFADD", TestKeyNameHll, "val333"); err != nil {
t.Errorf("PFADD err: %s", err)
return err
}

if _, err := c.Do("SADD", TestKeysSetName, "test-val-1"); err != nil {
if _, err := c.Do("SADD", TestKeyNameSet, "test-val-1"); err != nil {
t.Errorf("SADD err: %s", err)
return err
}
if _, err := c.Do("SADD", TestKeysSetName, "test-val-2"); err != nil {
if _, err := c.Do("SADD", TestKeyNameSet, "test-val-2"); err != nil {
t.Errorf("SADD err: %s", err)
return err
}

if _, err := c.Do("SET", singleStringKey, "this-is-a-string"); err != nil {
if _, err := c.Do("SET", TestKeyNameSingleString, "this-is-a-string"); err != nil {
t.Errorf("PFADD err: %s", err)
return err
}

// Create test streams
c.Do("XGROUP", "CREATE", TestKeysStreamName, "test_group_1", "$", "MKSTREAM")
c.Do("XGROUP", "CREATE", TestKeysStreamName, "test_group_2", "$", "MKSTREAM")
c.Do("XADD", TestKeysStreamName, TestStreamTimestamps[0], "field_1", "str_1")
c.Do("XADD", TestKeysStreamName, TestStreamTimestamps[1], "field_2", "str_2")
c.Do("XGROUP", "CREATE", TestKeyNameStream, "test_group_1", "$", "MKSTREAM")
c.Do("XGROUP", "CREATE", TestKeyNameStream, "test_group_2", "$", "MKSTREAM")
c.Do("XADD", TestKeyNameStream, TestStreamTimestamps[0], "field_1", "str_1")
c.Do("XADD", TestKeyNameStream, TestStreamTimestamps[1], "field_2", "str_2")

// Process messages to assign Consumers to their groups
c.Do("XREADGROUP", "GROUP", "test_group_1", "test_consumer_1", "COUNT", "1", "STREAMS", TestKeysStreamName, ">")
c.Do("XREADGROUP", "GROUP", "test_group_1", "test_consumer_2", "COUNT", "1", "STREAMS", TestKeysStreamName, ">")
c.Do("XREADGROUP", "GROUP", "test_group_2", "test_consumer_1", "COUNT", "1", "STREAMS", TestKeysStreamName, "0")
c.Do("XREADGROUP", "GROUP", "test_group_1", "test_consumer_1", "COUNT", "1", "STREAMS", TestKeyNameStream, ">")
c.Do("XREADGROUP", "GROUP", "test_group_1", "test_consumer_2", "COUNT", "1", "STREAMS", TestKeyNameStream, ">")
c.Do("XREADGROUP", "GROUP", "test_group_2", "test_consumer_1", "COUNT", "1", "STREAMS", TestKeyNameStream, "0")

time.Sleep(time.Millisecond * 100)
return nil
Expand All @@ -155,10 +158,10 @@ func deleteKeys(c redis.Conn, dbNumStr string) {
c.Do("DEL", key)
}

c.Do("DEL", TestKeysHllName)
c.Do("DEL", TestKeysSetName)
c.Do("DEL", TestKeysStreamName)
c.Do("DEL", singleStringKey)
c.Do("DEL", TestKeyNameHll)
c.Do("DEL", TestKeyNameSet)
c.Do("DEL", TestKeyNameStream)
c.Do("DEL", TestKeyNameSingleString)
}

func setupDBKeys(t *testing.T, uri string) {
Expand Down Expand Up @@ -363,15 +366,9 @@ func TestKeysReset(t *testing.T) {
setupDBKeys(t, os.Getenv("TEST_REDIS_URI"))
defer deleteKeysFromDB(t, os.Getenv("TEST_REDIS_URI"))

chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()

body := downloadURL(t, ts.URL+"/metrics")
if !strings.Contains(body, keys[0]) {
t.Errorf("Did not found key %q\n%s", keys[0], body)
t.Errorf("Did not find key %q\n%s", keys[0], body)
}

deleteKeysFromDB(t, os.Getenv("TEST_REDIS_URI"))
Expand Down Expand Up @@ -468,7 +465,7 @@ func init() {
keys = append(keys, fmt.Sprintf("key_%s_%d", n, testTimestamp))
}

singleStringKey = fmt.Sprintf("key_string_%d", testTimestamp)
TestKeyNameSingleString = fmt.Sprintf("key_string_%d", testTimestamp)

listKeys = append(listKeys, "beatles_list")

Expand Down
2 changes: 1 addition & 1 deletion exporter/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestHTTPScrapeMetricsEndpoints(t *testing.T) {
defer deleteKeysFromDB(t, os.Getenv("TEST_PWD_REDIS_URI"))

csk := dbNumStrFull + "=" + url.QueryEscape(keys[0]) // check-single-keys
css := dbNumStrFull + "=" + TestKeysStreamName // check-single-streams
css := dbNumStrFull + "=" + TestKeyNameStream // check-single-streams
cntk := dbNumStrFull + "=" + keys[0] + "*" // count-keys

u, err := url.Parse(os.Getenv("TEST_REDIS_URI"))
Expand Down
16 changes: 13 additions & 3 deletions exporter/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ var reMasterDirect = regexp.MustCompile(`^(master(_[0-9]+)?_(last_io_seconds_ago
slave0:ip=10.254.11.1,port=6379,state=online,offset=1751844676,lag=0
slave1:ip=10.254.11.2,port=6379,state=online,offset=1751844222,lag=0
*/

var reSlave = regexp.MustCompile(`^slave\d+`)

const (
InstanceRoleSlave = "slave"
)

func extractVal(s string) (val float64, err error) {
split := strings.Split(s, "=")
if len(split) != 2 {
Expand All @@ -60,7 +65,8 @@ func extractPercentileVal(s string) (percentile float64, val float64, err error)
return
}

func (e *Exporter) extractInfoMetrics(ch chan<- prometheus.Metric, info string, dbCount int) {
// returns the role of the instance we're scraping (master or slave)
func (e *Exporter) extractInfoMetrics(ch chan<- prometheus.Metric, info string, dbCount int) string {
keyValues := map[string]string{}
handledDBs := map[string]bool{}
cmdCount := map[string]uint64{}
Expand Down Expand Up @@ -161,8 +167,10 @@ func (e *Exporter) extractInfoMetrics(ch chan<- prometheus.Metric, info string,
}
}

instanceRole := keyValues["role"]

e.registerConstMetricGauge(ch, "instance_info", 1,
keyValues["role"],
instanceRole,
keyValues["redis_version"],
keyValues["redis_build_id"],
keyValues["redis_mode"],
Expand All @@ -174,12 +182,14 @@ func (e *Exporter) extractInfoMetrics(ch chan<- prometheus.Metric, info string,
keyValues["master_replid"],
)

if keyValues["role"] == "slave" {
if instanceRole == InstanceRoleSlave {
e.registerConstMetricGauge(ch, "slave_info", 1,
keyValues["master_host"],
keyValues["master_port"],
keyValues["slave_read_only"])
}

return instanceRole
}

func (e *Exporter) generateCommandLatencySummaries(ch chan<- prometheus.Metric, cmdLatencyMap map[string]map[float64]float64, cmdCount map[string]uint64, cmdSum map[string]float64) {
Expand Down
60 changes: 48 additions & 12 deletions exporter/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,6 @@ func TestClusterMaster(t *testing.T) {
ts := httptest.NewServer(e)
defer ts.Close()

chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()

body := downloadURL(t, ts.URL+"/metrics")
log.Debugf("master - body: %s", body)
for _, want := range []string{
Expand All @@ -159,6 +153,54 @@ func TestClusterMaster(t *testing.T) {
}
}

func TestClusterSkipCheckKeysIfMaster(t *testing.T) {
uriMaster := os.Getenv("TEST_REDIS_CLUSTER_MASTER_URI")
uriSlave := os.Getenv("TEST_REDIS_CLUSTER_SLAVE_URI")
if uriMaster == "" || uriSlave == "" {
t.Skipf("TEST_REDIS_CLUSTER_MASTER_URI or slave not set - skipping")
}

setupDBKeysCluster(t, uriMaster)
defer deleteKeysFromDBCluster(uriMaster)

for _, uri := range []string{uriMaster, uriSlave} {
for _, skip := range []bool{true, false} {
e, _ := NewRedisExporter(
uri,
Options{Namespace: "test",
Registry: prometheus.NewRegistry(),
CheckKeys: TestKeyNameHll,
SkipCheckKeysForRoleMaster: skip,
IsCluster: true,
})
ts := httptest.NewServer(e)

body := downloadURL(t, ts.URL+"/metrics")

expectedMetricPresent := true
if skip && uri == uriMaster {
expectedMetricPresent = false
}
t.Logf("skip: %#v uri: %s uri == uriMaster: %#v", skip, uri, uri == uriMaster)
t.Logf("expectedMetricPresent: %#v", expectedMetricPresent)

want := `test_key_size{db="db0",key="test-hll"} 3`

if expectedMetricPresent {
if !strings.Contains(body, want) {
t.Fatalf("expectedMetricPresent but missing. metric: %s body: %s\n", want, body)
}
} else {
if strings.Contains(body, want) {
t.Fatalf("should have skipped it but found it, body:\n%s", body)
}
}

ts.Close()
}
}
}

func TestClusterSlave(t *testing.T) {
if os.Getenv("TEST_REDIS_CLUSTER_SLAVE_URI") == "" {
t.Skipf("TEST_REDIS_CLUSTER_SLAVE_URI not set - skipping")
Expand All @@ -169,12 +211,6 @@ func TestClusterSlave(t *testing.T) {
ts := httptest.NewServer(e)
defer ts.Close()

chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()

body := downloadURL(t, ts.URL+"/metrics")
log.Debugf("slave - body: %s", body)
for _, want := range []string{
Expand Down
21 changes: 16 additions & 5 deletions exporter/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,28 @@
return info, err
}

func (e *Exporter) extractCheckKeyMetrics(ch chan<- prometheus.Metric, c redis.Conn) {
func (e *Exporter) extractCheckKeyMetrics(ch chan<- prometheus.Metric, redisClient redis.Conn) error {
c := redisClient

if e.options.IsCluster {
cc, err := e.connectToRedisCluster()
if err != nil {
return fmt.Errorf("Couldn't connect to redis cluster, err: %s", err)
}

Check warning on line 153 in exporter/keys.go

View check run for this annotation

Codecov / codecov/patch

exporter/keys.go#L152-L153

Added lines #L152 - L153 were not covered by tests
defer cc.Close()

c = cc
}

keys, err := parseKeyArg(e.options.CheckKeys)
if err != nil {
log.Errorf("Couldn't parse check-keys: %#v", err)
return
return fmt.Errorf("Couldn't parse check-keys: %w", err)

Check warning on line 161 in exporter/keys.go

View check run for this annotation

Codecov / codecov/patch

exporter/keys.go#L161

Added line #L161 was not covered by tests
}
log.Debugf("keys: %#v", keys)

singleKeys, err := parseKeyArg(e.options.CheckSingleKeys)
if err != nil {
log.Errorf("Couldn't parse check-single-keys: %#v", err)
return
return fmt.Errorf("Couldn't parse check-single-keys: %w", err)

Check warning on line 167 in exporter/keys.go

View check run for this annotation

Codecov / codecov/patch

exporter/keys.go#L167

Added line #L167 was not covered by tests
}
log.Debugf("e.singleKeys: %#v", singleKeys)

Expand Down Expand Up @@ -210,6 +220,7 @@
log.Error(err)
}
}
return nil
}

func (e *Exporter) extractCountKeysMetrics(ch chan<- prometheus.Metric, c redis.Conn) {
Expand Down
Loading
Loading