From 7bf7e6d5b062945efda8856a42f1bae9c400e227 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 23 Mar 2026 08:56:14 -0400 Subject: [PATCH] fix: make DynamoDB client properties lazy to fix intermittent host.docker.internal 404 errors DefaultTestDynamoDbClient dynamoDb, asyncDynamoDb, dynamoDbStreams, and asyncDynamoDbStreams properties were eagerly initialized at construction time. This forced the lazy hostName property to resolve before the DynamoDB Local server started, while the placeholder ServerSocket was still holding the port. In environments where host.docker.internal resolves (e.g. CI with Docker installed), the hostName() function detected the ServerSocket listening on host.docker.internal and incorrectly resolved the hostname to host.docker.internal instead of localhost. When the actual DynamoDB Local server later started (with Jetty 12), it returned HTTP 404 for requests with Host header host.docker.internal, causing all retry attempts to fail. Making the client properties lazy ensures hostName is resolved on first use during startUp(), after the DynamoDB Local server is actually running and accepting requests. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../testing/internal/DefaultTestDynamoDbClient.kt | 7 ++++--- .../testing/internal/DefaultTestDynamoDbClient.kt | 11 ++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tempest-testing-internal/src/main/kotlin/app/cash/tempest/testing/internal/DefaultTestDynamoDbClient.kt b/tempest-testing-internal/src/main/kotlin/app/cash/tempest/testing/internal/DefaultTestDynamoDbClient.kt index 9a3681d4d..3fdc09390 100644 --- a/tempest-testing-internal/src/main/kotlin/app/cash/tempest/testing/internal/DefaultTestDynamoDbClient.kt +++ b/tempest-testing-internal/src/main/kotlin/app/cash/tempest/testing/internal/DefaultTestDynamoDbClient.kt @@ -24,11 +24,12 @@ class DefaultTestDynamoDbClient( override val tables: List, private val port: Int, ) : AbstractIdleService(), TestDynamoDbClient { - // TODO: Is there a better way of doing this than making a network connection? + // Lazy so that hostName is resolved after the DynamoDB Local server is started, + // not at construction time when the placeholder ServerSocket is still holding the port. private val hostName by lazy { hostName(port) } - override val dynamoDb = buildDynamoDb(hostName, port) - override val dynamoDbStreams = buildDynamoDbStreams(hostName, port) + override val dynamoDb by lazy { buildDynamoDb(hostName, port) } + override val dynamoDbStreams by lazy { buildDynamoDbStreams(hostName, port) } override fun startUp() { reset() diff --git a/tempest2-testing-internal/src/main/kotlin/app/cash/tempest2/testing/internal/DefaultTestDynamoDbClient.kt b/tempest2-testing-internal/src/main/kotlin/app/cash/tempest2/testing/internal/DefaultTestDynamoDbClient.kt index 525394755..cc187aaab 100644 --- a/tempest2-testing-internal/src/main/kotlin/app/cash/tempest2/testing/internal/DefaultTestDynamoDbClient.kt +++ b/tempest2-testing-internal/src/main/kotlin/app/cash/tempest2/testing/internal/DefaultTestDynamoDbClient.kt @@ -25,13 +25,14 @@ class DefaultTestDynamoDbClient( override val tables: List, private val port: Int, ) : AbstractIdleService(), TestDynamoDbClient { - // TODO: Is there a better way of doing this than making a network connection? + // Lazy so that hostName is resolved after the DynamoDB Local server is started, + // not at construction time when the placeholder ServerSocket is still holding the port. private val hostName by lazy { hostName(port) } - override val dynamoDb = buildDynamoDb(hostName, port) - override val asyncDynamoDb = buildAsyncDynamoDb(hostName, port) - override val dynamoDbStreams = buildDynamoDbStreams(hostName, port) - override val asyncDynamoDbStreams = buildAsyncDynamoDbStreams(hostName, port) + override val dynamoDb by lazy { buildDynamoDb(hostName, port) } + override val asyncDynamoDb by lazy { buildAsyncDynamoDb(hostName, port) } + override val dynamoDbStreams by lazy { buildDynamoDbStreams(hostName, port) } + override val asyncDynamoDbStreams by lazy { buildAsyncDynamoDbStreams(hostName, port) } override fun startUp() { reset()