From 8ec90f81096274a0c3b19a9901f0a3cbb6ccd023 Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Mon, 16 Feb 2026 15:06:00 +0200 Subject: [PATCH 1/7] PT-1738 - add mongos to pt-mongodb-summary This commit adds mongos instances report table to the mongodb summary --- src/go/pt-mongodb-summary/main.go | 62 ++++++++++++++++++- src/go/pt-mongodb-summary/templates/mongos.go | 38 ++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/go/pt-mongodb-summary/templates/mongos.go diff --git a/src/go/pt-mongodb-summary/main.go b/src/go/pt-mongodb-summary/main.go index 66bff3343..5a1092507 100644 --- a/src/go/pt-mongodb-summary/main.go +++ b/src/go/pt-mongodb-summary/main.go @@ -15,6 +15,7 @@ package main import ( "bytes" + "cmp" "context" "crypto/tls" "crypto/x509" @@ -26,6 +27,7 @@ import ( "os" "os/user" "path/filepath" + "slices" "strings" "time" @@ -35,6 +37,7 @@ import ( "github.com/pkg/errors" "github.com/shirou/gopsutil/process" log "github.com/sirupsen/logrus" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -159,6 +162,29 @@ type clusterwideInfo struct { Chunks []proto.ChunksByCollection } +type mongosInstance struct { + Name string `bson:"_id"` + LastPing time.Time `bson:"ping"` + UpTime int `bson:"up"` + Version string `bson:"mongoVersion"` +} + +type mongosInfo struct { + Instances []mongosInstance `bson:"instances"` +} + +func (t mongosInfo) MaxNameLen() int { + if len(t.Instances) == 0 { + return 0 + } + + maxInst := slices.MaxFunc(t.Instances, func(a, b mongosInstance) int { + return cmp.Compare(len(a.Name), len(b.Name)) + }) + + return len(maxInst.Name) +} + type cliOptions struct { Host string User string @@ -184,6 +210,7 @@ type collectedInfo struct { RunningOps *opCounters SecuritySettings *security HostInfo *hostInfo + MongosInfo *mongosInfo Errors []string } @@ -256,6 +283,11 @@ func main() { ci := &collectedInfo{} + ci.MongosInfo, err = getMongosInfo(ctx, client) + if err != nil { + log.Warnf("[Error] cannot get mongos info: %v\n", err) + } + ci.HostInfo, err = getHostInfo(ctx, client) if err != nil { log.Errorf("Cannot get host info for %q: %s", opts.Host, err) @@ -332,7 +364,12 @@ func formatResults(ci *collectedInfo, format string) ([]byte, error) { default: buf = new(bytes.Buffer) - t := template.Must(template.New("replicas").Parse(templates.Replicas)) + t := template.Must(template.New("mongos").Parse(templates.MongosInfo)) + if err := t.Execute(buf, ci.MongosInfo); err != nil { + return nil, errors.Wrap(err, "cannot parse mongos section of the output template") + } + + t = template.Must(template.New("replicas").Parse(templates.Replicas)) if err := t.Execute(buf, ci.ReplicaMembers); err != nil { return nil, errors.Wrap(err, "cannot parse replicas section of the output template") } @@ -454,6 +491,29 @@ func countMongodProcesses() (int, error) { return count, nil } +func getMongosInfo(ctx context.Context, client *mongo.Client) (*mongosInfo, error) { + threshold := time.Now().Add(-300 * time.Second) + + filter := bson.M{ + "ping": bson.M{ + "$gt": threshold, + }, + } + + cursor, err := client.Database("config").Collection("mongos").Find(ctx, filter) + if err != nil { + return nil, fmt.Errorf("failed to find mongos: %w", err) + } + defer cursor.Close(ctx) + + var instances []mongosInstance + if err := cursor.All(ctx, &instances); err != nil { + return nil, fmt.Errorf("failed to decode mongos: %w", err) + } + + return &mongosInfo{Instances: instances}, nil +} + func getClusterwideInfo(ctx context.Context, client *mongo.Client) (*clusterwideInfo, error) { var databases databases diff --git a/src/go/pt-mongodb-summary/templates/mongos.go b/src/go/pt-mongodb-summary/templates/mongos.go new file mode 100644 index 000000000..2b4a2f9b1 --- /dev/null +++ b/src/go/pt-mongodb-summary/templates/mongos.go @@ -0,0 +1,38 @@ +// This program is copyright 2016-2026 Percona LLC and/or its affiliates. +// +// THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// This program is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, version 2. +// +// You should have received a copy of the GNU General Public License, version 2 +// along with this program; if not, see . + +package templates + +const MongosInfo = ` +# Mongos ################################################################################################# +{{ $padding := 4 }} +{{- $timeWidth := 25 -}} +{{- $hostWidth := .MaxNameLen -}} +{{- $versionWidth := 12 -}} +{{- printf "%-*s" $hostWidth "Host" -}} +{{- printf "%-*s" $padding " " -}} +{{- printf "%-*s" $timeWidth "LastPing" -}} +{{- printf "%-*s" $padding " " -}} +{{- printf "%-*s" $versionWidth "Version" -}} +{{- printf "%-*s" $padding " " -}}Uptime (sec) +{{- if .Instances -}} +{{- range .Instances }} +{{ printf "%-*s" $hostWidth .Name }} +{{- printf "%-*s" $padding " " -}}{{ printf "%-*s" $timeWidth (.LastPing.Format "2006-01-02T15:04:05Z07:00") }} +{{- printf "%-*s" $padding " " -}}{{ printf "%-*s" $versionWidth .Version }} +{{- printf "%-*s" $padding " " -}}{{ printf "%-15d" .UpTime }} +{{- end }} +{{- else }} + no mongos instances found +{{- end }} +` From e9978af37362f2d28f535b0190634fcb4a156aaf Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Mon, 16 Feb 2026 15:19:37 +0200 Subject: [PATCH 2/7] refactor template --- src/go/pt-mongodb-summary/templates/mongos.go | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/go/pt-mongodb-summary/templates/mongos.go b/src/go/pt-mongodb-summary/templates/mongos.go index 2b4a2f9b1..8c4159a93 100644 --- a/src/go/pt-mongodb-summary/templates/mongos.go +++ b/src/go/pt-mongodb-summary/templates/mongos.go @@ -15,24 +15,23 @@ package templates const MongosInfo = ` # Mongos ################################################################################################# -{{ $padding := 4 }} -{{- $timeWidth := 25 -}} +{{ "" }} +{{- $padding := " " -}} +{{- $timeWidth := 20 -}} {{- $hostWidth := .MaxNameLen -}} -{{- $versionWidth := 12 -}} -{{- printf "%-*s" $hostWidth "Host" -}} -{{- printf "%-*s" $padding " " -}} -{{- printf "%-*s" $timeWidth "LastPing" -}} -{{- printf "%-*s" $padding " " -}} -{{- printf "%-*s" $versionWidth "Version" -}} -{{- printf "%-*s" $padding " " -}}Uptime (sec) -{{- if .Instances -}} -{{- range .Instances }} -{{ printf "%-*s" $hostWidth .Name }} -{{- printf "%-*s" $padding " " -}}{{ printf "%-*s" $timeWidth (.LastPing.Format "2006-01-02T15:04:05Z07:00") }} -{{- printf "%-*s" $padding " " -}}{{ printf "%-*s" $versionWidth .Version }} -{{- printf "%-*s" $padding " " -}}{{ printf "%-15d" .UpTime }} -{{- end }} -{{- else }} +{{- $versionWidth := 15 -}} + +{{ printf "%-*s" $hostWidth "Host" }}{{ $padding }} +{{- printf "%-*s" $timeWidth "LastPing" }}{{ $padding }} +{{- printf "%-*s" $versionWidth "Version" }}{{ $padding }}Uptime (sec) +{{ if .Instances -}} +{{- range .Instances -}} +{{ printf "%-*s" $hostWidth .Name }}{{ $padding }} +{{- printf "%-*s" $timeWidth (.LastPing.Format "2006-01-02 15:04:05") }}{{ $padding }} +{{- printf "%-*s" $versionWidth .Version }}{{ $padding }} +{{- printf "%-15d" .UpTime }} +{{ end }} +{{- else -}} no mongos instances found {{- end }} ` From d74e42c51e59931f0b5bcd9e7427505de03dcd7f Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Mon, 16 Feb 2026 15:20:33 +0200 Subject: [PATCH 3/7] template refactor --- src/go/pt-mongodb-summary/templates/mongos.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/go/pt-mongodb-summary/templates/mongos.go b/src/go/pt-mongodb-summary/templates/mongos.go index 8c4159a93..896f09f43 100644 --- a/src/go/pt-mongodb-summary/templates/mongos.go +++ b/src/go/pt-mongodb-summary/templates/mongos.go @@ -27,7 +27,7 @@ const MongosInfo = ` {{ if .Instances -}} {{- range .Instances -}} {{ printf "%-*s" $hostWidth .Name }}{{ $padding }} -{{- printf "%-*s" $timeWidth (.LastPing.Format "2006-01-02 15:04:05") }}{{ $padding }} +{{- printf "%-*s" $timeWidth (.LastPing.Format "2006-01-02T15:04:05Z07:00") }}{{ $padding }} {{- printf "%-*s" $versionWidth .Version }}{{ $padding }} {{- printf "%-15d" .UpTime }} {{ end }} From 8d72051d35d72d3a1e22f7a6eee25374fcc36f90 Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Thu, 19 Feb 2026 16:13:37 +0200 Subject: [PATCH 4/7] add test --- src/go/pt-mongodb-summary/main_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/go/pt-mongodb-summary/main_test.go b/src/go/pt-mongodb-summary/main_test.go index 0f1a9e69e..1e4b485ff 100644 --- a/src/go/pt-mongodb-summary/main_test.go +++ b/src/go/pt-mongodb-summary/main_test.go @@ -151,3 +151,23 @@ func TestParseArgs(t *testing.T) { os.Stdout = old } + +func TestGetMongosInfo(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + client, err := tu.TestClient(ctx, tu.MongoDBMongosPort) + require.NoError(t, err) + + info, err := getMongosInfo(ctx, client) + require.NoError(t, err) + require.NotNil(t, info) + require.NotEmpty(t, info.Instances) + + for _, m := range info.Instances { + require.NotEmpty(t, m.Name) + require.NotEmpty(t, m.Version) + require.NotEqual(t, 0, m.UpTime) + require.False(t, m.LastPing.IsZero()) + } +} From 834e6cc824458418fc848318928736501309d81b Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Thu, 19 Feb 2026 16:48:39 +0200 Subject: [PATCH 5/7] Update README.rst --- src/go/pt-mongodb-summary/README.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/go/pt-mongodb-summary/README.rst b/src/go/pt-mongodb-summary/README.rst index 1bfc4041f..7c74ec1b2 100644 --- a/src/go/pt-mongodb-summary/README.rst +++ b/src/go/pt-mongodb-summary/README.rst @@ -59,6 +59,14 @@ Output example .. code-block:: none + # Mongos ################################################################################################# + Host LastPing Version Uptime (sec) + my-cluster-name-mongos-0:27017 2026-02-16T13:01:22Z 8.0.17-6 3553 + my-cluster-name-mongos-1:27017 2026-02-16T13:01:26Z 8.0.17-6 3543 + my-cluster-name-mongos-2:27017 2026-02-16T13:01:28Z 8.0.17-6 3533 + + + # Instances #################################################################################### ID Host Type ReplSet 0 localhost:17001 PRIMARY r1 From 0d1cf6d1d741a30afe2c08e8497c8d8014bcdceb Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Thu, 19 Feb 2026 16:49:15 +0200 Subject: [PATCH 6/7] Update README.rst --- src/go/pt-mongodb-summary/README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/go/pt-mongodb-summary/README.rst b/src/go/pt-mongodb-summary/README.rst index 7c74ec1b2..151127280 100644 --- a/src/go/pt-mongodb-summary/README.rst +++ b/src/go/pt-mongodb-summary/README.rst @@ -64,8 +64,6 @@ Output example my-cluster-name-mongos-0:27017 2026-02-16T13:01:22Z 8.0.17-6 3553 my-cluster-name-mongos-1:27017 2026-02-16T13:01:26Z 8.0.17-6 3543 my-cluster-name-mongos-2:27017 2026-02-16T13:01:28Z 8.0.17-6 3533 - - # Instances #################################################################################### ID Host Type ReplSet From 449520256545dbbc245bcc6488cb693eee4b7654 Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Tue, 3 Mar 2026 13:20:09 +0200 Subject: [PATCH 7/7] Refactor logs --- src/go/pt-mongodb-summary/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/go/pt-mongodb-summary/main.go b/src/go/pt-mongodb-summary/main.go index 5a1092507..9c7a1345a 100644 --- a/src/go/pt-mongodb-summary/main.go +++ b/src/go/pt-mongodb-summary/main.go @@ -285,7 +285,7 @@ func main() { ci.MongosInfo, err = getMongosInfo(ctx, client) if err != nil { - log.Warnf("[Error] cannot get mongos info: %v\n", err) + log.Warnf("[Warning] cannot get mongos info: %v\n", err) } ci.HostInfo, err = getHostInfo(ctx, client) @@ -295,7 +295,7 @@ func main() { } if ci.ReplicaMembers, err = util.GetReplicasetMembers(ctx, clientOptions); err != nil { - log.Warnf("[Error] cannot get replicaset members: %v\n", err) + log.Warnf("[Warning] cannot get replicaset members: %v\n", err) } log.Debugf("replicaMembers:\n%+v\n", ci.ReplicaMembers)