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

PMM-12805 Collstats, indexstats iterate only over collections, not views. #790

Merged
merged 25 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ac586c7
PMM-12805 Func to list collections without views.
JiriCtvrtka Jan 31, 2024
4656b4f
PMM-12805 Filter for real collections.
JiriCtvrtka Jan 31, 2024
1f02217
PMM-12805 Remove print.
JiriCtvrtka Jan 31, 2024
e76cf31
PMM-12805 Remove comment.
JiriCtvrtka Jan 31, 2024
1d88101
Merge branch 'main' into PMM-12805-view-error
JiriCtvrtka Jan 31, 2024
257c3b5
PMM-12805 Format.
JiriCtvrtka Jan 31, 2024
2189b78
PMM-12805 Lint.
JiriCtvrtka Jan 31, 2024
7e90ef4
PMM-12805 Lint.
JiriCtvrtka Jan 31, 2024
107f41f
PMM-12805 Required changes.
JiriCtvrtka Feb 6, 2024
4afd507
PMM-12805 Lint.
JiriCtvrtka Feb 6, 2024
cc39990
PMM-12805 Fix test.
JiriCtvrtka Feb 6, 2024
e52fb67
PMM-12805 Change in logic.
JiriCtvrtka Feb 6, 2024
43ab368
PMM-12805 Better name for func.
JiriCtvrtka Feb 6, 2024
648f754
PMM-12805 Refactor to get collections with/without views.
JiriCtvrtka Feb 6, 2024
b48fdd7
Update exporter/common.go
JiriCtvrtka Feb 6, 2024
dcaa9dd
PMM-12805 Fix tests after refactor.
JiriCtvrtka Feb 6, 2024
6a01aa1
PMM-12805 Fix.
JiriCtvrtka Feb 6, 2024
5c694dd
PMM-12805 Better naming.
JiriCtvrtka Feb 6, 2024
67297ab
PMM-12805 Better naming for converted map.
JiriCtvrtka Feb 6, 2024
cc2a079
Update exporter/common_test.go
JiriCtvrtka Feb 7, 2024
f2cd323
PMM-12805 Small change to check exact error message.
JiriCtvrtka Feb 7, 2024
20242d8
PMM-12805 Fix for empty namaspace.
JiriCtvrtka Feb 8, 2024
5ba2cb9
PMM-12805 Fix for namespaces.
JiriCtvrtka Feb 8, 2024
7d03f95
PMM-12805 Skip not complete namespaces.
JiriCtvrtka Feb 9, 2024
e97c9cb
PMM-12805 Better performance for discovery mode.
JiriCtvrtka Feb 9, 2024
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
26 changes: 11 additions & 15 deletions exporter/collstats_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,27 @@ func (d *collstatsCollector) Collect(ch chan<- prometheus.Metric) {
func (d *collstatsCollector) collect(ch chan<- prometheus.Metric) {
defer measureCollectTime(ch, "mongodb", "collstats")()

collections := d.collections

client := d.base.client
logger := d.base.logger

collections, err := filterCollectionsWithoutViews(d.ctx, client, d.collections)
if err != nil {
logger.Errorf("cannot list collections: %s", err.Error())

return
}

if d.discoveringMode {
namespaces, err := listAllCollections(d.ctx, client, d.collections, systemDBs)
onlyCollections, err := listCollectionsWithoutViews(d.ctx, client)
if err != nil {
logger.Errorf("cannot auto discover databases and collections: %s", err.Error())

return
}

collections = fromMapToSlice(namespaces)
for collection := range onlyCollections {
collections = append(collections, collection)
}
}

for _, dbCollection := range collections {
Expand Down Expand Up @@ -134,15 +141,4 @@ func (d *collstatsCollector) collect(ch chan<- prometheus.Metric) {
}
}

func fromMapToSlice(databases map[string][]string) []string {
var collections []string
for db, cols := range databases {
for _, value := range cols {
collections = append(collections, db+"."+value)
}
}

return collections
}

var _ prometheus.Collector = (*collstatsCollector)(nil)
43 changes: 43 additions & 0 deletions exporter/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package exporter

import (
"context"
"fmt"
"sort"
"strings"

Expand Down Expand Up @@ -153,6 +154,48 @@ func unique(slice []string) []string {
return list
}

func listCollectionsWithoutViews(ctx context.Context, client *mongo.Client) (map[string]struct{}, error) {
dbs, err := databases(ctx, client, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "cannot make the list of databases to list all collections")
}

res := make(map[string]struct{})
for _, db := range dbs {
if db == "" {
continue
}

collections, err := client.Database(db).ListCollectionNames(ctx, bson.M{"type": "collection"})
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("cannot get the list of collections from database %s", db))
}
for _, collection := range collections {
res[fmt.Sprintf("%s.%s", db, collection)] = struct{}{}
}
}

return res, nil
}

func filterCollectionsWithoutViews(ctx context.Context, client *mongo.Client, collections []string) ([]string, error) {
onlyCollections, err := listCollectionsWithoutViews(ctx, client)
if err != nil {
return nil, err
}

filteredCollections := []string{}
for _, collection := range collections {
if _, ok := onlyCollections[collection]; !ok {
return nil, errors.Errorf("collection/namespace %s is view and cannot be used for collstats/indexstats", collection)
JiriCtvrtka marked this conversation as resolved.
Show resolved Hide resolved
}

filteredCollections = append(filteredCollections, collection)
}

return filteredCollections, nil
}

