Skip to content

Commit

Permalink
feat(metric): Add rds_instance_age_seconds representing age of the in…
Browse files Browse the repository at this point in the history
…stance
  • Loading branch information
vmercierfr committed Nov 27, 2023
1 parent 8a6e65e commit 5559980
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ It collect key metrics about:
| rds_exporter_errors_total | | Total number of errors encountered by the exporter |
| rds_free_storage_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Free storage on the instance |
| rds_freeable_memory_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Amount of available random access memory. For MariaDB, MySQL, Oracle, and PostgreSQL DB instances, this metric reports the value of the MemAvailable field of /proc/meminfo |
| rds_instance_age_seconds | `aws_account_id`, `aws_region`, `dbidentifier` | Instance age |
| rds_instance_info | `aws_account_id`, `aws_region`, `dbi_resource_id`, `dbidentifier`, `deletion_protection`, `engine`, `engine_version`, `instance_class`, `multi_az`, `performance_insights_enabled`, `pending_maintenance`, `pending_modified_values`, `role`, `source_dbidentifier`, `storage_type` | RDS instance information |
| rds_instance_log_files_size_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Total of log files on the instance |
| rds_instance_max_iops_average | `aws_account_id`, `aws_region`, `dbidentifier` | Maximum IOPS of underlying EC2 instance |
Expand All @@ -64,10 +65,10 @@ It collect key metrics about:
| rds_quota_total_storage_bytes | `aws_account_id`, `aws_region` | Maximum total storage for all DB instances |
| rds_read_iops_average | `aws_account_id`, `aws_region`, `dbidentifier` | Average number of disk read I/O operations per second |
| rds_read_throughput_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Average number of bytes read from disk per second |
| rds_transaction_logs_disk_usage_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Disk space used by transaction logs (only on PostgreSQL) |
| rds_replica_lag_seconds | `aws_account_id`, `aws_region`, `dbidentifier` | For read replica configurations, the amount of time a read replica DB instance lags behind the source DB instance. Applies to MariaDB, Microsoft SQL Server, MySQL, Oracle, and PostgreSQL read replicas |
| rds_replication_slot_disk_usage_average | `aws_account_id`, `aws_region`, `dbidentifier` | Disk space used by replication slot files. Applies to PostgreSQL |
| rds_swap_usage_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Amount of swap space used on the DB instance. This metric is not available for SQL Server |
| rds_transaction_logs_disk_usage_bytes | `aws_account_id`, `aws_region`, `dbidentifier` | Disk space used by transaction logs (only on PostgreSQL) |
| rds_usage_allocated_storage_average | `aws_account_id`, `aws_region` | Total storage used by AWS RDS instances |
| rds_usage_db_instances_average | `aws_account_id`, `aws_region` | AWS RDS instance count |
| rds_usage_manual_snapshots_average | `aws_account_id`, `aws_region` | Manual snapshots count |
Expand Down
9 changes: 9 additions & 0 deletions internal/app/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type rdsCollector struct {
exporterBuildInformation *prometheus.Desc
transactionLogsDiskUsage *prometheus.Desc
certificateValidTill *prometheus.Desc
age *prometheus.Desc
}

