-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support for OkHttp thread metrics #1022
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,8 +25,13 @@ public object HttpClientMetricAttributes { | |
public val AcquiredConnection: Attributes = attributesOf { "state" to "acquired" } | ||
public val QueuedRequest: Attributes = attributesOf { "state" to "queued" } | ||
public val InFlightRequest: Attributes = attributesOf { "state" to "in-flight" } | ||
public val TotalThread: Attributes = attributesOf { "state" to "total" } | ||
public val ActiveThread: Attributes = attributesOf { "state" to "active" } | ||
} | ||
|
||
@InternalApi | ||
public data class ThreadState(val total: Long, val active: Long) | ||
|
||
/** | ||
* Container for common HTTP engine related metrics. Engine implementations can re-use this and update | ||
* the various fields in whatever manner fits best (increment/decrement vs current absolute value). | ||
|
@@ -39,6 +44,7 @@ public object HttpClientMetricAttributes { | |
public class HttpClientMetrics( | ||
scope: String, | ||
public val provider: TelemetryProvider, | ||
threadStateCallback: (() -> ThreadState?)? = null, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems different for no apparent reason. It's not wrong I guess but feels odd that we would special case it since each engine can adapt however they want to the current way metrics are exposed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I concede it's very different from what we have elsewhere and feels very specialized right now. I think one problem is that we don't directly control the thread pool. We don't decide when new threads are created or when they swap from idle to active. All we can do is inspect the state at any given moment to know the current value. That sounds like what an We could start a background thread in the OkHttp engine to periodically inspect the thread pool and stuff the idle/active counts into Is your greater concern that this change exposes the concept of callbacks outside of |
||
) : Closeable { | ||
private val meter = provider.meterProvider.getOrCreateMeter(scope) | ||
|
||
|
@@ -76,34 +82,6 @@ public class HttpClientMetrics( | |
"The amount of time a connection has been open", | ||
) | ||
|
||
private val connectionLimitHandle = meter.createAsyncUpDownCounter( | ||
"smithy.client.http.connections.limit", | ||
{ it.record(_connectionsLimit.value) }, | ||
"{connection}", | ||
"Max connections configured for the HTTP client", | ||
) | ||
|
||
private val connectionUsageHandle = meter.createAsyncUpDownCounter( | ||
"smithy.client.http.connections.usage", | ||
::recordConnectionState, | ||
"{connection}", | ||
"Current state of connections (idle, acquired)", | ||
) | ||
|
||
private val requestsConcurrencyLimitHandle = meter.createAsyncUpDownCounter( | ||
"smithy.client.http.requests.limit", | ||
{ it.record(_requestConcurrencyLimit.value) }, | ||
"{request}", | ||
"Max concurrent requests configured for the HTTP client", | ||
) | ||
|
||
private val requestsHandle = meter.createAsyncUpDownCounter( | ||
"smithy.client.http.requests.usage", | ||
::recordRequestsState, | ||
"{request}", | ||
"The current state of HTTP client request concurrency (queued, in-flight)", | ||
) | ||
|
||
public val bytesSent: MonotonicCounter = meter.createMonotonicCounter( | ||
"smithy.client.http.bytes_sent", | ||
"By", | ||
|
@@ -122,6 +100,41 @@ public class HttpClientMetrics( | |
"The amount of time after a request has been sent spent waiting on a response from the remote server", | ||
) | ||
|
||
private val asyncHandles = listOfNotNull( | ||
meter.createAsyncUpDownCounter( | ||
"smithy.client.http.connections.limit", | ||
{ it.record(_connectionsLimit.value) }, | ||
"{connection}", | ||
"Max connections configured for the HTTP client", | ||
), | ||
meter.createAsyncUpDownCounter( | ||
"smithy.client.http.connections.usage", | ||
::recordConnectionState, | ||
"{connection}", | ||
"Current state of connections (idle, acquired)", | ||
), | ||
meter.createAsyncUpDownCounter( | ||
"smithy.client.http.requests.limit", | ||
{ it.record(_requestConcurrencyLimit.value) }, | ||
"{request}", | ||
"Max concurrent requests configured for the HTTP client", | ||
), | ||
meter.createAsyncUpDownCounter( | ||
"smithy.client.http.requests.usage", | ||
::recordRequestsState, | ||
"{request}", | ||
"The current state of HTTP client request concurrency (queued, in-flight)", | ||
), | ||
threadStateCallback?.let { callback -> | ||
meter.createAsyncUpDownCounter( | ||
"smithy.client.http.threads.count", | ||
{ measurement -> recordThreadState(measurement, callback) }, | ||
"{thread}", | ||
"Current state of HTTP engine threads (active, running)", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How are "running" threads different from "active" ones? I see only "total" and "active" are recorded. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, "active" and "running" are not the correct terms here. I can change those to "total" and "active". |
||
) | ||
}, | ||
) | ||
|
||
/** | ||
* The maximum number of connections configured for the client | ||
*/ | ||
|
@@ -186,13 +199,17 @@ public class HttpClientMetrics( | |
measurement.record(acquiredConnections, HttpClientMetricAttributes.AcquiredConnection) | ||
} | ||
|
||
private fun recordThreadState(measurement: LongAsyncMeasurement, callback: () -> ThreadState?) { | ||
callback()?.let { threadState -> | ||
measurement.record(threadState.total, HttpClientMetricAttributes.TotalThread) | ||
measurement.record(threadState.active, HttpClientMetricAttributes.ActiveThread) | ||
} | ||
} | ||
|
||
override fun close() { | ||
val exceptions = listOf( | ||
runCatching(connectionLimitHandle::stop), | ||
runCatching(connectionUsageHandle::stop), | ||
runCatching(requestsHandle::stop), | ||
runCatching(requestsConcurrencyLimitHandle::stop), | ||
).mapNotNull(Result<*>::exceptionOrNull) | ||
val exceptions = asyncHandles | ||
.map { handle -> runCatching { handle.stop() } } | ||
.mapNotNull(Result<*>::exceptionOrNull) | ||
|
||
exceptions.firstOrNull()?.let { first -> | ||
exceptions.drop(1).forEach(first::addSuppressed) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correctness: The state attribute for this should probably follow the usage convention where the sub states total to some limit.
e.g. thread state usage metric
smithy.client.http.threads.usage
with states ofidle | consumed
and a separate metric for the limitsmithy.client.http.threads.limit
.Also I suppose we should probably get this defined in the SRA before codifying it in an implementation.
We may not know the total thread limit in every case or thread pools may be dynamic and spin down threads which would break the "should" part of the convention but I think that's fine. In the ideal case the convention applies and even when it doesn't it's still how you'd want it graphed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'll change this to idle/active threads.