-
Notifications
You must be signed in to change notification settings - Fork 16
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
Rewrite streaming API to use websocket #383
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #383 +/- ##
============================================
+ Coverage 48.33% 50.20% +1.86%
- Complexity 541 565 +24
============================================
Files 140 143 +3
Lines 3906 3928 +22
Branches 259 253 -6
============================================
+ Hits 1888 1972 +84
+ Misses 1825 1740 -85
- Partials 193 216 +23
|
23e7ecc
to
0fb76ce
Compare
0525c33
to
9df9c23
Compare
fdafe91
to
5af79ca
Compare
5af79ca
to
47eef95
Compare
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.
Looks great already, much better than before. I've added some comments, mostly regarding documentation.
bigbone/src/main/kotlin/social/bigbone/api/method/StreamingMethods.kt
Outdated
Show resolved
Hide resolved
bigbone/src/main/kotlin/social/bigbone/api/entity/streaming/ParsedStreamEvent.kt
Outdated
Show resolved
Hide resolved
bigbone/src/main/kotlin/social/bigbone/api/entity/streaming/StreamType.kt
Outdated
Show resolved
Hide resolved
@bocops In the Should we nudge users toward using Should we throw an error if users call |
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.
Looks good! :)
@bocops Any opinion on my comment above? 🤓 |
I wonder if I've not used the streaming API so far, so I don't know if increasing this value is strictly necessary. Do you? If it is, we should keep a function like this, but probably(?) also advise users against using the same client to access streaming and non-streaming APIs? In this case, we could throw an error as you suggest. If it is not necessary, we could also remove the function, but advise users to use |
Probably, yes.
Not entirely true: We also get the streaming URL from the Instance API on MastodonClient instantiation. If users do not call So if we were to remove the
I don’t know either, no. But I can find out. 👍 Ideally, I’d like to find a solution that works in all cases for all users. In the worst case, we could also use per-call settings that would differ from our usual settings, and apply them only to those stream calls. |
Sorry, I was quickly looking at the current state instead of your changes, and thus missed the URL retrieval. I understand that you don't want to tackle #114 here, but it might still be useful to think about how If, in the long term, we do want to get instance information whenever a client is built, then the streaming URL will always be available without additional overhead, and there would again be less need for a |
bigbone/src/main/kotlin/social/bigbone/api/WebSocketCallback.kt
Outdated
Show resolved
Hide resolved
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.
Thanks, this is a real improvement! 👍
- Gets the streaming URL for every MastodonClient instance - Sets the pingInterval instead of a timeout, but only for stream calls
@andregasser @bocops Having read square/okhttp#3197 and square/okhttp#3227 and https://square.github.io/okhttp/3.x/okhttp/index.html?okhttp3/OkHttpClient.Builder.html#pingInterval-long-java.util.concurrent.TimeUnit-, I think we should be able to go without the read timeout increase and instead set the |
I think this is a good way to go 👍 It's simpler and cleaner. Looking forward to do some manual testing with this. |
Description
In this PR, I improve the streaming API:
Closes #87.
Streaming URL
Some servers may use different streaming URLs than the regular instance URL. For that reason, I need to parse the
Instance
info duringMastodonClient
build.I have seen #114 but decided not to go that route here yet. We should rewrite the
MastodonClient
builder as part of that ticket though!Instead, if
useStreamingApi
is called as part of the builder, I retrieve the instance information—depending on API version—and get the streaming URL from it. For that, I did not reuse theInstance
data classes for each version, nor the (fallback) code to retrieve the Instance info’s instance version:bigbone/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
Lines 741 to 755 in 3bc604d
I opted for raw JSON parsing to hopefully keep the memory footprint a bit lower. I’m not sure if it impacts anything, though. I haven’t tested that.
There’s definitely room for improvement for the instance info retrieval based on API version. Since we will likely remove support for v1 (Mastodon below version 4) very soon anyway, I have not spent much more time to fix this though.
okhttp shortcomings
okhttp doesn’t like
ws
orwss
schemes so we need to replace them withhttp
andhttps
. Otherwise,HttpUrl
will immediately fail as soon as we supply the websocket version to thescheme
portion of its builder. 👀EventType polymorphism
The event payload we receive for each web socket message can look as follows:
https://docs.joinmastodon.org/methods/streaming/#events-11
event
, which should always be present, holds the event type. Depending on the event type,payload
may or may not be present and represents the data related to the event type. For example fordelete
, it would hold the ID of the status that had been deleted. Forupdate
, it would contain the JSON representation of aStatus
created.I have tried to use kotlinx.serialization’s polymorphism feature but failed because I would have needed to unescape (we get an escaped payload that may contain JSON or just a plain string) it first. I did not find a way to get that working. 😞
For that reason, I introduced the
RawStreamEvent
class and theParsedStreamEvent
sealed interface with implementations for each event type. For each web socket message, before calling the callback, I call theRawStreamEvent.toStreamEvent
extension function which then takes the string payload and parses it to any of theParsedStreamEvent
-implementing data objects/classes, depending onevent
type.Type of Change
Breaking Changes
With the replacement came loads of breaking changes. I’ve replaced the previous
Handler
andShutdownable
with a leaner callback and an extension ofCloseable
, so now the signatures, while similar, are actually quite different for callers.It’s no longer necessary (or even possible) to call
useStreamingApi
when building theMastodonClient
.How Has This Been Tested?
I have tried to get okhttp’s
mockwebserver
and also fabric8io’s improved variant to run, but failed, unfortunately.Initially, I had wanted to implement tests that would emulate a server serving a websocket connection so that I could test multiple different scenario.
As that didn’t work out, I implemented the tests using mockk like we do with all other endpoint tests.
Mandatory Checklist
gradle check
and there were no errors reportedOptional Things To Check
The items below are some more things to check before asking other people to review your code.
/docs
folder (e.g. API Coverage page)?