func NewCollector(logger slog.Logger, collectorConfiguration Configuration, awsAccountID string, awsRegion string, rdsClient rdsClient, ec2Client EC2Client, cloudWatchClient cloudWatchClient, servicequotasClient servicequotasClient) *rdsCollector {
Expand Down Expand Up @@ -129,6 +130,10 @@ func NewCollector(logger slog.Logger, collectorConfiguration Configuration, awsA
"RDS instance information",
[]string{"aws_account_id", "aws_region", "dbidentifier", "dbi_resource_id", "instance_class", "engine", "engine_version", "storage_type", "multi_az", "deletion_protection", "role", "source_dbidentifier", "pending_modified_values", "pending_maintenance", "performance_insights_enabled", "ca_certificate_identifier"}, nil,
),
age: prometheus.NewDesc("rds_instance_age_seconds",
"RDS age",
[]string{"aws_account_id", "aws_region", "dbidentifier"}, nil,
),
maxAllocatedStorage: prometheus.NewDesc("rds_max_allocated_storage_bytes",
"Upper limit in gibibytes to which Amazon RDS can automatically scale the storage of the DB instance",
[]string{"aws_account_id", "aws_region", "dbidentifier"}, nil,
Expand Down Expand Up @@ -454,6 +459,10 @@ func (c *rdsCollector) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(c.backupRetentionPeriod, prometheus.GaugeValue, float64(instance.BackupRetentionPeriod), c.awsAccountID, c.awsRegion, dbidentifier)
ch <- prometheus.MustNewConstMetric(c.certificateValidTill, prometheus.GaugeValue, float64(instance.CertificateValidTill.Unix()), c.awsAccountID, c.awsRegion, dbidentifier)

if instance.Age != nil {
ch <- prometheus.MustNewConstMetric(c.age, prometheus.GaugeValue, *instance.Age, c.awsAccountID, c.awsRegion, dbidentifier)
}

if instance.LogFilesSize != nil {
ch <- prometheus.MustNewConstMetric(c.logFilesSize, prometheus.GaugeValue, float64(*instance.LogFilesSize), c.awsAccountID, c.awsRegion, dbidentifier)
}
Expand Down
9 changes: 9 additions & 0 deletions internal/app/rds/rds.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type RdsInstanceMetrics struct {
SourceDBInstanceIdentifier string
CACertificateIdentifier string
CertificateValidTill time.Time
Age *float64
}

const (
Expand Down Expand Up @@ -254,6 +255,13 @@ func (r *RDSFetcher) computeInstanceMetrics(dbInstance aws_rds_types.DBInstance,

role, sourceDBInstanceIdentifier := getRoleInCluster(&dbInstance)

var age *float64

if dbInstance.InstanceCreateTime != nil {
diff := time.Since(*dbInstance.InstanceCreateTime).Seconds()
age = &diff
}

metrics := RdsInstanceMetrics{
AllocatedStorage: converter.GigaBytesToBytes(dbInstance.AllocatedStorage),
BackupRetentionPeriod: converter.DaystoSeconds(dbInstance.BackupRetentionPeriod),
Expand All @@ -277,6 +285,7 @@ func (r *RDSFetcher) computeInstanceMetrics(dbInstance aws_rds_types.DBInstance,
StorageType: *dbInstance.StorageType,
CACertificateIdentifier: *dbInstance.CACertificateIdentifier,
CertificateValidTill: *dbInstance.CertificateDetails.ValidTill,
Age: age,
}

return metrics, nil
Expand Down
20 changes: 20 additions & 0 deletions internal/app/rds/rds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func newRdsCertificateDetails() *aws_rds_types.CertificateDetails {

func newRdsInstance() *aws_rds_types.DBInstance {
DBInstanceIdentifier := randomString(10)
now := time.Now()

return &aws_rds_types.DBInstance{
AllocatedStorage: 5,
Expand All @@ -88,6 +89,7 @@ func newRdsInstance() *aws_rds_types.DBInstance {
StorageType: aws.String("gp3"),
CACertificateIdentifier: aws.String("rds-ca-2019"),
CertificateDetails: newRdsCertificateDetails(),
InstanceCreateTime: &now,
}
}

Expand Down Expand Up @@ -353,3 +355,21 @@ func TestPendingModificationDueToUnappliedParameterGroup(t *testing.T) {
require.NoError(t, err, "GetInstancesMetrics must succeed")
assert.Equal(t, true, metrics.Instances[*rdsInstance.DBInstanceIdentifier].PendingModifiedValues, "Should have pending modification")
}

func TestInstanceAge(t *testing.T) {
// Mock RDS instance
rdsInstance := newRdsInstance()
creationDate := time.Date(2023, 9, 25, 12, 25, 0, 0, time.UTC) // Date of our first release
rdsInstance.InstanceCreateTime = &creationDate
mockDescribeDBInstancesOutput := &aws_rds.DescribeDBInstancesOutput{DBInstances: []aws_rds_types.DBInstance{*rdsInstance}}

mock := mockRDSClient{DescribeDBInstancesOutput: mockDescribeDBInstancesOutput}
configuration := rds.Configuration{}
client := rds.NewFetcher(mock, configuration)
metrics, err := client.GetInstancesMetrics()

expectedAge := time.Since(*rdsInstance.InstanceCreateTime)

require.NoError(t, err, "GetInstancesMetrics must succeed")
assert.Equal(t, int(expectedAge.Seconds()), int(*metrics.Instances[*rdsInstance.DBInstanceIdentifier].Age), "Age should match expected age")
}

0 comments on commit 5559980

Please sign in to comment.