Skip to content

Commit 4aa1d6d

Browse files
Merge pull request #1076 from percona/PT-1738-pt-mongodb-summary-add-mongos-report
PT-1738 - add mongos to pt-mongodb-summary
2 parents 47e8d70 + 4495202 commit 4aa1d6d

4 files changed

Lines changed: 125 additions & 2 deletions

File tree

src/go/pt-mongodb-summary/README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ Output example
5959
6060
.. code-block:: none
6161
62+
# Mongos #################################################################################################
63+
Host LastPing Version Uptime (sec)
64+
my-cluster-name-mongos-0:27017 2026-02-16T13:01:22Z 8.0.17-6 3553
65+
my-cluster-name-mongos-1:27017 2026-02-16T13:01:26Z 8.0.17-6 3543
66+
my-cluster-name-mongos-2:27017 2026-02-16T13:01:28Z 8.0.17-6 3533
67+
6268
# Instances ####################################################################################
6369
ID Host Type ReplSet
6470
0 localhost:17001 PRIMARY r1

src/go/pt-mongodb-summary/main.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package main
1515

1616
import (
1717
"bytes"
18+
"cmp"
1819
"context"
1920
"crypto/tls"
2021
"crypto/x509"
@@ -26,6 +27,7 @@ import (
2627
"os"
2728
"os/user"
2829
"path/filepath"
30+
"slices"
2931
"strings"
3032
"time"
3133

@@ -35,6 +37,7 @@ import (
3537
"github.com/pkg/errors"
3638
"github.com/shirou/gopsutil/process"
3739
log "github.com/sirupsen/logrus"
40+
"go.mongodb.org/mongo-driver/bson"
3841
"go.mongodb.org/mongo-driver/bson/primitive"
3942
"go.mongodb.org/mongo-driver/mongo"
4043
"go.mongodb.org/mongo-driver/mongo/options"
@@ -159,6 +162,29 @@ type clusterwideInfo struct {
159162
Chunks []proto.ChunksByCollection
160163
}
161164

165+
type mongosInstance struct {
166+
Name string `bson:"_id"`
167+
LastPing time.Time `bson:"ping"`
168+
UpTime int `bson:"up"`
169+
Version string `bson:"mongoVersion"`
170+
}
171+
172+
type mongosInfo struct {
173+
Instances []mongosInstance `bson:"instances"`
174+
}
175+
176+
func (t mongosInfo) MaxNameLen() int {
177+
if len(t.Instances) == 0 {
178+
return 0
179+
}
180+
181+
maxInst := slices.MaxFunc(t.Instances, func(a, b mongosInstance) int {
182+
return cmp.Compare(len(a.Name), len(b.Name))
183+
})
184+
185+
return len(maxInst.Name)
186+
}
187+
162188
type cliOptions struct {
163189
Host string
164190
User string
@@ -184,6 +210,7 @@ type collectedInfo struct {
184210
RunningOps *opCounters
185211
SecuritySettings *security
186212
HostInfo *hostInfo
213+
MongosInfo *mongosInfo
187214
Errors []string
188215
}
189216

@@ -256,14 +283,19 @@ func main() {
256283

257284
ci := &collectedInfo{}
258285

286+
ci.MongosInfo, err = getMongosInfo(ctx, client)
287+
if err != nil {
288+
log.Warnf("[Warning] cannot get mongos info: %v\n", err)
289+
}
290+
259291
ci.HostInfo, err = getHostInfo(ctx, client)
260292
if err != nil {
261293
log.Errorf("Cannot get host info for %q: %s", opts.Host, err)
262294
os.Exit(cannotGetHostInfo) //nolint:gocritic
263295
}
264296

265297
if ci.ReplicaMembers, err = util.GetReplicasetMembers(ctx, clientOptions); err != nil {
266-
log.Warnf("[Error] cannot get replicaset members: %v\n", err)
298+
log.Warnf("[Warning] cannot get replicaset members: %v\n", err)
267299
}
268300

269301
log.Debugf("replicaMembers:\n%+v\n", ci.ReplicaMembers)
@@ -332,7 +364,12 @@ func formatResults(ci *collectedInfo, format string) ([]byte, error) {
332364
default:
333365
buf = new(bytes.Buffer)
334366

335-
t := template.Must(template.New("replicas").Parse(templates.Replicas))
367+
t := template.Must(template.New("mongos").Parse(templates.MongosInfo))
368+
if err := t.Execute(buf, ci.MongosInfo); err != nil {
369+
return nil, errors.Wrap(err, "cannot parse mongos section of the output template")
370+
}
371+
372+
t = template.Must(template.New("replicas").Parse(templates.Replicas))
336373
if err := t.Execute(buf, ci.ReplicaMembers); err != nil {
337374
return nil, errors.Wrap(err, "cannot parse replicas section of the output template")
338375
}
@@ -454,6 +491,29 @@ func countMongodProcesses() (int, error) {
454491
return count, nil
455492
}
456493

494+
func getMongosInfo(ctx context.Context, client *mongo.Client) (*mongosInfo, error) {
495+
threshold := time.Now().Add(-300 * time.Second)
496+
497+
filter := bson.M{
498+
"ping": bson.M{
499+
"$gt": threshold,
500+
},
501+
}
502+
503+
cursor, err := client.Database("config").Collection("mongos").Find(ctx, filter)
504+
if err != nil {
505+
return nil, fmt.Errorf("failed to find mongos: %w", err)
506+
}
507+
defer cursor.Close(ctx)
508+
509+
var instances []mongosInstance
510+
if err := cursor.All(ctx, &instances); err != nil {
511+
return nil, fmt.Errorf("failed to decode mongos: %w", err)
512+
}
513+
514+
return &mongosInfo{Instances: instances}, nil
515+
}
516+
457517
func getClusterwideInfo(ctx context.Context, client *mongo.Client) (*clusterwideInfo, error) {
458518
var databases databases
459519

src/go/pt-mongodb-summary/main_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,23 @@ func TestParseArgs(t *testing.T) {
151151

152152
os.Stdout = old
153153
}
154+
155+
func TestGetMongosInfo(t *testing.T) {
156+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
157+
defer cancel()
158+
159+
client, err := tu.TestClient(ctx, tu.MongoDBMongosPort)
160+
require.NoError(t, err)
161+
162+
info, err := getMongosInfo(ctx, client)
163+
require.NoError(t, err)
164+
require.NotNil(t, info)
165+
require.NotEmpty(t, info.Instances)
166+
167+
for _, m := range info.Instances {
168+
require.NotEmpty(t, m.Name)
169+
require.NotEmpty(t, m.Version)
170+
require.NotEqual(t, 0, m.UpTime)
171+
require.False(t, m.LastPing.IsZero())
172+
}
173+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// This program is copyright 2016-2026 Percona LLC and/or its affiliates.
2+
//
3+
// THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
4+
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
5+
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
6+
//
7+
// This program is free software; you can redistribute it and/or modify it under
8+
// the terms of the GNU General Public License as published by the Free Software
9+
// Foundation, version 2.
10+
//
11+
// You should have received a copy of the GNU General Public License, version 2
12+
// along with this program; if not, see <https://www.gnu.org/licenses/>.
13+
14+
package templates
15+
16+
const MongosInfo = `
17+
# Mongos #################################################################################################
18+
{{ "" }}
19+
{{- $padding := " " -}}
20+
{{- $timeWidth := 20 -}}
21+
{{- $hostWidth := .MaxNameLen -}}
22+
{{- $versionWidth := 15 -}}
23+
24+
{{ printf "%-*s" $hostWidth "Host" }}{{ $padding }}
25+
{{- printf "%-*s" $timeWidth "LastPing" }}{{ $padding }}
26+
{{- printf "%-*s" $versionWidth "Version" }}{{ $padding }}Uptime (sec)
27+
{{ if .Instances -}}
28+
{{- range .Instances -}}
29+
{{ printf "%-*s" $hostWidth .Name }}{{ $padding }}
30+
{{- printf "%-*s" $timeWidth (.LastPing.Format "2006-01-02T15:04:05Z07:00") }}{{ $padding }}
31+
{{- printf "%-*s" $versionWidth .Version }}{{ $padding }}
32+
{{- printf "%-15d" .UpTime }}
33+
{{ end }}
34+
{{- else -}}
35+
no mongos instances found
36+
{{- end }}
37+
`

0 commit comments

Comments
 (0)