Skip to content

Commit

Permalink
Merge pull request #8 from app-sre/rds_conn
Browse files Browse the repository at this point in the history
Add RDS max_connections metric
  • Loading branch information
jfchevrette authored Oct 16, 2019
2 parents 6e74717 + 0ed7fd7 commit ebf1add
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 44 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/app-sre/aws-resource-exporter
go 1.12

require (
github.com/aws/aws-sdk-go v1.15.77
github.com/aws/aws-sdk-go v1.25.9
github.com/prometheus/client_golang v0.9.3
github.com/prometheus/common v0.4.1
github.com/prometheus/procfs v0.0.2 // indirect
Expand Down
9 changes: 5 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5Vpd
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aws/aws-sdk-go v1.15.77 h1:qlut2MDI5mRKllPC6grO5n9M8UhPQg1TIA9cYAkC/gc=
github.com/aws/aws-sdk-go v1.15.77/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.25.9 h1:WtVzerf5wSgPwlTTwl+ktCq/0GCS5MI9ZlLIcjsTr+Q=
github.com/aws/aws-sdk-go v1.25.9/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
Expand All @@ -22,13 +22,14 @@ github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80n
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down
114 changes: 75 additions & 39 deletions rds.go
Original file line number Diff line number Diff line change
@@ -1,56 +1,98 @@
package main

import (
"sync"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)

// DBMaxConnections is a hardcoded map of instance types and DB Parameter Group names
// This is a dump workaround created because by default the DB Parameter Group `max_connections` is a function
// that is hard to parse and process in code and it contains a variable whose value is unknown to us (DBInstanceClassMemory)
// AWS has no means to return the actual `max_connections` value.
var DBMaxConnections = map[string]map[string]int64{
"db.t2.small": map[string]int64{
"default.mysql5.7": 150,
},
"db.m5.2xlarge": map[string]int64{
"default.postgres10": 3429,
},
"db.m5.large": map[string]int64{
"default.postgres10": 823,
},
}

// RDSExporter defines an instance of the RDS Exporter
type RDSExporter struct {
sess *session.Session
AllocatedStorage *prometheus.Desc
DBInstanceClass *prometheus.Desc
DBInstanceStatus *prometheus.Desc
EngineVersion *prometheus.Desc
sess *session.Session
AllocatedStorage *prometheus.Desc
DBInstanceClass *prometheus.Desc
DBInstanceStatus *prometheus.Desc
EngineVersion *prometheus.Desc
MaxConnections *prometheus.Desc
MaxConnectionsMappingErrors *prometheus.Desc

MaxConnectionsMappingErrorsValue float64

mutex *sync.Mutex
}

// NewRDSExporter creates a new RDSExporter instance
func NewRDSExporter(sess *session.Session) *RDSExporter {
log.Info("[RDS] Initializing RDS exporter")
return &RDSExporter{
sess: sess,
sess: sess,
mutex: &sync.Mutex{},
AllocatedStorage: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "allocatedstorage"),
prometheus.BuildFQName(namespace, "", "rds_allocatedstorage"),
"The amount of allocated storage in bytes.",
[]string{"aws_region", "dbinstance_identifier"},
nil,
),
DBInstanceClass: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "dbinstanceclass"),
prometheus.BuildFQName(namespace, "", "rds_dbinstanceclass"),
"The DB instance class (type).",
[]string{"aws_region", "dbinstance_identifier", "instance_class"},
nil,
),
DBInstanceStatus: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "dbinstancestatus"),
prometheus.BuildFQName(namespace, "", "rds_dbinstancestatus"),
"The instance status.",
[]string{"aws_region", "dbinstance_identifier", "instance_status"},
nil,
),
EngineVersion: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "engineversion"),
prometheus.BuildFQName(namespace, "", "rds_engineversion"),
"The DB engine type and version.",
[]string{"aws_region", "dbinstance_identifier", "engine", "engine_version"},
nil,
),
MaxConnections: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "rds_maxconnections"),
"The DB's max_connections value",
[]string{"aws_region", "dbinstance_identifier"},
nil,
),
MaxConnectionsMappingErrors: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "rds_maxconnections_errors"),
"Indicates no mapping found for instance/parameter group.",
[]string{},
nil,
),
}
}

