Skip to content

Commit b31a6e6

Browse files
authored
Finer concurrency in 'MetricType.observe' / 'MetricType.collect' (#59)
* finer concurrency when collecting metrics * better concurrency for 'PromHistogram.observe' * make subHistograms a map
1 parent 7e0cf8f commit b31a6e6

File tree

5 files changed

+139
-135
lines changed

5 files changed

+139
-135
lines changed

Sources/Prometheus/MetricTypes/Counter.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,24 @@ public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, Pr
4848
/// - Returns:
4949
/// Newline separated Prometheus formatted metric string
5050
public func collect() -> String {
51-
return self.lock.withLock {
52-
var output = [String]()
53-
54-
if let help = self.help {
55-
output.append("# HELP \(self.name) \(help)")
56-
}
57-
output.append("# TYPE \(self.name) \(self._type)")
58-
59-
output.append("\(self.name) \(self.value)")
60-
61-
self.metrics.forEach { (labels, value) in
62-
let labelsString = encodeLabels(labels)
63-
output.append("\(self.name)\(labelsString) \(value)")
64-
}
65-
66-
return output.joined(separator: "\n")
51+
let (value, metrics) = self.lock.withLock {
52+
(self.value, self.metrics)
53+
}
54+
var output = [String]()
55+
56+
if let help = self.help {
57+
output.append("# HELP \(self.name) \(help)")
6758
}
59+
output.append("# TYPE \(self.name) \(self._type)")
60+
61+
output.append("\(self.name) \(value)")
62+
63+
metrics.forEach { (labels, value) in
64+
let labelsString = encodeLabels(labels)
65+
output.append("\(self.name)\(labelsString) \(value)")
66+
}
67+
68+
return output.joined(separator: "\n")
6869
}
6970

7071
/// Increments the Counter
@@ -104,4 +105,3 @@ public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, Pr
104105
}
105106
}
106107
}
107-

