File: oplog_status.go

package info (click to toggle)
prometheus-mongodb-exporter 1.0.0%2Bgit20180522.e755a44-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 668 kB
  • sloc: sh: 65; makefile: 27
file content (143 lines) | stat: -rw-r--r-- 4,230 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package collector

import (
	"time"

	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var (
	oplogStatusCount = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: Namespace,
		Subsystem: "replset_oplog",
		Name:      "items_total",
		Help:      "The total number of changes in the oplog",
	})
	oplogStatusHeadTimestamp = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: Namespace,
		Subsystem: "replset_oplog",
		Name:      "head_timestamp",
		Help:      "The timestamp of the newest change in the oplog",
	})
	oplogStatusTailTimestamp = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: Namespace,
		Subsystem: "replset_oplog",
		Name:      "tail_timestamp",
		Help:      "The timestamp of the oldest change in the oplog",
	})
	oplogStatusSizeBytes = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Namespace: Namespace,
		Subsystem: "replset_oplog",
		Name:      "size_bytes",
		Help:      "Size of oplog in bytes",
	}, []string{"type"})
)

// OplogCollectionStats represents metrics about an oplog collection
type OplogCollectionStats struct {
	Count       float64 `bson:"count"`
	Size        float64 `bson:"size"`
	StorageSize float64 `bson:"storageSize"`
}

// OplogStatus represents oplog metrics
type OplogStatus struct {
	TailTimestamp   float64
	HeadTimestamp   float64
	CollectionStats *OplogCollectionStats
}

// BsonMongoTimestampToUnix converts a mongo timestamp to UNIX time
// there's gotta be a better way to do this, but it works for now :/
func BsonMongoTimestampToUnix(timestamp bson.MongoTimestamp) float64 {
	return float64(timestamp >> 32)
}

// GetOplogTimestamp fetches the latest oplog timestamp
func GetOplogTimestamp(session *mgo.Session, returnTail bool) (float64, error) {
	sortBy := "$natural"
	if returnTail {
		sortBy = "-$natural"
	}

	var (
		err    error
		tries  int
		result struct {
			Timestamp bson.MongoTimestamp `bson:"ts"`
		}
	)
	maxTries := 2
	for tries < maxTries {
		err = session.DB("local").C("oplog.rs").Find(nil).Sort(sortBy).Limit(1).One(&result)
		if err != nil {
			tries++
			time.Sleep(500 * time.Millisecond)
		} else {
			return BsonMongoTimestampToUnix(result.Timestamp), err
		}
	}

	return 0, err
}

// GetOplogCollectionStats fetches oplog collection stats
func GetOplogCollectionStats(session *mgo.Session) (*OplogCollectionStats, error) {
	results := &OplogCollectionStats{}
	err := session.DB("local").Run(bson.M{"collStats": "oplog.rs"}, &results)
	return results, err
}

// Export exports metrics to Prometheus
func (status *OplogStatus) Export(ch chan<- prometheus.Metric) {
	oplogStatusSizeBytes.WithLabelValues("current").Set(0)
	oplogStatusSizeBytes.WithLabelValues("storage").Set(0)
	if status.CollectionStats != nil {
		oplogStatusCount.Set(status.CollectionStats.Count)
		oplogStatusSizeBytes.WithLabelValues("current").Set(status.CollectionStats.Size)
		oplogStatusSizeBytes.WithLabelValues("storage").Set(status.CollectionStats.StorageSize)
	}
	if status.HeadTimestamp != 0 && status.TailTimestamp != 0 {
		oplogStatusHeadTimestamp.Set(status.HeadTimestamp)
		oplogStatusTailTimestamp.Set(status.TailTimestamp)
	}

	oplogStatusCount.Collect(ch)
	oplogStatusHeadTimestamp.Collect(ch)
	oplogStatusTailTimestamp.Collect(ch)
	oplogStatusSizeBytes.Collect(ch)
}

// Describe describes metrics collected
func (status *OplogStatus) Describe(ch chan<- *prometheus.Desc) {
	oplogStatusCount.Describe(ch)
	oplogStatusHeadTimestamp.Describe(ch)
	oplogStatusTailTimestamp.Describe(ch)
	oplogStatusSizeBytes.Describe(ch)
}

// GetOplogStatus fetches oplog collection stats
func GetOplogStatus(session *mgo.Session) *OplogStatus {
	oplogStatus := &OplogStatus{}
	collectionStats, err := GetOplogCollectionStats(session)
	if err != nil {
		glog.Error("Failed to get local.oplog_rs collection stats.")
		return nil
	}

	headTimestamp, err := GetOplogTimestamp(session, false)
	tailTimestamp, err := GetOplogTimestamp(session, true)
	if err != nil {
		glog.Error("Failed to get oplog head or tail timestamps.")
		return nil
	}

	oplogStatus.CollectionStats = collectionStats
	oplogStatus.HeadTimestamp = headTimestamp
	oplogStatus.TailTimestamp = tailTimestamp

	return oplogStatus
}