// Describe is used by the Prometheus client to return a description of the metrics
func (e *RDSExporter) Describe(ch chan<- *prometheus.Desc) {
ch <- e.AllocatedStorage
ch <- e.DBInstanceStatus
ch <- e.EngineVersion
}

// Collect is used by the Prometheus client to collect and return the metrics values
func (e *RDSExporter) Collect(ch chan<- prometheus.Metric) {
svc := rds.New(e.sess)
input := &rds.DescribeDBInstancesInput{}
Expand All @@ -73,41 +115,35 @@ func (e *RDSExporter) Collect(ch chan<- prometheus.Metric) {
}
}

parameterGroups := make(map[string][]*rds.Parameter)
for _, instance := range instances {
for _, dbpg := range instance.DBParameterGroups {
if *dbpg.ParameterApplyStatus == "in-sync" {
if _, ok := parameterGroups[*dbpg.DBParameterGroupName]; ok {
log.Debugln("ParameterGroup", *dbpg.DBParameterGroupName, "exists in cache.")
continue
}

log.Debugln("Fetching parameters for group", *dbpg.DBParameterGroupName)
input := &rds.DescribeDBParametersInput{
DBParameterGroupName: dbpg.DBParameterGroupName,
}
var parameters []*rds.Parameter
for {
exporterMetrics.IncrementRequests()
result, err := svc.DescribeDBParameters(input)
if err != nil {
log.Errorf("[RDS] Call to DescribeDBInstances failed in region %s: %s", *e.sess.Config.Region, err)
exporterMetrics.IncrementErrors()
return
}
parameters = append(parameters, result.Parameters...)
input.Marker = result.Marker
if result.Marker == nil {
break
}
}
parameterGroups[*dbpg.DBParameterGroupName] = parameters
var maxConnections int64
if val, ok := DBMaxConnections[*instance.DBInstanceClass]; ok {
if val, ok := val[*instance.DBParameterGroups[0].DBParameterGroupName]; ok {
log.Debugf("[RDS] Found mapping for instance type %s group %s value %d",
*instance.DBInstanceClass,
*instance.DBParameterGroups[0].DBParameterGroupName,
val)
maxConnections = val
} else {
log.Errorf("[RDS] No DB max_connections mapping exists for instance type %s parameter group %s",
*instance.DBInstanceClass,
*instance.DBParameterGroups[0].DBParameterGroupName)
e.mutex.Lock()
e.MaxConnectionsMappingErrorsValue++
e.mutex.Unlock()
}
} else {
log.Errorf("[RDS] No DB max_connections mapping exists for instance type %s",
*instance.DBInstanceClass)
e.mutex.Lock()
e.MaxConnectionsMappingErrorsValue++
e.mutex.Unlock()
}

ch <- prometheus.MustNewConstMetric(e.MaxConnections, prometheus.GaugeValue, float64(maxConnections), *e.sess.Config.Region, *instance.DBInstanceIdentifier)
ch <- prometheus.MustNewConstMetric(e.AllocatedStorage, prometheus.GaugeValue, float64(*instance.AllocatedStorage*1024*1024*1024), *e.sess.Config.Region, *instance.DBInstanceIdentifier)
ch <- prometheus.MustNewConstMetric(e.DBInstanceStatus, prometheus.GaugeValue, 1, *e.sess.Config.Region, *instance.DBInstanceIdentifier, *instance.DBInstanceStatus)
ch <- prometheus.MustNewConstMetric(e.EngineVersion, prometheus.GaugeValue, 1, *e.sess.Config.Region, *instance.DBInstanceIdentifier, *instance.Engine, *instance.EngineVersion)
ch <- prometheus.MustNewConstMetric(e.DBInstanceClass, prometheus.GaugeValue, 1, *e.sess.Config.Region, *instance.DBInstanceIdentifier, *instance.DBInstanceClass)
}
ch <- prometheus.MustNewConstMetric(e.MaxConnectionsMappingErrors, prometheus.CounterValue, e.MaxConnectionsMappingErrorsValue)
}

0 comments on commit ebf1add

Please sign in to comment.