Sources/Prometheus/MetricTypes/Gauge.swift

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,24 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
5151
/// - Returns:
5252
/// Newline separated Prometheus formatted metric string
5353
public func collect() -> String {
54-
return self.lock.withLock {
55-
var output = [String]()
56-
57-
if let help = self.help {
58-
output.append("# HELP \(self.name) \(help)")
59-
}
60-
output.append("# TYPE \(self.name) \(self._type)")
61-
62-
output.append("\(self.name) \(self.value)")
63-
64-
self.metrics.forEach { (labels, value) in
65-
let labelsString = encodeLabels(labels)
66-
output.append("\(self.name)\(labelsString) \(value)")
67-
}
68-
69-
return output.joined(separator: "\n")
54+
let (value, metrics) = self.lock.withLock {
55+
(self.value, self.metrics)
56+
}
57+
var output = [String]()
58+
59+
if let help = self.help {
60+
output.append("# HELP \(self.name) \(help)")
7061
}
62+
output.append("# TYPE \(self.name) \(self._type)")
63+
64+
output.append("\(self.name) \(value)")
65+
66+
metrics.forEach { (labels, value) in
67+
let labelsString = encodeLabels(labels)
68+
output.append("\(self.name)\(labelsString) \(value)")
69+
}
70+
71+
return output.joined(separator: "\n")
7172
}
7273

7374
/// Sets the Gauge to the current unix-time in seconds

Sources/Prometheus/MetricTypes/Histogram.swift

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ extension HistogramLabels {
8383
public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels>: PromMetric, PrometheusHandled {
8484
/// Prometheus instance that created this Histogram
8585
internal weak var prometheus: PrometheusClient?
86-
86+
8787
/// Name of this Histogram, required
8888
public let name: String
8989
/// Help text of this Histogram, optional
@@ -99,10 +99,10 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
9999
internal let upperBounds: [Double]
100100

101101
/// Labels for this Histogram
102-
internal private(set) var labels: Labels
102+
internal let labels: Labels
103103

104104
/// Sub Histograms for this Histogram
105-
fileprivate var subHistograms: [PromHistogram<NumType, Labels>] = []
105+
fileprivate var subHistograms: [Labels: PromHistogram<NumType, Labels>] = [:]
106106

107107
/// Total value of the Histogram
108108
private let sum: PromCounter<NumType, EmptyLabels>
@@ -121,9 +121,9 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
121121
internal init(_ name: String, _ help: String? = nil, _ labels: Labels = Labels(), _ buckets: Buckets = .defaultBuckets, _ p: PrometheusClient) {
122122
self.name = name
123123
self.help = help
124-
124+
125125
self.prometheus = p
126-
126+
127127
self.sum = .init("\(self.name)_sum", nil, 0, p)
128128

129129
self.labels = labels
@@ -142,68 +142,76 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
142142
/// - Returns:
143143
/// Newline separated Prometheus formatted metric string
144144
public func collect() -> String {
145-
return self.lock.withLock {
146-
var output = [String]()
147-
148-
if let help = self.help {
149-
output.append("# HELP \(self.name) \(help)")
150-
}
151-
output.append("# TYPE \(self.name) \(self._type)")
152-
153-
var acc: NumType = 0
154-
for (i, bound) in self.upperBounds.enumerated() {
155-
acc += self.buckets[i].get()
156-
self.labels.le = bound.description
157-
let labelsString = encodeLabels(self.labels)
158-
output.append("\(self.name)_bucket\(labelsString) \(acc)")
159-
}
160-
161-
let labelsString = encodeLabels(self.labels, ["le"])
162-
output.append("\(self.name)_count\(labelsString) \(acc)")
163-
164-
output.append("\(self.name)_sum\(labelsString) \(self.sum.get())")
165-
166-
self.subHistograms.forEach { subHistogram in
167-
var acc: NumType = 0
168-
for (i, bound) in subHistogram.upperBounds.enumerated() {
169-
acc += subHistogram.buckets[i].get()
170-
subHistogram.labels.le = bound.description
171-
let labelsString = encodeLabels(subHistogram.labels)
172-
output.append("\(subHistogram.name)_bucket\(labelsString) \(acc)")
173-
}
174-
175-
let labelsString = encodeLabels(subHistogram.labels, ["le"])
176-
output.append("\(subHistogram.name)_count\(labelsString) \(acc)")
177-
178-
output.append("\(subHistogram.name)_sum\(labelsString) \(subHistogram.sum.get())")
179-
180-
subHistogram.labels.le = ""
145+
let (buckets, subHistograms, labels) = self.lock.withLock {
146+
(self.buckets, self.subHistograms, self.labels)
147+
}
148+
149+
var output = [String]()
150+
151+
if let help = self.help {
152+
output.append("# HELP \(self.name) \(help)")
153+
}
154+
output.append("# TYPE \(self.name) \(self._type)")
155+
collectBuckets(buckets: buckets,
156+
upperBounds: self.upperBounds,
157+
name: self.name,
158+
labels: labels,
159+
sum: self.sum.get(),
160+
into: &output)
161+
162+
subHistograms.forEach { subHistogram in
163+
let (subHistogramBuckets, subHistogramLabels) = self.lock.withLock {
164+
(subHistogram.value.buckets, subHistogram.value.labels)
181165
}
182-
183-
self.labels.le = ""
184-
185-
return output.joined(separator: "\n")
166+
collectBuckets(buckets: subHistogramBuckets,
167+
upperBounds: subHistogram.value.upperBounds,
168+
name: subHistogram.value.name,
169+
labels: subHistogramLabels,
170+
sum: subHistogram.value.sum.get(),
171+
into: &output)
186172
}
173+
return output.joined(separator: "\n")
187174
}
188-
175+
176+
private func collectBuckets(buckets: [PromCounter<NumType, EmptyLabels>],
177+
upperBounds: [Double],
178+
name: String,
179+
labels: Labels,
180+
sum: NumType,
181+
into output: inout [String]) {
182+
var labels = labels
183+
var acc: NumType = 0
184+
for (i, bound) in upperBounds.enumerated() {
185+
acc += buckets[i].get()
186+
labels.le = bound.description
187+
let labelsString = encodeLabels(labels)
188+
output.append("\(name)_bucket\(labelsString) \(acc)")
189+
}
190+
191+
let labelsString = encodeLabels(labels, ["le"])
192+
output.append("\(name)_count\(labelsString) \(acc)")
193+
194+
output.append("\(name)_sum\(labelsString) \(sum)")
195+
}
196+
189197
/// Observe a value
190198
///
191199
/// - Parameters:
192200
/// - value: Value to observe
193201
/// - labels: Labels to attach to the observed value
194202
public func observe(_ value: NumType, _ labels: Labels? = nil) {
195-
self.lock.withLock {
196-
if let labels = labels, type(of: labels) != type(of: EmptySummaryLabels()) {
197-
guard let his = self.prometheus?.getOrCreateHistogram(with: labels, for: self) else { fatalError("Lingering Histogram") }
198-
his.observe(value)
203+
if let labels = labels, type(of: labels) != type(of: EmptySummaryLabels()) {
204+
let his: PromHistogram<NumType, Labels> = self.lock.withLock {
205+
self.getOrCreateHistogram(with: labels)
199206
}
200-
self.sum.inc(value)
201-
202-
for (i, bound) in self.upperBounds.enumerated() {
203-
if bound >= value.doubleValue {
204-
self.buckets[i].inc()
205-
return
206-
}
207+
his.observe(value)
208+
}
209+
self.sum.inc(value)
210+
211+
for (i, bound) in self.upperBounds.enumerated() {
212+
if bound >= value.doubleValue {
213+
self.buckets[i].inc()
214+
return
207215
}
208216
}
209217
}
@@ -222,21 +230,16 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
222230
}
223231
return try body()
224232
}
225-
}
226233

227-
extension PrometheusClient {
228234
/// Helper for histograms & labels
229-
fileprivate func getOrCreateHistogram<T: Numeric, U: HistogramLabels>(with labels: U, for histogram: PromHistogram<T, U>) -> PromHistogram<T, U> {
230-
let histograms = histogram.subHistograms.filter { (metric) -> Bool in
231-
guard metric.name == histogram.name, metric.help == histogram.help, metric.labels == labels else { return false }
232-
return true
233-
}
234-
if histograms.count > 2 { fatalError("Somehow got 2 histograms with the same data type") }
235-
if let histogram = histograms.first {
235+
fileprivate func getOrCreateHistogram(with labels: Labels) -> PromHistogram<NumType, Labels> {
236+
let histogram = self.subHistograms[labels]
237+
if let histogram = histogram, histogram.name == self.name, histogram.help == self.help {
236238
return histogram
237239
} else {
238-
let newHistogram = PromHistogram<T, U>(histogram.name, histogram.help, labels, Buckets(histogram.upperBounds), self)
239-
histogram.subHistograms.append(newHistogram)
240+
guard let prometheus = prometheus else { fatalError("Lingering Histogram") }
241+
let newHistogram = PromHistogram(self.name, self.help, labels, Buckets(self.upperBounds), prometheus)
242+
self.subHistograms[labels] = newHistogram
240243
return newHistogram
241244
}
242245
}

Sources/Prometheus/MetricTypes/Summary.swift

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -95,43 +95,44 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
9595
/// - Returns:
9696
/// Newline separated Prometheus formatted metric string
9797
public func collect() -> String {
98-
return self.lock.withLock {
99-
var output = [String]()
100-
101-
if let help = self.help {
102-
output.append("# HELP \(self.name) \(help)")
103-
}
104-
output.append("# TYPE \(self.name) \(self._type)")
98+
let (subSummaries, values) = lock.withLock {
99+
(self.subSummaries, self.values)
100+
}
101+
102+
var output = [String]()
103+
104+
if let help = self.help {
105+
output.append("# HELP \(self.name) \(help)")
106+
}
107+
output.append("# TYPE \(self.name) \(self._type)")
108+
var labels = self.labels
109+
calculateQuantiles(quantiles: self.quantiles, values: values.map { $0.doubleValue }).sorted { $0.key < $1.key }.forEach { (arg) in
110+
let (q, v) = arg
111+
labels.quantile = "\(q)"
112+
let labelsString = encodeLabels(labels)
113+
output.append("\(self.name)\(labelsString) \(format(v))")
114+
}
115+
116+
let labelsString = encodeLabels(labels, ["quantile"])
117+
output.append("\(self.name)_count\(labelsString) \(self.count.get())")
118+
output.append("\(self.name)_sum\(labelsString) \(format(self.sum.get().doubleValue))")
105119

106-
calculateQuantiles(quantiles: self.quantiles, values: self.values.map { $0.doubleValue }).sorted { $0.key < $1.key }.forEach { (arg) in
120+
subSummaries.forEach { subSum in
121+
var subSumLabels = subSum.labels
122+
let subSumValues = lock.withLock { subSum.values }
123+
calculateQuantiles(quantiles: self.quantiles, values: subSumValues.map { $0.doubleValue }).sorted { $0.key < $1.key }.forEach { (arg) in
107124
let (q, v) = arg
108-
self.labels.quantile = "\(q)"
109-
let labelsString = encodeLabels(self.labels)
110-
output.append("\(self.name)\(labelsString) \(format(v))")
111-
}
112-
113-
let labelsString = encodeLabels(self.labels, ["quantile"])
114-
output.append("\(self.name)_count\(labelsString) \(self.count.get())")
115-
output.append("\(self.name)_sum\(labelsString) \(format(self.sum.get().doubleValue))")
116-
117-
self.subSummaries.forEach { subSum in
118-
calculateQuantiles(quantiles: self.quantiles, values: subSum.values.map { $0.doubleValue }).sorted { $0.key < $1.key }.forEach { (arg) in
119-
let (q, v) = arg
120-
subSum.labels.quantile = "\(q)"
121-
let labelsString = encodeLabels(subSum.labels)
122-
output.append("\(subSum.name)\(labelsString) \(format(v))")
123-
}
124-
125-
let labelsString = encodeLabels(subSum.labels, ["quantile"])
126-
output.append("\(subSum.name)_count\(labelsString) \(subSum.count.get())")
127-
output.append("\(subSum.name)_sum\(labelsString) \(format(subSum.sum.get().doubleValue))")
128-
subSum.labels.quantile = ""
125+
subSumLabels.quantile = "\(q)"
126+
let labelsString = encodeLabels(subSumLabels)
127+
output.append("\(subSum.name)\(labelsString) \(format(v))")
129128
}
130-
131-
self.labels.quantile = ""
132-
133-
return output.joined(separator: "\n")
129+
130+
let labelsString = encodeLabels(subSumLabels, ["quantile"])
131+
output.append("\(subSum.name)_count\(labelsString) \(subSum.count.get())")
132+
output.append("\(subSum.name)_sum\(labelsString) \(format(subSum.sum.get().doubleValue))")
134133
}
134+
135+
return output.joined(separator: "\n")
135136
}
136137

137138
// Updated for SwiftMetrics 2.0 to be unit agnostic if displayUnit is set or default to nanoseconds.

Sources/Prometheus/Prometheus.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,3 @@ public enum PrometheusError: Error {
290290
/// but there was no `PrometheusClient` bootstrapped
291291
case prometheusFactoryNotBootstrapped(bootstrappedWith: String)
292292
}
293-

0 commit comments

Comments
 (0)