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

metricbeat/module/mongodb/collstats: Add extra collstats metrics #42171

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

stefans-elastic
Copy link
Contributor

@stefans-elastic stefans-elastic commented Dec 27, 2024

Proposed commit message

[metricbeats][mongodb] handle extra collstats metrics

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding change to the default configuration files
  • I have added tests that prove my fix is effective or that my feature works
  • I have added an entry in CHANGELOG.next.asciidoc or CHANGELOG-developer.next.asciidoc.

Disruptive User Impact

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

Use cases

Screenshots

Screenshot 2025-01-02 at 10 24 05 AM

Logs

@stefans-elastic stefans-elastic added enhancement Team:Obs-InfraObs Label for the Observability Infrastructure Monitoring team labels Dec 27, 2024
@stefans-elastic stefans-elastic self-assigned this Dec 27, 2024
@botelastic botelastic bot added needs_team Indicates that the issue/PR needs a Team:* label and removed needs_team Indicates that the issue/PR needs a Team:* label labels Dec 27, 2024
Copy link
Contributor

mergify bot commented Dec 27, 2024

This pull request does not have a backport label.
If this is a bug or security fix, could you label this PR @stefans-elastic? 🙏.
For such, you'll need to label your PR with:

  • The upcoming major version of the Elastic Stack
  • The upcoming minor version of the Elastic Stack (if you're not pushing a breaking change)

To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-8./d is the label to automatically backport to the 8./d branch. /d is the digit

Copy link
Contributor

mergify bot commented Dec 27, 2024

backport-8.x has been added to help with the transition to the new branch 8.x.
If you don't need it please use backport-skip label and remove the backport-8.x label.

@mergify mergify bot added the backport-8.x Automated backport to the 8.x branch with mergify label Dec 27, 2024
@stefans-elastic stefans-elastic marked this pull request as ready for review January 2, 2025 12:50
@stefans-elastic stefans-elastic requested review from a team as code owners January 2, 2025 12:51
@shmsr
Copy link
Member

shmsr commented Jan 16, 2025

Here are a few suggestions:

diff --git a/metricbeat/module/mongodb/collstats/_meta/fields.yml b/metricbeat/module/mongodb/collstats/_meta/fields.yml
index 27c4f989a7..a4a09f8d09 100644
--- a/metricbeat/module/mongodb/collstats/_meta/fields.yml
+++ b/metricbeat/module/mongodb/collstats/_meta/fields.yml
@@ -117,19 +117,19 @@
         - name: stats.avgObjSize
           type: long
           description: >
-            The average size of an object in the collection.
+            The average size of an object in the collection (in bytes).
         - name: stats.storageSize
           type: long
           description: >
-            The total amount of storage allocated to this collection for document storage.
+            The total amount of storage allocated to this collection for document storage (in bytes).
         - name: stats.totalIndexSize
           type: long
           description: >
-            The total size of all indexes.
+            The total size of all indexes (in bytes).
         - name: stats.totalSize
           type: long
           description: >
-            The sum of the storageSize and totalIndexSize.
+            The sum of the storageSize and totalIndexSize (in bytes).
         - name: stats.max
           type: long
           description: >
diff --git a/metricbeat/module/mongodb/collstats/collstats.go b/metricbeat/module/mongodb/collstats/collstats.go
index 64f345c8d2..c760845d0f 100644
--- a/metricbeat/module/mongodb/collstats/collstats.go
+++ b/metricbeat/module/mongodb/collstats/collstats.go
@@ -90,7 +90,7 @@ func (m *Metricset) Fetch(reporter mb.ReporterV2) error {
 
 	totals, ok := result["totals"].(map[string]interface{})
 	if !ok {
-		return errors.New("collection 'totals' are not a map")
+		return errors.New("collection 'totals' is not a map")
 	}
 
 	if err = res.Err(); err != nil {
@@ -147,9 +147,13 @@ func (m *Metricset) Fetch(reporter mb.ReporterV2) error {
 
 func fetchCollStats(client *mongo.Client, dbName, collectionName string) (map[string]interface{}, error) {
 	db := client.Database(dbName)
-	colStats := db.RunCommand(context.Background(), bson.M{"collStats": collectionName})
+	collStats := db.RunCommand(context.Background(), bson.M{"collStats": collectionName})
+	if err := collStats.Err(); err != nil {
+		return nil, fmt.Errorf("collStats command failed: %w", err)
+	}
+
 	var statsRes map[string]interface{}
-	if err := colStats.Decode(&statsRes); err != nil {
+	if err := collStats.Decode(&statsRes); err != nil {
 		return nil, fmt.Errorf("could not decode mongo response for database=%s, collection=%s: %w", dbName, collectionName, err)
 	}
 
diff --git a/metricbeat/module/mongodb/collstats/data.go b/metricbeat/module/mongodb/collstats/data.go
index cef81df7c3..e9600fb83e 100644
--- a/metricbeat/module/mongodb/collstats/data.go
+++ b/metricbeat/module/mongodb/collstats/data.go
@@ -30,9 +30,12 @@ func eventMapping(key string, data mapstr.M) (mapstr.M, error) {
 		return nil, err
 	}
 
+	// NOTE: splitKey handles the case where the collection can have "." in the name
+	database, collection := names[0], names[1]
+
 	event := mapstr.M{
-		"db":         names[0],
-		"collection": names[1],
+		"db":         database,
+		"collection": collection,
 		"name":       key,
 		"total": mapstr.M{
 			"time": mapstr.M{
@@ -111,11 +114,11 @@ func mustGetMapStrValue(m mapstr.M, key string) interface{} {
 }
 
 func splitKey(key string) ([]string, error) {
-	names := strings.SplitN(key, ".", 2)
+	dbColl := strings.SplitN(key, ".", 2)
 
-	if len(names) < 2 {
+	if len(dbColl) < 2 {
 		return nil, errors.New("collection name invalid")
 	}
 
-	return names, nil
+	return dbColl, nil
 }
diff --git a/metricbeat/module/mongodb/collstats/data_test.go b/metricbeat/module/mongodb/collstats/data_test.go
index a921d14d72..eefd800c21 100644
--- a/metricbeat/module/mongodb/collstats/data_test.go
+++ b/metricbeat/module/mongodb/collstats/data_test.go
@@ -21,7 +21,7 @@ package collstats
 
 import (
 	"encoding/json"
-	"io/ioutil"
+	"os"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -30,8 +30,7 @@ import (
 )
 
 func TestEventMapping(t *testing.T) {
-
-	content, err := ioutil.ReadFile("./_meta/test/input.json")
+	content, err := os.ReadFile("./_meta/test/input.json")
 	assert.NoError(t, err)
 
 	data := mapstr.M{}

@shmsr shmsr changed the title [metricbeats][mongodb] handle extra collstats metrics metricbeat/module/mongodb/collstats: Add extra collstats metrics Jan 16, 2025
@shmsr
Copy link
Member

shmsr commented Jan 16, 2025

Also, can you also write a better commit message; helps in future when we need to revisit the changes and to know what and why of the change.

@shmsr
Copy link
Member

shmsr commented Jan 16, 2025

But yes, rest looks good!

"totalIndexSize": mustGetMapStrValue(data, "stats.totalIndexSize"),
"totalSize": mustGetMapStrValue(data, "stats.totalSize"),
"max": mustGetMapStrValue(data, "stats.max"),
"nindexes": mustGetMapStrValue(data, "stats.nindexes"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a lot of fileds which we get here @shmsr .
We planned to list down the most useful one.

MetricSetFields: event,
})
wg.Add(1)
go func(eventReporter mb.ReporterV2, mongoClient *mongo.Client, group string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also what's the rationale behind using goroutines here? Also, even if we are keeping it can be do bounded concurrency. Dont wanna fire too many queries and burden the customer's MongoDB server.

    sem := make(chan struct{}, 10) // Limit concurrent operations
    
    for group, info := range totals {
        sem <- struct{}{} // Acquire
        go func() {
            defer func() { <-sem }() // Release
            // Existing goroutine code
        }()
    }

I mean, can we add semaphore or worker pool to limit the concurrency?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIP: Also, without bounded concurrency, errgroup is cleaner way to implement this: https://pkg.go.dev/golang.org/x/sync/errgroup

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree to this:
For large datasets, the number of goroutines can overwhelm the system. Let's use workerpool to limit the concurrency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've replaced WaitGroup with errgroup. Btw, I set goroutins limit to 10 like was suggested in the example. Is 10 a good limit or should I change it to something else?

MetricSetFields: event,
})
wg.Add(1)
go func(eventReporter mb.ReporterV2, mongoClient *mongo.Client, group string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree to this:
For large datasets, the number of goroutines can overwhelm the system. Let's use workerpool to limit the concurrency.

return
}

collStats, err := fetchCollStats(mongoClient, names[0], names[1])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better naming for names[0], names[1] please

@stefans-elastic
Copy link
Contributor Author

@ishleenk17 @shmsr thank you for your reviews!
I've made the requested changes, please take a look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-8.x Automated backport to the 8.x branch with mergify enhancement Team:Obs-InfraObs Label for the Observability Infrastructure Monitoring team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add totalsize metric in MongoDB collstats metricset
4 participants