@@ -242,6 +242,48 @@ func TestConnectionPoolConcurrency(t *testing.T) {
242242 assert .Equal (t , 1000 , metadata .RequestCount )
243243}
244244
245+ // TestConnectionPoolConcurrencyWithDeletes verifies that concurrent Get and Delete
246+ // operations do not race. Previously, Get used a manual RUnlock/Lock/RLock upgrade
247+ // that created a window in which another goroutine could delete the connection,
248+ // causing Get to update and return a deleted connection.
249+ func TestConnectionPoolConcurrencyWithDeletes (t * testing.T ) {
250+ ctx := context .Background ()
251+ pool := NewSessionConnectionPool (ctx )
252+
253+ mockConn := & mcp.Connection {}
254+ pool .Set ("backend1" , "session1" , mockConn )
255+
256+ done := make (chan bool )
257+
258+ // Goroutines that continuously Get the connection
259+ for i := 0 ; i < 5 ; i ++ {
260+ go func () {
261+ for j := 0 ; j < 200 ; j ++ {
262+ pool .Get ("backend1" , "session1" )
263+ }
264+ done <- true
265+ }()
266+ }
267+
268+ // Goroutines that interleave Set and Delete operations
269+ for i := 0 ; i < 3 ; i ++ {
270+ go func () {
271+ for j := 0 ; j < 50 ; j ++ {
272+ pool .Set ("backend1" , "session1" , mockConn )
273+ pool .Delete ("backend1" , "session1" )
274+ }
275+ done <- true
276+ }()
277+ }
278+
279+ for i := 0 ; i < 8 ; i ++ {
280+ <- done
281+ }
282+ // No assertion needed — the goal is to detect races via -race flag.
283+ // If the race condition is present, this test will fail non-deterministically
284+ // under the Go race detector (go test -race).
285+ }
286+
245287func TestConnectionPoolDeleteNonExistent (t * testing.T ) {
246288 ctx := context .Background ()
247289 pool := NewSessionConnectionPool (ctx )
0 commit comments