func listAllCollections(ctx context.Context, client *mongo.Client, filterInNamespaces []string, excludeDBs []string) (map[string][]string, error) {
namespaces := make(map[string][]string)

Expand Down
42 changes: 39 additions & 3 deletions exporter/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
var (
testDBs = []string{"testdb01", "testdb02"}
testColls = []string{"col01", "col02", "colxx", "colyy"}
testViews = []string{"view01", "view02"}
)

func setupDB(ctx context.Context, t *testing.T, client *mongo.Client) {
Expand All @@ -45,6 +46,10 @@ func setupDB(ctx context.Context, t *testing.T, client *mongo.Client) {
}
}
}
for _, view := range testViews {
err := client.Database(testDBs[0]).CreateView(ctx, view, testColls[0], mongo.Pipeline{})
assert.NoError(t, err)
}
}

func cleanupDB(ctx context.Context, client *mongo.Client) {
Expand Down Expand Up @@ -115,7 +120,7 @@ func TestListCollections(t *testing.T) {
t.Run("With namespaces list", func(t *testing.T) {
// Advanced filtering test
wantNS := map[string][]string{
"testdb01": {"col01", "col02", "colxx", "colyy"},
"testdb01": {"col01", "col02", "colxx", "colyy", "system.views", "view01", "view02"},
"testdb02": {"col01", "col02"},
}
// List all collections in testdb01 (inDBs[0]) but only col01 and col02 from testdb02.
Expand All @@ -127,18 +132,27 @@ func TestListCollections(t *testing.T) {

t.Run("Empty namespaces list", func(t *testing.T) {
wantNS := map[string][]string{
"testdb01": {"col01", "col02", "colxx", "colyy"},
"testdb01": {"col01", "col02", "colxx", "colyy", "system.views", "view01", "view02"},
"testdb02": {"col01", "col02", "colxx", "colyy"},
}
namespaces, err := listAllCollections(ctx, client, nil, systemDBs)
assert.NoError(t, err)
assert.Equal(t, wantNS, namespaces)
})

t.Run("Collections without views", func(t *testing.T) {
expected := []string{"testdb01.system.views", "testdb01.col01", "testdb01.colxx", "testdb01.colyy", "testdb02.colxx", "testdb02.colyy", "testdb02.col02", "testdb02.col01"}
collections, err := listCollectionsWithoutViews(ctx, client)
assert.NoError(t, err)
for _, expectedCollection := range expected {
assert.Contains(t, collections, expectedCollection)
}
})

t.Run("Count basic", func(t *testing.T) {
count, err := nonSystemCollectionsCount(ctx, client, nil, nil)
assert.NoError(t, err)
assert.Equal(t, 8, count)
assert.Equal(t, 11, count)
})

t.Run("Filtered count", func(t *testing.T) {
Expand Down Expand Up @@ -172,3 +186,25 @@ func TestSplitNamespace(t *testing.T) {
assert.Equal(t, tc.wantCollection, coll)
}
}

//nolint:paralleltest
func TestFilterCollectionsWithoutViews(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

client := tu.DefaultTestClient(ctx, t)

setupDB(ctx, t, client)
defer cleanupDB(ctx, client)

t.Run("Views in provided collection list (should fail)", func(t *testing.T) {
_, err := filterCollectionsWithoutViews(ctx, client, []string{"testdb01.col01", "testdb01.system.views", "testdb01.view01"})
assert.Error(t, err, "collection/namespace testdb01.view01 is view and annot be used for collstats/indexstats")
})

t.Run("No Views in provided collection list", func(t *testing.T) {
filtered, err := filterCollectionsWithoutViews(ctx, client, []string{"testdb01.col01", "testdb01.system.views"})
assert.NoError(t, err)
assert.Equal(t, []string{"testdb01.col01", "testdb01.system.views"}, filtered)
})
}
19 changes: 13 additions & 6 deletions exporter/indexstats_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,27 @@ func (d *indexstatsCollector) Collect(ch chan<- prometheus.Metric) {
func (d *indexstatsCollector) collect(ch chan<- prometheus.Metric) {
defer measureCollectTime(ch, "mongodb", "indexstats")()

collections := d.collections

logger := d.base.logger
client := d.base.client
logger := d.base.logger

collections, err := filterCollectionsWithoutViews(d.ctx, client, d.collections)
if err != nil {
logger.Errorf("cannot list collections: %s", err.Error())

return
}

if d.discoveringMode {
namespaces, err := listAllCollections(d.ctx, client, d.collections, systemDBs)
onlyCollections, err := listCollectionsWithoutViews(d.ctx, client)
if err != nil {
logger.Errorf("cannot auto discover databases and collections")
logger.Errorf("cannot auto discover databases and collections: %s", err.Error())

return
}

collections = fromMapToSlice(namespaces)
for collection := range onlyCollections {
collections = append(collections, collection)
}
}

for _, dbCollection := range collections {
Expand Down