This commit is contained in:
tomer doron 2019-01-14 02:00:18 -08:00
parent da46fbb762
commit 9907101ff8
3 changed files with 26 additions and 26 deletions

View File

@ -137,18 +137,18 @@ private final class CachingMetricsHandler: MetricsHandler {
} }
public func makeCounter(label: String, dimensions: [(String, String)]) -> Counter { public func makeCounter(label: String, dimensions: [(String, String)]) -> Counter {
return counters.getOrSet(label: label, dimensions:dimensions, maker: self.wrapped.makeCounter) return self.counters.getOrSet(label: label, dimensions: dimensions, maker: self.wrapped.makeCounter)
} }
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder { public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder {
let maker = { (label: String, dimensions: [(String, String)]) -> Recorder in let maker = { (label: String, dimensions: [(String, String)]) -> Recorder in
self.wrapped.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate) self.wrapped.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate)
} }
return recorders.getOrSet(label: label, dimensions:dimensions, maker: maker) return self.recorders.getOrSet(label: label, dimensions: dimensions, maker: maker)
} }
public func makeTimer(label: String, dimensions: [(String, String)]) -> Timer { public func makeTimer(label: String, dimensions: [(String, String)]) -> Timer {
return timers.getOrSet(label: label, dimensions:dimensions, maker: self.wrapped.makeTimer) return self.timers.getOrSet(label: label, dimensions: dimensions, maker: self.wrapped.makeTimer)
} }
private class Cache<T> { private class Cache<T> {
@ -157,9 +157,9 @@ private final class CachingMetricsHandler: MetricsHandler {
// once we see how real life workloads behaves // once we see how real life workloads behaves
// for example, for short opetations like hashmap lookup mutexes are worst than r/w locks in 99% reads, but better than them in mixed r/w mode // for example, for short opetations like hashmap lookup mutexes are worst than r/w locks in 99% reads, but better than them in mixed r/w mode
private let lock = Lock() private let lock = Lock()
func getOrSet(label: String, dimensions: [(String, String)], maker: (String, [(String, String)]) -> T) -> T { func getOrSet(label: String, dimensions: [(String, String)], maker: (String, [(String, String)]) -> T) -> T {
let key = self.fqn(label: label, dimensions: dimensions) let key = self.fqn(label: label, dimensions: dimensions)
return self.lock.withLock { return self.lock.withLock {
if let item = items[key] { if let item = items[key] {
return item return item
@ -170,7 +170,7 @@ private final class CachingMetricsHandler: MetricsHandler {
} }
} }
} }
private func fqn(label: String, dimensions: [(String, String)]) -> String { private func fqn(label: String, dimensions: [(String, String)]) -> String {
return [[label], dimensions.compactMap { "\($0.0).\($0.1)" }].flatMap { $0 }.joined(separator: ".") return [[label], dimensions.compactMap { "\($0.0).\($0.1)" }].flatMap { $0 }.joined(separator: ".")
} }

View File

@ -2,23 +2,23 @@ import Metrics
class SimpleMetricsLibrary: MetricsHandler { class SimpleMetricsLibrary: MetricsHandler {
init() {} init() {}
func makeCounter(label: String, dimensions: [(String, String)]) -> Counter { func makeCounter(label: String, dimensions: [(String, String)]) -> Counter {
return ExampleCounter(label, dimensions) return ExampleCounter(label, dimensions)
} }
func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder { func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder {
let maker:(String, [(String, String)]) -> Recorder = aggregate ? ExampleRecorder.init : ExampleGauge.init let maker: (String, [(String, String)]) -> Recorder = aggregate ? ExampleRecorder.init : ExampleGauge.init
return maker(label, dimensions) return maker(label, dimensions)
} }
func makeTimer(label: String, dimensions: [(String, String)]) -> Timer { func makeTimer(label: String, dimensions: [(String, String)]) -> Timer {
return ExampleTimer(label, dimensions) return ExampleTimer(label, dimensions)
} }
private class ExampleCounter: Counter { private class ExampleCounter: Counter {
init(_: String, _: [(String, String)]) {} init(_: String, _: [(String, String)]) {}
let lock = NSLock() let lock = NSLock()
var value: Int64 = 0 var value: Int64 = 0
func increment<DataType: BinaryInteger>(_ value: DataType) { func increment<DataType: BinaryInteger>(_ value: DataType) {
@ -27,16 +27,16 @@ class SimpleMetricsLibrary: MetricsHandler {
} }
} }
} }
private class ExampleRecorder: Recorder { private class ExampleRecorder: Recorder {
init(_: String, _: [(String, String)]) {} init(_: String, _: [(String, String)]) {}
private let lock = NSLock() private let lock = NSLock()
var values = [(Int64, Double)]() var values = [(Int64, Double)]()
func record<DataType: BinaryInteger>(_ value: DataType) { func record<DataType: BinaryInteger>(_ value: DataType) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record<DataType: BinaryFloatingPoint>(_ value: DataType) {
// this may loose percision, but good enough as an example // this may loose percision, but good enough as an example
let v = Double(value) let v = Double(value)
@ -49,43 +49,43 @@ class SimpleMetricsLibrary: MetricsHandler {
if 0 == self._max || v > self._max { self._max = v } if 0 == self._max || v > self._max { self._max = v }
} }
} }
var _sum: Double = 0 var _sum: Double = 0
var sum: Double { var sum: Double {
return self.lock.withLock { _sum } return self.lock.withLock { _sum }
} }
private var _count: Int = 0 private var _count: Int = 0
var count: Int { var count: Int {
return self.lock.withLock { _count } return self.lock.withLock { _count }
} }
private var _min: Double = 0 private var _min: Double = 0
var min: Double { var min: Double {
return self.lock.withLock { _min } return self.lock.withLock { _min }
} }
private var _max: Double = 0 private var _max: Double = 0
var max: Double { var max: Double {
return self.lock.withLock { _max } return self.lock.withLock { _max }
} }
} }
private class ExampleGauge: Recorder { private class ExampleGauge: Recorder {
init(_: String, _: [(String, String)]) {} init(_: String, _: [(String, String)]) {}
let lock = NSLock() let lock = NSLock()
var _value: Double = 0 var _value: Double = 0
func record<DataType: BinaryInteger>(_ value: DataType) { func record<DataType: BinaryInteger>(_ value: DataType) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record<DataType: BinaryFloatingPoint>(_ value: DataType) {
// this may loose percision but good enough as an example // this may loose percision but good enough as an example
self.lock.withLock { _value = Double(value) } self.lock.withLock { _value = Double(value) }
} }
} }
private class ExampleTimer: ExampleRecorder, Timer { private class ExampleTimer: ExampleRecorder, Timer {
func recordNanoseconds(_ duration: Int64) { func recordNanoseconds(_ duration: Int64) {
super.record(duration) super.record(duration)

View File

@ -219,7 +219,7 @@ class MetricsTests: XCTestCase {
XCTAssertEqual(counter3.label, name3, "expected label to match") XCTAssertEqual(counter3.label, name3, "expected label to match")
XCTAssertNotEqual(counter3, counter, "expected caching to work with dimensions") XCTAssertNotEqual(counter3, counter, "expected caching to work with dimensions")
} }
func testCachingWithDimensions() throws { func testCachingWithDimensions() throws {
// bootstrap with our test metrics // bootstrap with our test metrics
let metrics = TestMetrics() let metrics = TestMetrics()
@ -246,14 +246,14 @@ class MetricsTests: XCTestCase {
XCTAssertNotEqual(counter3, counter, "expected caching to work with dimensions") XCTAssertNotEqual(counter3, counter, "expected caching to work with dimensions")
// different dimensions "key" // different dimensions "key"
let name4 = name let name4 = name
let dimensions4 = dimensions.map{ ($0.0 + "-test" , $0.1) } let dimensions4 = dimensions.map { ($0.0 + "-test", $0.1) }
let counter4 = Metrics.global.makeCounter(label: name4, dimensions: dimensions4) as! TestCounter let counter4 = Metrics.global.makeCounter(label: name4, dimensions: dimensions4) as! TestCounter
XCTAssertEqual(counter4.label, name4, "expected label to match") XCTAssertEqual(counter4.label, name4, "expected label to match")
XCTAssertEqual(counter4.dimensions.description, dimensions4.description, "expected dimensions to match") XCTAssertEqual(counter4.dimensions.description, dimensions4.description, "expected dimensions to match")
XCTAssertNotEqual(counter4, counter, "expected caching to work with dimensions") XCTAssertNotEqual(counter4, counter, "expected caching to work with dimensions")
// different dimensions "value" // different dimensions "value"
let name5 = name let name5 = name
let dimensions5 = dimensions.map{ ($0.0, $0.1 + "-test") } let dimensions5 = dimensions.map { ($0.0, $0.1 + "-test") }
let counter5 = Metrics.global.makeCounter(label: name5, dimensions: dimensions5) as! TestCounter let counter5 = Metrics.global.makeCounter(label: name5, dimensions: dimensions5) as! TestCounter
XCTAssertEqual(counter5.label, name5, "expected label to match") XCTAssertEqual(counter5.label, name5, "expected label to match")
XCTAssertEqual(counter5.dimensions.description, dimensions5.description, "expected dimensions to match") XCTAssertEqual(counter5.dimensions.description, dimensions5.description, "expected dimensions to match")