Skip to content

Add a ConnectionListener#7456

Merged
swankjesse merged 13 commits into
square:masterfrom
yschimke:connection_listener
Jan 1, 2023
Merged

Add a ConnectionListener#7456
swankjesse merged 13 commits into
square:masterfrom
yschimke:connection_listener

Conversation

@yschimke

Copy link
Copy Markdown
Collaborator

Given that we have happy eyeballs, and are considering features like client side load balancing, having visibility into connections seems warranted.

Also specifically for Android, dealing with different networks makes this important.

@yschimke yschimke requested a review from swankjesse September 18, 2022 10:06
@Flaky // STDOUT logging enabled for test
@Timeout(30)
@Tag("Slow")
class ConnectionListenerTest {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently just a subset of EventListenerTest without too much thought of the interesting cases.

Comment thread okhttp/src/jvmMain/kotlin/okhttp3/ConnectionListener.kt Outdated
Comment thread okhttp/src/jvmMain/kotlin/okhttp3/ConnectionListener.kt
Comment thread okhttp/src/jvmMain/kotlin/okhttp3/ConnectionListener.kt Outdated

@swankjesse swankjesse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a great idea. Let’s ship it in 5.0.

open fun connectionOpened(connection: Connection) {}
open fun connectionClosed(connection: Connection) {}
open fun connectionAcquired(connection: Connection, call: Call) {}
open fun connectionReleased(connection: Connection, call: Call) {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should have successCount here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or perhaps something else where we track possibly-non-fatal exceptions, like RealConnection.trackFailure() ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep - let's add in a follow up. We should definitely expose this information here, but worth dedicated focus.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Maybe it aught to be a property on the connection.

Comment thread okhttp/src/jvmMain/kotlin/okhttp3/internal/connection/RealConnection.kt Outdated
# Conflicts:
#	okhttp/src/jvmMain/kotlin/okhttp3/internal/connection/RealRoutePlanner.kt
@yschimke yschimke changed the title [RFC] Attempt at a ConnectionListener Add a ConnectionListener Dec 29, 2022
@yschimke yschimke marked this pull request as ready for review December 29, 2022 01:59
field = value
if (!previousValue) {
queueEvent {
connectionListener.noNewExchanges(this)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the one that feels unfortunate... Relies on connection.synchronizedWithEvents

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think we should just pull up the event-notifying side-effect into the callsite

@swankjesse swankjesse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d like to find a way to avoid the event queueing. . . might not be possible? Lemme take a stab at it.

@swankjesse

Copy link
Copy Markdown
Collaborator

I think it’s possible 'cause we already plumbed through the Socket so we could close it without holding a lock.

@swankjesse swankjesse force-pushed the connection_listener branch from ab75541 to 6c1c796 Compare January 1, 2023 00:54
@swankjesse swankjesse force-pushed the connection_listener branch from 6c1c796 to e7d750b Compare January 1, 2023 01:00
logEvent(ConnectionEvent.ConnectEnd(System.nanoTime(), connection))
}

override fun connectionClosed(connection: Connection) = logEvent(ConnectionEvent.ConnectionClosed(System.nanoTime(), connection))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should assert that Start & Close events are balanced.

logEvent(ConnectionEvent.ConnectionAcquired(System.nanoTime(), connection, call))
}

override fun connectionReleased(connection: Connection, call: Call) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should assert that Acquire & Release events are balanced.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add. Probably as a fast follow.

private val handshakeCertificates = localhost()

open val fastFallback: Boolean = true
open val fastFallback: Boolean get() = true

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoids a potential problem accessing state from the superclass constructor.

"ConnectionAcquired",
"NoNewExchanges",
"ConnectionReleased",
"ConnectionClosed",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

}

private fun logEvent(e: ConnectionEvent) {
if (e.connection != null) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

/**
* Invoked when a connection is marked for no new exchanges.
*/
open fun noNewExchanges(connection: Connection) {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, with my pushed commits we don’t send this event if we’re definitely going to close the connection immediately

connection.assertThreadHoldsLock()

connections.add(connection)
// connection.queueEvent { connectionListener.connectEnd(connection) }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What to do here? I think we need a test to check symmetry before we can drop this comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I can add, unless you plan to.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets do follow-up. This PR is big enough!

Platform.get().logCloseableLeak(message, callReference.callStackTrace)

references.removeAt(i)
connection.noNewExchanges = true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very small behavior change here - if we leak a Call that doesn‘t immediately mark the connection with noNewExchanges. That’s fine.

@swankjesse swankjesse merged commit 735ed1a into square:master Jan 1, 2023
@yschimke yschimke deleted the connection_listener branch May 27, 2023 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants