Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
MongoDB Atlas monitor implementation (#1322)
Browse files Browse the repository at this point in the history
* MongoDB Atlas monitor implementation

Issue: https://signalfuse.atlassian.net/browse/INT-771
  • Loading branch information
bjsignalfx authored Jun 3, 2020
1 parent 2a304e6 commit ce9f1ea
Show file tree
Hide file tree
Showing 13 changed files with 2,475 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/monitor-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ These are all of the monitors included in the agent, along with their possible c
- [logstash](./monitors/logstash.md)
- [logstash-tcp](./monitors/logstash-tcp.md)
- [memory](./monitors/memory.md)
- [mongodb-atlas](./monitors/mongodb-atlas.md)
- [net-io](./monitors/net-io.md)
- [ntp](./monitors/ntp.md)
- [openshift-cluster](./monitors/openshift-cluster.md)
Expand Down
181 changes: 181 additions & 0 deletions docs/monitors/mongodb-atlas.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/Microsoft/go-winio v0.4.11
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/SAP/go-hdb v0.14.1 // indirect
github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a
github.com/ShowMax/go-fqdn v0.0.0-20160909083404-2501cdd51ef4
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190315122603-6f9e54af456e // indirect
Expand Down Expand Up @@ -113,6 +114,7 @@ require (
github.com/mitchellh/go-wordwrap v1.0.0
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452
github.com/mitchellh/pointerstructure v0.0.0-20170205204203-f2329fcfa9e2 // indirect
github.com/mongodb/go-client-mongodb-atlas v0.2.0
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.1
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ github.com/RoaringBitmap/roaring v0.4.16/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQi
github.com/SAP/go-hdb v0.13.1/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0=
github.com/SAP/go-hdb v0.14.1 h1:hkw4ozGZ/i4eak7ZuGkY5e0hxiXFdNUBNhr4AvZVNFE=
github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPEkMo=
github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a h1:KFHLI4QGttB0i7M3qOkAo8Zn/GSsxwwCnInFqBaYtkM=
github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a/go.mod h1:D73UAuEPckrDorYZdtlCu2ySOLuPB5W4rhIkmmc/XbI=
github.com/SermoDigital/jose v0.9.1 h1:atYaHPD3lPICcbK1owly3aPm0iaJGSGPi0WD4vLznv8=
github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
github.com/ShowMax/go-fqdn v0.0.0-20160909083404-2501cdd51ef4 h1:LbMMQr4Ij1Eq5RUNsLBpNmcnhRXuq+BlyInEbI4FTAU=
Expand Down Expand Up @@ -662,11 +664,15 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mongodb/go-client-mongodb-atlas v0.2.0 h1:UH02byrzAlboirAMgV0sMIJqXbEfmGXIBa5IniwUZNI=
github.com/mongodb/go-client-mongodb-atlas v0.2.0/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwielbut/pointy v1.1.0 h1:U5/YEfoIkaGCHv0St3CgjduqXID4FNRoyZgLM1kY9vg=
github.com/mwielbut/pointy v1.1.0/go.mod h1:MvvO+uMFj9T5DMda33HlvogsFBX7pWWKAkFIn4teYwY=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
Expand Down
1 change: 1 addition & 0 deletions pkg/core/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
_ "github.com/signalfx/signalfx-agent/pkg/monitors/logstash/tcp"
_ "github.com/signalfx/signalfx-agent/pkg/monitors/memory"
_ "github.com/signalfx/signalfx-agent/pkg/monitors/metadata/hostmetadata"
_ "github.com/signalfx/signalfx-agent/pkg/monitors/mongodb/atlas"
_ "github.com/signalfx/signalfx-agent/pkg/monitors/netio"
_ "github.com/signalfx/signalfx-agent/pkg/monitors/ntp"
_ "github.com/signalfx/signalfx-agent/pkg/monitors/postgresql"
Expand Down
323 changes: 323 additions & 0 deletions pkg/monitors/mongodb/atlas/genmetadata.go

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions pkg/monitors/mongodb/atlas/mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package atlas

// metricsMap maps Atlas measurement names to metrics.
var metricsMap = map[string]string{
"ASSERT_REGULAR": assertsRegular,
"ASSERT_WARNING": assertsWarning,
"ASSERT_MSG": assertsMsg,
"ASSERT_USER": assertsUser,
"CACHE_BYTES_READ_INTO": cacheBytesReadInto,
"CACHE_BYTES_WRITTEN_FROM": cacheBytesWrittenFrom,
"CACHE_USED_BYTES": cacheUsedBytes,
"CACHE_DIRTY_BYTES": cacheDirtyBytes,
"OPCOUNTER_CMD": opcounterCommand,
"OPCOUNTER_DELETE": opcounterDelete,
"OPCOUNTER_GETMORE": opcounterGetmore,
"OPCOUNTER_INSERT": opcounterInsert,
"OPCOUNTER_QUERY": opcounterQuery,
"OPCOUNTER_UPDATE": opcounterUpdate,
"OPCOUNTER_REPL_CMD": opcounterReplCommand,
"OPCOUNTER_REPL_DELETE": opcounterReplDelete,
"OPCOUNTER_REPL_INSERT": opcounterReplInsert,
"OPCOUNTER_REPL_UPDATE": opcounterReplUpdate,
"CONNECTIONS": connectionsCurrent,
"CURSORS_TOTAL_OPEN": cursorsTotalOpen,
"CURSORS_TOTAL_TIMED_OUT": cursorsTimedOut,
"DB_STORAGE_TOTAL": storageSize,
"DB_DATA_SIZE_TOTAL": dataSize,
"DB_INDEX_SIZE_TOTAL": indexSize,
"DOCUMENT_METRICS_RETURNED": documentMetricsReturned,
"DOCUMENT_METRICS_INSERTED": documentMetricsInserted,
"DOCUMENT_METRICS_UPDATED": documentMetricsUpdated,
"DOCUMENT_METRICS_DELETED": documentMetricsDeleted,
"MEMORY_RESIDENT": memResident,
"MEMORY_VIRTUAL": memVirtual,
"MEMORY_MAPPED": memMapped,
"NETWORK_BYTES_IN": networkBytesIn,
"NETWORK_BYTES_OUT": networkBytesOut,
"NETWORK_NUM_REQUESTS": networkNumRequests,
"OP_EXECUTION_TIME_READS": opExecutionTimeReads,
"OP_EXECUTION_TIME_WRITES": opExecutionTimeWrites,
"OP_EXECUTION_TIME_COMMANDS": opExecutionTimeCommands,
"OPLOG_RATE_GB_PER_HOUR": oplogRate,
"OPLOG_MASTER_LAG_TIME_DIFF": oplogMasterLagTimeDiff,
"OPLOG_SLAVE_LAG_MASTER_TIME": oplogSlaveLagMasterTime,
"OPLOG_MASTER_TIME": oplogMasterTime,
"EXTRA_INFO_PAGE_FAULTS": extraInfoPageFaults,
"GLOBAL_LOCK_CURRENT_QUEUE_TOTAL": globalLockCurrentQueueTotal,
"GLOBAL_LOCK_CURRENT_QUEUE_READERS": globalLockCurrentQueueReaders,
"GLOBAL_LOCK_CURRENT_QUEUE_WRITERS": globalLockCurrentQueueWriters,
"QUERY_EXECUTOR_SCANNED": queryExecutorScanned,
"QUERY_EXECUTOR_SCANNED_OBJECTS": queryExecutorScannedObjects,
"QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED": queryTargetingScannedObjectsPerReturned,
"QUERY_TARGETING_SCANNED_PER_RETURNED": queryTargetingScannedPerReturned,
"TICKETS_AVAILABLE_READS": ticketsAvailableReads,
"TICKETS_AVAILABLE_WRITE": ticketsAvailableWrite,
"DISK_PARTITION_IOPS_READ": diskPartitionIopsRead,
"DISK_PARTITION_IOPS_WRITE": diskPartitionIopsWrite,
"DISK_PARTITION_IOPS_TOTAL": diskPartitionIopsTotal,
"DISK_PARTITION_LATENCY_READ": diskPartitionLatencyRead,
"DISK_PARTITION_LATENCY_WRITE": diskPartitionLatencyWrite,
"DISK_PARTITION_SPACE_FREE": diskPartitionSpaceFree,
"DISK_PARTITION_SPACE_PERCENT_FREE": diskPartitionSpacePercentFree,
"DISK_PARTITION_SPACE_USED": diskPartitionSpaceUsed,
"DISK_PARTITION_SPACE_PERCENT_USED": diskPartitionSpacePercentUsed,
"DISK_PARTITION_UTILIZATION": diskPartitionUtilization,
"PROCESS_CPU_USER": processCPUUser,
"PROCESS_CPU_KERNEL": processCPUKernel,
"PROCESS_NORMALIZED_CPU_USER": processNormalizedCPUUser,
"PROCESS_NORMALIZED_CPU_KERNEL": processNormalizedCPUKernel,
"PROCESS_NORMALIZED_CPU_CHILDREN_USER": processNormalizedCPUChildrenUser,
"PROCESS_NORMALIZED_CPU_CHILDREN_KERNEL": processNormalizedCPUChildrenKernel,
"SYSTEM_CPU_USER": systemCPUUser,
"SYSTEM_CPU_KERNEL": systemCPUKernel,
"SYSTEM_CPU_NICE": systemCPUNice,
"SYSTEM_CPU_IOWAIT": systemCPUIowait,
"SYSTEM_CPU_IRQ": systemCPUIrq,
"SYSTEM_CPU_SOFTIRQ": systemCPUSoftirq,
"SYSTEM_CPU_GUEST": systemCPUGuest,
"SYSTEM_CPU_STEAL": systemCPUSteal,
"SYSTEM_NORMALIZED_CPU_USER": systemNormalizedCPUUser,
"SYSTEM_NORMALIZED_CPU_KERNEL": systemNormalizedCPUKernel,
"SYSTEM_NORMALIZED_CPU_NICE": systemNormalizedCPUNice,
"SYSTEM_NORMALIZED_CPU_IOWAIT": systemNormalizedCPUIowait,
"SYSTEM_NORMALIZED_CPU_IRQ": systemNormalizedCPUIrq,
"SYSTEM_NORMALIZED_CPU_SOFTIRQ": systemNormalizedCPUSoftirq,
"SYSTEM_NORMALIZED_CPU_GUEST": systemNormalizedCPUGuest,
"SYSTEM_NORMALIZED_CPU_STEAL": systemNormalizedCPUSteal,
"OPERATIONS_SCAN_AND_ORDER": operationsScanAndOrder,
"BACKGROUND_FLUSH_AVG": backgroundFlushAvg,
}
51 changes: 51 additions & 0 deletions pkg/monitors/mongodb/atlas/measurements/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package measurements

import (
"fmt"

"github.com/mongodb/go-client-mongodb-atlas/mongodbatlas"
log "github.com/sirupsen/logrus"
)

// Process is the MongoDB Process identified by the host and port on which the Process is running.
type Process struct {
ID string // The hostname and port of the Atlas MongoDB process in hostname:port format.
ProjectID string // A string value that uniquely identifies a Atlas project.
Host string // Name of the host in which the MongoDB Process is running.
Port int // Port number on which the MongoDB Process is running.
ShardName string // Name of the shard this process belongs to. Only present if this process is part of a sharded cluster.
ReplicaSetName string // Name of the replica set this process belongs to. Only present if this process is part of a replica set.
TypeName string // Type for this Atlas MongoDB process.
}

// nextPage gets the next page for pagination request.
func nextPage(resp *mongodbatlas.Response) (bool, int) {
if resp == nil || len(resp.Links) == 0 || resp.IsLastPage() {
return false, -1
}

currentPage, err := resp.CurrentPage()

if err != nil {
log.WithError(err).Error("failed to get the next page")
return false, -1
}

return true, currentPage + 1
}

func errorMsg(err error, resp *mongodbatlas.Response) (string, error) {
if err != nil {
return "request for getting %s failed (Atlas project: %s, host: %s, port: %d)", err
}

if resp == nil {
return "response for getting %s returned empty (Atlas project: %s, host: %s, port: %d)", fmt.Errorf("empty response")
}

if err := mongodbatlas.CheckResponse(resp.Response); err != nil {
return "response for getting %s returned error (Atlas project: %s, host: %s, port: %d)", err
}

return "", nil
}
180 changes: 180 additions & 0 deletions pkg/monitors/mongodb/atlas/measurements/disks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package measurements

import (
"context"
"sync"
"sync/atomic"
"time"

"github.com/mongodb/go-client-mongodb-atlas/mongodbatlas"
log "github.com/sirupsen/logrus"
)

// DisksMeasurements are the metric measurements of a particular disk partition in a MongoDB process host.
type DisksMeasurements map[Process]struct {
PartitionName string
Measurements []*mongodbatlas.Measurements
}

// DisksGetter is for fetching metric measurements of disk partitions in the MongoDB processes hosts.
type DisksGetter interface {
GetMeasurements(ctx context.Context, timeout time.Duration, processes []Process) DisksMeasurements
}

// disksGetter implements DisksGetter
type disksGetter struct {
projectID string
granularity string
period string
client *mongodbatlas.Client
enableCache bool
mutex *sync.Mutex
measurementsCache *atomic.Value
disksCache *atomic.Value
}

// NewProcessesGetter returns a new ProcessesGetter.
func NewDisksGetter(projectID string, granularity string, period string, client *mongodbatlas.Client, enableCache bool) DisksGetter {
return &disksGetter{
projectID: projectID,
granularity: granularity,
period: period,
client: client,
enableCache: enableCache,
mutex: new(sync.Mutex),
measurementsCache: new(atomic.Value),
disksCache: new(atomic.Value),
}
}

// GetMeasurements gets metric measurements of disk partitions in the hosts of the given MongoDB processes.
func (getter *disksGetter) GetMeasurements(ctx context.Context, timeout time.Duration, processes []Process) DisksMeasurements {
var measurements = make(DisksMeasurements)

partitions := getter.getPartitions(ctx, timeout, processes)

var wg1 sync.WaitGroup

wg1.Add(1)

go func() {
defer wg1.Done()

var wg2 sync.WaitGroup
for process, partitionNames := range partitions {
for _, partitionName := range partitionNames {
wg2.Add(1)

go func(process Process, partitionName string) {
defer wg2.Done()

var ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()

getter.setMeasurements(ctx, measurements, process, partitionName, 1)
}(process, partitionName)
}
}
wg2.Wait()

if getter.enableCache {
getter.measurementsCache.Store(measurements)
}
}()

if getter.measurementsCache.Load() != nil && getter.enableCache {
return getter.measurementsCache.Load().(DisksMeasurements)
}

wg1.Wait()

return measurements
}

// getPartitions is a helper function for fetching the names of disk partitions is the hosts of given MongoDB processes.
func (getter *disksGetter) getPartitions(ctx context.Context, timeout time.Duration, processes []Process) map[Process][]string {
var partitions = make(map[Process][]string)

var wg1 sync.WaitGroup

wg1.Add(1)

go func() {
defer wg1.Done()

var wg2 sync.WaitGroup
for _, process := range processes {
wg2.Add(1)

go func(process Process) {
defer wg2.Done()

var ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()

partitionNames := getter.getPartitionNames(ctx, process, 1)

getter.mutex.Lock()
defer getter.mutex.Unlock()
partitions[process] = partitionNames
}(process)
}
wg2.Wait()

if getter.enableCache {
getter.disksCache.Store(partitions)
}
}()

if getter.disksCache.Load() != nil && getter.enableCache {
return getter.disksCache.Load().(map[Process][]string)
}

wg1.Wait()

return partitions
}

// getPartitionNames is a helper function of function getPartitions.
func (getter *disksGetter) getPartitionNames(ctx context.Context, process Process, page int) (names []string) {
list, resp, err := getter.client.ProcessDisks.List(ctx, getter.projectID, process.Host, process.Port, &mongodbatlas.ListOptions{PageNum: page})

if msg, err := errorMsg(err, resp); err != nil {
log.WithError(err).Errorf(msg, "disk partition names", getter.projectID, process.Host, process.Port)
return names
}

if ok, next := nextPage(resp); ok {
names = append(names, getter.getPartitionNames(ctx, process, next)...)
}

for _, r := range list.Results {
names = append(names, r.PartitionName)
}

return names
}

// setMeasurements is a helper function of method GetMeasurements.
func (getter *disksGetter) setMeasurements(ctx context.Context, disksMeasurements DisksMeasurements, process Process, partitionName string, pageNum int) {
var opts = &mongodbatlas.ProcessMeasurementListOptions{ListOptions: &mongodbatlas.ListOptions{PageNum: pageNum}, Granularity: getter.granularity, Period: getter.period}

list, resp, err := getter.client.ProcessDiskMeasurements.List(ctx, getter.projectID, process.Host, process.Port, partitionName, opts)

if msg, err := errorMsg(err, resp); err != nil {
log.WithError(err).Errorf(msg, "disk measurements", getter.projectID, process.Host, process.Port)
return
}

if ok, next := nextPage(resp); ok {
getter.setMeasurements(ctx, disksMeasurements, process, partitionName, next)
}

getter.mutex.Lock()
defer getter.mutex.Unlock()

disksMeasurements[process] = struct {
PartitionName string
Measurements []*mongodbatlas.Measurements
}{PartitionName: partitionName, Measurements: list.Measurements}
}
Loading

0 comments on commit ce9f1ea

Please sign in to comment.