go-agent icon indicating copy to clipboard operation
go-agent copied to clipboard

added SessionResetter into driver_conn optional interfaces

Open siddhesh-tamhanekar opened this issue 11 months ago • 2 comments

Links

N/A

Details

Problem

The nrmysql driver in newrelic/go-agent does not properly handle connection reuse in case of last query was errored when MySQL closes an idle connection and application try to reuse it, This leads to a "commands out of sync" error when the same connection is used again.

Steps to Reproduce The following test program demonstrates the issue:

package main

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/newrelic/go-agent/v3/integrations/nrmysql"
)

func main() {
	mysqlConnString := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", "user", "pass", "127.0.0.1", "3306", "db_name")
	mdb, err := sql.Open("nrmysql", mysqlConnString)
	fmt.Println("mysql open error:", err)

	mdb.Ping()
	fmt.Println("ping error:", err)

	// This query will fail
	q("select * from table_not_exists", mdb)

	// Simulate MySQL closing the connection after idle time (e.g., 10s)
	time.Sleep(time.Second * 12)

	// This will result in "commands out of sync" error
	q("select * from users", mdb)
}

func q(sql string, mdb *sql.DB) {
	rows, err := mdb.Query(sql)
	if err == nil {
		cols, _ := rows.Columns()
		columns := make([]interface{}, len(cols))
		columnPointers := make([]interface{}, len(cols))
		for i := range columns {
			columnPointers[i] = &columns[i]
		}
		rows.Next()
		scanErr := rows.Scan(columnPointers...)
		fmt.Println("mysql query error:", err, scanErr, columns)
	} else {
		fmt.Println("mysql query error:", err)
	}
}

Root Cause

The repository provides an SQL driver that implements various sql package interfaces. One of these interfaces is driver.SessionResetter, which is already implemented in sql_driver.go. However, the optionalMethodsConn type in sql_driver_optional_methods.go did not support driver.SessionResetter.

Solution

  • Updated internal/tools/interface-wrapping to include SessionResetter.
  • Regenerated optionalMethodsConn to ensure SessionResetter is properly implemented.
  • This allows MySQL driver resetSession method to be called to determine whether the connection is still valid before reuse.

Additional Notes

  • The interface-wrapping tool was updated to recognize SessionResetter.
  • optionalMethodsConn was regenerated to reflect this change.
  • Tests have been validated to ensure correctness.
  • No breaking changes introduced.

siddhesh-tamhanekar avatar Feb 11 '25 14:02 siddhesh-tamhanekar

CLA assistant check
All committers have signed the CLA.

CLAassistant avatar Feb 11 '25 14:02 CLAassistant

Hi @siddhesh-tamhanekar,

Thanks for the contribution! We started our testing work flows on this PR. We want to spend more time reviewing these changes in the upcoming weeks. We will tag you if anything needs to be brought to your attention.

mirackara avatar Feb 24 '25 18:02 mirackara

@siddhesh-tamhanekar Can you rebase this against develop? Also, would it be possible to add a case for the situation outlined above to our unit test library?

iamemilio avatar Apr 07 '25 16:04 iamemilio