Skip to content

Commit 55df245

Browse files
committed
Use std allocator
1 parent 0cbccaf commit 55df245

File tree

4 files changed

+99
-16
lines changed

4 files changed

+99
-16
lines changed

build.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,12 @@ plugins {
99
allprojects {
1010
group = "io.github.trueangle"
1111
version = "0.0.2"
12-
}
12+
}
13+
/*
14+
15+
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
16+
compilerOptions {
17+
freeCompilerArgs.add("-Xallocator=std")
18+
freeCompilerArgs.add("-Xruntime-logs=gc=info")
19+
}
20+
}*/

lambda-runtime/build.gradle.kts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dev.mokkery.MockMode
2+
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
23

34
plugins {
45
alias(libs.plugins.kotlin.multiplatform)
@@ -9,10 +10,23 @@ plugins {
910
}
1011

1112
kotlin {
12-
macosArm64()
13+
macosArm64 {
14+
binaries {
15+
getTest(NativeBuildType.DEBUG).freeCompilerArgs += listOf("-Xruntime-logs=gc=info", "-Xallocator=std")
16+
executable {
17+
freeCompilerArgs += listOf("-Xallocator=std")
18+
}
19+
}
20+
}
1321
macosX64()
1422
//linuxArm64() // https://youtrack.jetbrains.com/issue/KT-36871/Support-Aarch64-Linux-as-a-host-for-the-Kotlin-Native
15-
linuxX64()
23+
linuxX64 {
24+
binaries {
25+
executable {
26+
freeCompilerArgs += listOf("-Xruntime-logs=gc=info", "-Xallocator=std")
27+
}
28+
}
29+
}
1630

1731
sourceSets {
1832
commonMain.dependencies {

lambda-runtime/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/api/LambdaRuntimeClient.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import io.ktor.client.statement.HttpResponse
2121
import io.ktor.http.HttpStatusCode
2222
import io.ktor.http.content.OutgoingContent
2323
import io.ktor.http.contentType
24+
import io.ktor.http.isSuccess
2425
import io.ktor.util.reflect.TypeInfo
2526
import kotlinx.serialization.json.Json
2627
import kotlin.time.Duration.Companion.minutes
@@ -53,6 +54,11 @@ internal class LambdaClientImpl(private val httpClient: HttpClient): LambdaClien
5354
exponentialDelay()
5455
}
5556
}
57+
58+
if(!response.status.isSuccess()) {
59+
throw NonRecoverableStateException(message = "Lambda environment is unavailable. Can't retrieve an event. Terminating")
60+
}
61+
5662
val context = contextFromResponseHeaders(response)
5763
val body = try {
5864
response.body(bodyType) as T

lambda-runtime/src/nativeTest/kotlin/io/github/trueangle/knative/lambda/runtime/LambdaRuntimeTest.kt

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import dev.mokkery.verify.VerifyMode.Companion.exactly
1111
import dev.mokkery.verify.VerifyMode.Companion.not
1212
import dev.mokkery.verify.VerifyMode.Companion.order
1313
import dev.mokkery.verifySuspend
14-
import io.github.trueangle.knative.lambda.runtime.LambdaEnvironmentException.*
14+
import io.github.trueangle.knative.lambda.runtime.LambdaEnvironmentException.BadRequestException
15+
import io.github.trueangle.knative.lambda.runtime.LambdaEnvironmentException.NonRecoverableStateException
1516
import io.github.trueangle.knative.lambda.runtime.LambdaRuntimeException.Invocation.EventBodyParseException
1617
import io.github.trueangle.knative.lambda.runtime.LambdaRuntimeException.Invocation.HandlerException
1718
import io.github.trueangle.knative.lambda.runtime.ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_FUNCTION_NAME
@@ -33,12 +34,8 @@ import io.ktor.client.engine.mock.MockRequestHandleScope
3334
import io.ktor.client.engine.mock.respond
3435
import io.ktor.client.engine.mock.respondBadRequest
3536
import io.ktor.client.engine.mock.respondError
36-
import io.ktor.client.engine.mock.respondOk
37-
import io.ktor.client.request.HttpRequestData
3837
import io.ktor.http.HttpHeaders
3938
import io.ktor.http.HttpStatusCode
40-
import io.ktor.http.content.ChannelWriterContent
41-
import io.ktor.http.content.OutgoingContent
4239
import io.ktor.http.headers
4340
import io.ktor.http.headersOf
4441
import io.ktor.util.reflect.typeInfo
@@ -48,16 +45,15 @@ import io.ktor.utils.io.copyTo
4845
import kotlinx.cinterop.ExperimentalForeignApi
4946
import kotlinx.cinterop.toKString
5047
import kotlinx.coroutines.test.runTest
51-
import kotlinx.io.Buffer
52-
import kotlinx.io.RawSource
53-
import kotlinx.io.Source
5448
import kotlinx.serialization.json.Json
5549
import platform.posix.getenv
5650
import platform.posix.setenv
51+
import kotlin.experimental.ExperimentalNativeApi
52+
import kotlin.native.runtime.GC
53+
import kotlin.native.runtime.NativeRuntimeApi
5754
import kotlin.test.BeforeTest
5855
import kotlin.test.Test
5956
import kotlin.test.assertFailsWith
60-
import kotlin.test.assertTrue
6157

6258
internal const val RESOURCES_PATH = "src/nativeTest/resources"
6359

@@ -197,10 +193,7 @@ class LambdaRuntimeTest {
197193
val lambdaRunner = createRunner(MockEngine { request ->
198194
val path = request.url.encodedPath
199195
when {
200-
path.contains("/invocation/next") -> {
201-
respondError(HttpStatusCode.InternalServerError)
202-
}
203-
196+
path.contains("/invocation/next") -> respondError(HttpStatusCode.InternalServerError)
204197
else -> respondError(HttpStatusCode.Forbidden)
205198
}
206199
})
@@ -368,6 +361,68 @@ class LambdaRuntimeTest {
368361
verify(not) { lambdaRunner.env.terminate() }
369362
}
370363

364+
@OptIn(NativeRuntimeApi::class, ExperimentalStdlibApi::class, ExperimentalNativeApi::class)
365+
@Test
366+
fun `Validate leaks`() = runTest {
367+
val invocationCount = 3
368+
var invocationIndex = 0
369+
val events = buildList(invocationCount) {
370+
repeat(invocationCount) { add("Hello world") }
371+
}
372+
373+
val lambdaRunner = createRunner(MockEngine { request ->
374+
val path = request.url.encodedPath
375+
when {
376+
path.contains("invocation/next") -> {
377+
if (invocationIndex >= invocationCount) {
378+
respondError(HttpStatusCode.InternalServerError, headers = headers {
379+
append(HttpHeaders.ContentType, "application/json")
380+
append("Lambda-Runtime-Aws-Request-Id", context.awsRequestId)
381+
append("Lambda-Runtime-Deadline-Ms", context.deadlineTimeInMs.toString())
382+
append("Lambda-Runtime-Invoked-Function-Arn", context.invokedFunctionArn)
383+
})
384+
} else {
385+
respondNextEventSuccess(events[invocationIndex++])
386+
}
387+
}
388+
389+
path.contains("/invocation/${context.awsRequestId}/response") -> respond(
390+
content = ByteReadChannel("Ok"),
391+
status = HttpStatusCode.Accepted,
392+
headers = headersOf(HttpHeaders.ContentType, "application/json")
393+
)
394+
395+
else -> respondBadRequest()
396+
}
397+
})
398+
399+
val handler = object : LambdaBufferedHandler<String, String> {
400+
override suspend fun handleRequest(input: String, context: Context): String = "Hello"
401+
}
402+
403+
assertFailsWith<TerminateException> {
404+
lambdaRunner.run { handler }
405+
}
406+
407+
GC.collect()
408+
GC.lastGCInfo?.let {gcInfo->
409+
println(
410+
"Heap Size Before: ${
411+
gcInfo.memoryUsageBefore.map {
412+
it.key + " - " + it.value.totalObjectsSizeBytes / 1024 / 1024
413+
}
414+
}"
415+
)
416+
println(
417+
"Heap Size After: ${
418+
gcInfo.memoryUsageAfter.map {
419+
it.key + " - " + it.value.totalObjectsSizeBytes / 1024 / 1024
420+
}
421+
}"
422+
)
423+
}
424+
}
425+
371426
@OptIn(ExperimentalForeignApi::class)
372427
private fun mockEnvironment() {
373428
if (getenv(AWS_LAMBDA_FUNCTION_NAME)?.toKString().isNullOrEmpty()) {

0 commit comments

Comments
 (0)