From 0029d8efab8dcaa88b59756aa138698665a84f17 Mon Sep 17 00:00:00 2001 From: sksamuel Date: Tue, 12 May 2026 00:01:53 -0500 Subject: [PATCH] Convert createClient/shutdown failures to Unhealthy in DynamoDB check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two problems with the old wiring: 1. `createClient()` is a user-supplied lambda invoked inside `runInterruptible`. If it threw synchronously (typical: AWS region or credential resolution failure on `AmazonDynamoDBClient.builder().build()`), the exception propagated through `runInterruptible` and out of `check()` — the `.fold` on `Result` was never reached. The registry sees an exception rather than the expected Unhealthy. 2. The inner `use` extension called `shutdown()` unconditionally. If `shutdown()` itself threw (rare but possible during AWS SDK teardown), the original check failure was masked. Wrap the whole runInterruptible in runCatching and guard the shutdown call so the original cause always reaches the operator. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../cohort/aws/dynamo/DynamoDBHealthCheck.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/cohort-aws-dynamo/src/main/kotlin/com/sksamuel/cohort/aws/dynamo/DynamoDBHealthCheck.kt b/cohort-aws-dynamo/src/main/kotlin/com/sksamuel/cohort/aws/dynamo/DynamoDBHealthCheck.kt index 763969d8..810850a6 100644 --- a/cohort-aws-dynamo/src/main/kotlin/com/sksamuel/cohort/aws/dynamo/DynamoDBHealthCheck.kt +++ b/cohort-aws-dynamo/src/main/kotlin/com/sksamuel/cohort/aws/dynamo/DynamoDBHealthCheck.kt @@ -18,18 +18,26 @@ class DynamoDBHealthCheck( private fun AmazonDynamoDB.use(f: (AmazonDynamoDB) -> T): Result { val result = runCatching { f(this) } - this.shutdown() + runCatching { this.shutdown() } return result } override suspend fun check(): HealthCheckResult { - return runInterruptible(Dispatchers.IO) { - createClient().use { - it.listTables(1) + // Catch errors from createClient() itself (AWS region/credential resolution can throw + // synchronously on builder().build()). Previously such failures escaped runInterruptible + // and propagated out of check() instead of producing an Unhealthy result. + return runCatching { + runInterruptible(Dispatchers.IO) { + createClient().use { + it.listTables(1) + } } }.fold( - { HealthCheckResult.healthy("DynamoDB access successful") }, - { HealthCheckResult.unhealthy("Could not connect to DynamoDB", it) } + { it.fold( + { HealthCheckResult.healthy("DynamoDB access successful") }, + { HealthCheckResult.unhealthy("Could not connect to DynamoDB", it) } + )}, + { HealthCheckResult.unhealthy("Could not connect to DynamoDB", it) }, ) } }