From 11ba394e7d79ac8aed79d1361ab4d1b455b8ff52 Mon Sep 17 00:00:00 2001
From: Brendan Dougherty <brendan.dougherty@shopify.com>
Date: Tue, 3 Sep 2024 10:40:44 -0400
Subject: [PATCH] Merge pull request #182 from
 Shopify/v19.0.5-shopify-2-backport-vtcombo-conn-leak

Backport: vtcombo: close query service on drop database
(cherry picked from commit 17ebe6f7219bab24e752ab24ce762cb1c535a569)
---
 .../vtcombo/recreate/recreate_test.go         | 21 +++++++++++++++++++
 go/vt/vtcombo/tablet_map.go                   |  6 +++++-
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/go/test/endtoend/vtcombo/recreate/recreate_test.go b/go/test/endtoend/vtcombo/recreate/recreate_test.go
index e66edb7688a..15cb63c3d7d 100644
--- a/go/test/endtoend/vtcombo/recreate/recreate_test.go
+++ b/go/test/endtoend/vtcombo/recreate/recreate_test.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -101,6 +102,9 @@ func TestDropAndRecreateWithSameShards(t *testing.T) {
 
 	cur := conn.Session(ks1+"@primary", nil)
 
+	mysqlConnCountBefore, err := getMySQLConnectionCount(ctx, cur)
+	require.Nil(t, err)
+
 	_, err = cur.Execute(ctx, "DROP DATABASE "+ks1, nil)
 	require.Nil(t, err)
 
@@ -108,6 +112,23 @@ func TestDropAndRecreateWithSameShards(t *testing.T) {
 	require.Nil(t, err)
 
 	assertTabletsPresent(t)
+
+	// Check the connection count after the CREATE. There will be zero connections after the DROP as the database
+	// no longer exists, but after it gets recreated any open pools will be able to reestablish connections.
+	mysqlConnCountAfter, err := getMySQLConnectionCount(ctx, cur)
+	require.Nil(t, err)
+
+	// Assert that we're not leaking mysql connections, but allow for some wiggle room due to transient connections
+	assert.InDelta(t, mysqlConnCountBefore, mysqlConnCountAfter, 5,
+		"not within allowable delta: mysqlConnCountBefore=%d, mysqlConnCountAfter=%d", mysqlConnCountBefore, mysqlConnCountAfter)
+}
+
+func getMySQLConnectionCount(ctx context.Context, session *vtgateconn.VTGateSession) (int, error) {
+	result, err := session.Execute(ctx, "select variable_value from performance_schema.global_status where variable_name='threads_connected'", nil)
+	if err != nil {
+		return 0, err
+	}
+	return strconv.Atoi(result.Rows[0][0].ToString())
 }
 
 func assertTabletsPresent(t *testing.T) {
diff --git a/go/vt/vtcombo/tablet_map.go b/go/vt/vtcombo/tablet_map.go
index 84ffa10fd3a..b1c045335a6 100644
--- a/go/vt/vtcombo/tablet_map.go
+++ b/go/vt/vtcombo/tablet_map.go
@@ -234,7 +234,11 @@ func DeleteKs(
 			tablet.tm.Stop()
 			tablet.tm.Close()
 			tablet.qsc.SchemaEngine().Close()
-			err := ts.DeleteTablet(ctx, tablet.alias)
+			err := tablet.qsc.QueryService().Close(ctx)
+			if err != nil {
+				return err
+			}
+			err = ts.DeleteTablet(ctx, tablet.alias)
 			if err != nil {
 				return err
 			}