Skip to content

Commit 79bd796

Browse files
committed
fix: address Copilot review — use{} for resources, 404 for not found, curl -fSs
- Use Kotlin use{} blocks for PreparedStatement and ResultSet cleanup - Return 404 ResponseEntity when member not found - Handle null HikariPoolMXBean with proper error response - Remove non-null assertion (!!) — return ResponseEntity directly - Use curl -fSs in test.sh to fail on HTTP errors Signed-off-by: slayerjain <shubhamkjain@outlook.com>
1 parent 8a4717e commit 79bd796

2 files changed

Lines changed: 53 additions & 55 deletions

File tree

ps-cache-kotlin/src/main/kotlin/com/demo/App.kt

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.springframework.jdbc.core.JdbcTemplate
66
import org.springframework.web.bind.annotation.GetMapping
77
import org.springframework.web.bind.annotation.RequestParam
88
import org.springframework.web.bind.annotation.RestController
9+
import org.springframework.http.ResponseEntity
910
import javax.sql.DataSource
1011
import com.zaxxer.hikari.HikariDataSource
1112

@@ -29,63 +30,60 @@ class AccountController(private val jdbc: JdbcTemplate, private val dataSource:
2930
@GetMapping("/health")
3031
fun health() = mapOf("status" to "ok")
3132

32-
/**
33-
* /account?member=N queries the travel_account table.
34-
*
35-
* JDBC PS caching (prepareThreshold=1):
36-
* - 1st call on a fresh connection: Parse(query="SELECT ...") + Bind + Describe + Execute
37-
* - 2nd+ calls on same connection: Bind(ps="S_1") + Execute only (cached PS)
38-
*
39-
* The /evict endpoint forces HikariCP to evict all connections, so the
40-
* NEXT /account call gets a fresh connection with cold PS cache.
41-
*/
4233
@GetMapping("/account")
43-
fun getAccount(@RequestParam("member") memberId: Int): Any {
44-
return jdbc.execute { conn: java.sql.Connection ->
45-
conn.autoCommit = false
46-
try {
47-
val ps = conn.prepareStatement(
48-
"""SELECT id, member_id, name, balance
49-
FROM travelcard.travel_account
50-
WHERE member_id = ?"""
51-
)
52-
ps.setInt(1, memberId)
53-
val rs = ps.executeQuery()
34+
fun getAccount(@RequestParam("member") memberId: Int): ResponseEntity<Any> {
35+
val result = jdbc.execute(
36+
org.springframework.jdbc.core.ConnectionCallback<Account?> { conn ->
37+
conn.autoCommit = false
38+
try {
39+
conn.prepareStatement(
40+
"""SELECT id, member_id, name, balance
41+
FROM travelcard.travel_account
42+
WHERE member_id = ?"""
43+
).use { ps ->
44+
ps.setInt(1, memberId)
45+
ps.executeQuery().use { rs ->
46+
val account = if (rs.next()) {
47+
Account(
48+
id = rs.getInt("id"),
49+
memberId = rs.getInt("member_id"),
50+
name = rs.getString("name"),
51+
balance = rs.getInt("balance")
52+
)
53+
} else null
5454

55-
val result = if (rs.next()) {
56-
Account(
57-
id = rs.getInt("id"),
58-
memberId = rs.getInt("member_id"),
59-
name = rs.getString("name"),
60-
balance = rs.getInt("balance")
61-
)
62-
} else {
63-
mapOf("error" to "not found", "member_id" to memberId)
55+
conn.commit()
56+
account
57+
}
58+
}
59+
} catch (e: Exception) {
60+
conn.rollback()
61+
throw e
6462
}
63+
})
6564

66-
rs.close()
67-
ps.close()
68-
conn.commit()
69-
result
70-
} catch (e: Exception) {
71-
conn.rollback()
72-
throw e
73-
}
74-
}!!
65+
return if (result != null) {
66+
ResponseEntity.ok(result)
67+
} else {
68+
ResponseEntity.status(404).body(mapOf("error" to "not found", "member_id" to memberId))
69+
}
7570
}
7671

77-
/**
78-
* /evict forces HikariCP to evict all idle connections.
79-
* Next request gets a FRESH PG connection → cold PS cache.
80-
* This simulates what happens in production when connections cycle.
81-
*/
8272
@GetMapping("/evict")
83-
fun evict(): Map<String, Any> {
84-
val hikari = dataSource as HikariDataSource
85-
hikari.hikariPoolMXBean?.softEvictConnections()
86-
// Also wait briefly for eviction
73+
fun evict(): ResponseEntity<Map<String, Any>> {
74+
val hikari = dataSource as? HikariDataSource
75+
?: return ResponseEntity.status(500).body(mapOf("error" to "not a HikariDataSource"))
76+
77+
val mxBean = hikari.hikariPoolMXBean
78+
?: return ResponseEntity.status(500).body(mapOf("error" to "pool MXBean not available"))
79+
80+
mxBean.softEvictConnections()
8781
Thread.sleep(200)
88-
return mapOf("evicted" to true, "active" to (hikari.hikariPoolMXBean?.activeConnections ?: 0),
89-
"idle" to (hikari.hikariPoolMXBean?.idleConnections ?: 0))
82+
83+
return ResponseEntity.ok(mapOf(
84+
"evicted" to true,
85+
"active" to mxBean.activeConnections,
86+
"idle" to mxBean.idleConnections
87+
))
9088
}
9189
}

ps-cache-kotlin/test.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,31 @@ echo "=== PS-Cache Mock Mismatch Test (Kotlin/JDBC) ==="
77

88
echo "--- Window 1: Connection A ---"
99
echo " /account?member=19:"
10-
curl -s "$BASE_URL/account?member=19"
10+
curl -fSs "$BASE_URL/account?member=19"
1111
echo ""
1212
sleep 0.3
1313

1414
echo " /account?member=23:"
15-
curl -s "$BASE_URL/account?member=23"
15+
curl -fSs "$BASE_URL/account?member=23"
1616
echo ""
1717
sleep 0.3
1818

1919
echo ""
2020
echo "--- Evict (force new connection) ---"
2121
echo " /evict:"
22-
curl -s "$BASE_URL/evict"
22+
curl -fSs "$BASE_URL/evict"
2323
echo ""
2424
sleep 1
2525

2626
echo ""
2727
echo "--- Window 2: Connection B ---"
2828
echo " /account?member=31:"
29-
curl -s "$BASE_URL/account?member=31"
29+
curl -fSs "$BASE_URL/account?member=31"
3030
echo ""
3131
sleep 0.3
3232

3333
echo " /account?member=42:"
34-
curl -s "$BASE_URL/account?member=42"
34+
curl -fSs "$BASE_URL/account?member=42"
3535
echo ""
3636

3737
echo ""

0 commit comments

Comments
 (0)