GoogleCloudPlatform/cloud-sql-go-connector

dialer is closed error

Closed this issue · 2 comments

Bug Description

Sure, here's a draft for your GitHub issue:


Issue Summary:
After updating a Go dependency yesterday, the application utilizing cloud-sql-go-connector began rejecting database connections.

Details:
image

  • Upon investigation, the GCP logs revealed the following error: Link to GCP logs
  • The connection is established using GORM with cloud-sql-go-connector.
  • The application itself is a Go language service built on the Echo framework.

Error Description:
The error seems to stem from the database connection being rejected after updating the Go dependency. Despite the connection being established through cloud-sql-go-connector, the logs indicate an issue with maintaining connectivity.

Steps to Reproduce:

  1. Update the Go dependency.
  2. Attempt to establish a connection to the database using cloud-sql-go-connector.
  3. Observe the rejection of the database connection.

Expected Behavior:
The application should be able to establish and maintain a connection to the database using cloud-sql-go-connector after updating the Go dependency.

Environment:

  • Application: Go language service
  • Framework: Echo
  • Database Connector: cloud-sql-go-connector

Additional Information:

  • Any relevant information or context that could assist in troubleshooting or understanding the issue.

Screenshots/Logs:
image

  • Screenshot of the error message or relevant logs: Link to screenshot/log
  • Any other pertinent visual or textual information related to the issue.

Priority:
This issue is affecting the functionality of the application and requires urgent attention.

Assignees:
Assign appropriate individuals or teams responsible for addressing this issue.

Labels:
Apply relevant labels to categorize and prioritize this issue.


Feel free to customize this draft further based on your specific needs or additional information you may have.

Example code (or command)

func New(pg_url, gcpCredentialDbbase64 string) *gorm.DB {

	gcpCredentialDb, err := base64.StdEncoding.DecodeString(gcpCredentialDbbase64)
	if err != nil {
		log.Fatal(err)
	}

	cleanup, err := pgxv4.RegisterDriver("cloudsql-postgres", cloudsqlconn.WithCredentialsJSON(gcpCredentialDb))
	if err != nil {
		log.Fatal(err)
	}
	defer cleanup()

	db, err := gorm.Open(postgres.New(postgres.Config{
		DriverName: "cloudsql-postgres",
		DSN:        pg_url,
	}))

	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
	return db
}

Stacktrace

No response

Steps to reproduce?

  1. ?
  2. ?
  3. ?
    ...

Environment

  1. OS type and version:
  2. Go version:
  3. Cloud SQL Go Connector version:

Additional Details

No response

Hi @jSierraB3991 Thanks for raising an issue on the Cloud SQL Go Connector 😄

What is actually happening here is that you have a small bug in your code that was previously going unnoticed in older versions and not being properly raised. We just fixed it as of v1.9.0 via #766

Let me explain what is going on here and the source of the error:

This error resolves around the cleanup. Let's take a look at the RegisterDriver code to understand what is happening:

// RegisterDriver registers a Postgres driver that uses the cloudsqlconn.Dialer
// configured with the provided options. The choice of name is entirely up to
// the caller and may be used to distinguish between multiple registrations of
// differently configured Dialers. The driver uses pgx/v4 internally.
// RegisterDriver returns a cleanup function that should be called one the
// database connection is no longer needed.
func RegisterDriver(name string, opts ...cloudsqlconn.Option) (func() error, error) {
d, err := cloudsqlconn.NewDialer(context.Background(), opts...)
if err != nil {
return func() error { return nil }, err
}
sql.Register(name, &pgDriver{
d: d,
dbURIs: make(map[string]string),
})
return func() error { return d.Close() }, nil

The key piece here is how RegisterDriver returns d.Close() which in your code sample is your cleanup variable.

When you use defer cleanup() you are essentially saying once your New() func returns the db object, immediately call cleanup which is d.Close() to close the Dialer. You need the dialer to be open to establish future connections and refresh any previous connections.

So in your code sample you should return db, cleanup and move your defer cleanup() to wherever you are calling your New() func from.

This is why we added the nice error to point this out. Previously as you mention it wouldn't error immediately but would error once the single connection expired (not making it obvious the true cause of the error) which is why you may not have run into it.

We try to make this clear in our README sample but we can probably update it to be even more clear:

// call cleanup when you're done with the database connection
defer cleanup()

This was originally caught in the AlloyDB mirror of this library: GoogleCloudPlatform/alloydb-go-connector#522 (comment)

Hope this clears up the confusion. If you have any follow-up questions or need any more clarity please ask them here 😄