Skip to content

Commit f199e2d

Browse files
feat: Graphql client memory optimization (#325)
## Summary Do not produce byte array multiple times and close connection <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Avoids repeated byte-array allocations, uses fixed-length streaming with explicit Content-Length, and always disconnects the HTTP connection. > > - **Networking (`GraphQLClient.kt`)**: > - Optimize request construction and streaming: > - Precompute `requestBytes` (UTF-8) once; use for `Content-Length`, `setFixedLengthStreamingMode`, and output stream writes. > - Remove redundant writes/flushes and avoid multiple `toByteArray()` calls. > - Connection handling: > - Use local `connectionLocal` with a `finally` block to `disconnect()` the `HttpURLConnection`. > - Minor cleanup: > - Remove unused `Dispatchers` import. > - Consistent use of `connectionLocal` for response/error streams. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 298a739. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 72f2592 commit f199e2d

File tree

1 file changed

+13
-10
lines changed
  • sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/network

1 file changed

+13
-10
lines changed

sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/network/GraphQLClient.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.launchdarkly.observability.network
22

33
import com.launchdarkly.observability.coroutines.DispatcherProviderHolder
4-
import kotlinx.coroutines.Dispatchers
54
import kotlinx.coroutines.withContext
65
import kotlinx.serialization.KSerializer
76
import kotlinx.serialization.json.Json
@@ -74,6 +73,7 @@ class GraphQLClient(
7473
variables: Map<String, JsonElement> = emptyMap(),
7574
dataSerializer: KSerializer<T>
7675
): GraphQLResponse<T> = withContext(DispatcherProviderHolder.current.io) {
76+
var connection: HttpURLConnection? = null
7777
try {
7878
val query = loadQuery(queryFileName)
7979
val request = GraphQLRequest(
@@ -82,12 +82,13 @@ class GraphQLClient(
8282
)
8383

8484
val requestJson = json.encodeToString(request)
85-
val connection = connectionProvider.openConnection(endpoint)
85+
val requestBytes = requestJson.toByteArray(Charsets.UTF_8)
86+
val connectionLocal = connectionProvider.openConnection(endpoint).also { connection = it }
8687

87-
connection.apply {
88+
connectionLocal.apply {
8889
requestMethod = "POST"
90+
setRequestProperty("Content-Length", requestBytes.size.toString())
8991
setRequestProperty("Content-Type", "application/json")
90-
setRequestProperty("Content-Length", requestJson.toByteArray().size.toString())
9192

9293
// Add custom headers
9394
headers.forEach { (key, value) ->
@@ -97,20 +98,20 @@ class GraphQLClient(
9798
doOutput = true
9899
connectTimeout = CONNECT_TIMEOUT
99100
readTimeout = READ_TIMEOUT
101+
setFixedLengthStreamingMode(requestBytes.size)
100102
}
101103

102104
// Send request
103-
connection.outputStream.use { outputStream ->
104-
outputStream.write(requestJson.toByteArray())
105-
outputStream.flush()
105+
connectionLocal.outputStream.use { outputStream ->
106+
outputStream.write(requestBytes)
106107
}
107108

108109
// Read response
109-
val responseCode = connection.responseCode
110+
val responseCode = connectionLocal.responseCode
110111
val responseJson = if (responseCode == HttpURLConnection.HTTP_OK) {
111-
connection.inputStream.bufferedReader().use { it.readText() }
112+
connectionLocal.inputStream.bufferedReader().use { it.readText() }
112113
} else {
113-
val errorText = connection.errorStream?.bufferedReader()?.use { it.readText() } ?: "No error body"
114+
val errorText = connectionLocal.errorStream?.bufferedReader()?.use { it.readText() } ?: "No error body"
114115
throw IOException("HTTP Error $responseCode: $errorText")
115116
}
116117

@@ -125,6 +126,8 @@ class GraphQLClient(
125126
GraphQLError(message = e.message.toString())
126127
)
127128
)
129+
} finally {
130+
connection?.disconnect()
128131
}
129132
}
130133

0 commit comments

Comments
 (0)