-
Notifications
You must be signed in to change notification settings - Fork 351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pgx Cloud SQL Proxy in-process example #433
Comments
Hi @StevenACoffman. I agree that the cloudsqlpostgres dialer example should demonstrate connecting with pgx, since lib/pq is no longer being actively developed. Our Go connectivity sample demonstrates connecting using pgx/stdlib and database/sql, but assumes that you're running the proxy in the background which won't work if you prefer not to run a sidecar. @kurtisvg thoughts? |
Replacing I think using |
Yeah, it is probably better to add a new hook. I'm not sure how you would replicate the current hook's driver's
If you have any better idea for a more convenient dial func (that uses context?), I'd love to see that too. |
Ok, if I was making a new // Package pgxproxy adds a 'cloudsqlpostgres' driver to use when you want
// to access a Cloud SQL Database via the go database/sql library.
// It is a wrapper over the driver found at github.com/lib/pqx.
// To use this driver, you can look at an example in
// postgres_test package in the hook_test.go file
package pgxproxy
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"net"
"regexp"
"time"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/stdlib"
"github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy"
)
func init() {
sql.Register("cloudsqlpostgres", &Driver{})
}
type Driver struct {
ctx context.Context
}
// instanceRegexp is used to parse the addr returned by lib/pq.
// lib/pq returns the format '[project:region:instance]:port'
var instanceRegexp = regexp.MustCompile(`^\[(.+)\]:[0-9]+$`)
func DialFunc(ctx context.Context, network string, addr string) (net.Conn, error) {
matches := instanceRegexp.FindStringSubmatch(addr)
if len(matches) != 2 {
return nil, fmt.Errorf("failed to parse addr: %q. It should conform to the regular expression %q", addr, instanceRegexp)
}
instance := matches[1]
return proxy.Dial(instance)
}
func LookupFunc(ctx context.Context, host string) ([]string, error) {
return []string{host}, nil
}
func DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
return nil, fmt.Errorf("timeout is not currently supported for cloudsqlpostgres dialer")
}
func (d *Driver) WithContext(ctx context.Context) {
d.ctx = ctx
}
func (d *Driver) Open(dsn string) (driver.Conn, error) {
config, configErr := pgx.ParseConfig(dsn)
if configErr != nil {
return nil, configErr
}
config.DialFunc = DialFunc
config.LookupFunc = LookupFunc
if d.ctx == nil {
d.ctx = context.Background()
}
connStr := stdlib.RegisterConnConfig(config)
// pgx? cloudsqlpostgres?
db, dbErr := sql.Open("pgx", connStr)
if dbErr != nil {
return nil, dbErr
}
driver := db.Driver()
// pgx? cloudsqlpostgres?
return driver.Open("pgx")
} Not sure about the |
The above func (d *Driver) Open(dsn string) (driver.Conn, error) {
config, configErr := pgx.ParseConfig(dsn)
if configErr != nil {
return nil, fmt.Errorf("couldn't parse DB DSN: %w", configErr)
}
config.DialFunc = DialFunc
config.LookupFunc = LookupFunc
if d.ctx == nil {
d.ctx = context.Background()
}
_ = stdlib.RegisterConnConfig(config)
return stdlib.GetDefaultDriver().Open(dsn) So I'm not sure I can put a PR that will get accepted, despite the fact that my use case is a little simpler to solve. Can I get a hand? |
I think right now my preference is exposing a better If you would like to directly address this issue and just document the current behavior with the current If you'd prefer to work on a better API surface for Otherwise we can leave this issue open as is since it contains the current instructions for using pgx, and mark it closed when we have a better |
Yes, please go for that by all means. I'm having trouble making more time to do this properly, as we're able to limp by with my hacky solution until contexts are used more consistently. |
With the DialContext added in #483 connecting to cloudsql instance using in-process cloudsql-proxy with jackc/pgx can be accomplished in a context aware way: var (
host = os.Getenv("DBHOST") // DBHOST for GCP cloudsql is in format project:region:instance
name = os.Getenv("DBNAME")
user = os.Getenv("DBUSER")
pass = os.Getenv("DBPASS")
)
var instanceRegexp = regexp.MustCompile(`^\[(.+)\]:[0-9]+$`)
// In-process Proxy
// Note that sslmode=disable is required it does not mean that the connection
// is unencrypted. All connections via the proxy are completely encrypted.
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=disable", host, user, pass, name)
config, configErr := pgx.ParseConfig(dsn)
if configErr != nil {
log.Fatal(configErr)
}
// e.g. net.Dialer.DialContext
config.DialFunc = func(ctx context.Context, network string, addr string) (net.Conn, error) {
matches := instanceRegexp.FindStringSubmatch(addr)
if len(matches) != 2 {
return nil, fmt.Errorf("failed to parse addr: %q. It should conform to the regular expression %q", addr, instanceRegexp)
}
instance := matches[1]
return proxy.DialContext(ctx, instance)
}
ctx := context.Background()
// e.g. net.Resolver.LookupHost
config.LookupFunc = func(ctx context.Context, host string) ([]string, error) {
return []string{host}, nil
}
conn, connErr := pgx.ConnectConfig(ctx, config) |
We're currently working on some major structural improvements to the proxy. Once those are done we'll circle back to address support for pgx. |
@enocom @kurtisvg Just checking in after 6 months. Our specific use case for cloudsql-proxy is in conjunction with sqlc, which just added support for pgx/v4 connection pool with their respective API usage, which differs from the standard. The details of that are in database/sqlsqlc-dev/sqlc#1037). If this is on the near term roadmap, knowing it would help us with our internal planning. |
And if you're looking to just get something working, here's an example of how to use pgx with the new dialer: https://github.com/kurtisvg/cloud-sql-go-connector#pgx-for-postgres. |
Circling back to this. Right now I'd recommend trying out the v2 dialer which makes this a breeze. In addition, we're working on some updated code samples that will highlight how to use pgx with an in-process dialer. |
In addition to the example linked above with the Go connector, we also have an example that hooks up pgx with the database/sql interface: https://github.com/GoogleCloudPlatform/golang-samples/blob/main/cloudsql/postgres/database-sql/connect_connector.go. Also, v2 is finally available in public preview: https://github.com/GoogleCloudPlatform/cloud-sql-proxy/releases/tag/v2.0.0.preview.0. |
The authors of the lib/pq library are encouraging new projects to use pgx instead:
I am connecting to cloudsql from GKE, but I would prefer not to run a sidecar. However, I am using pgx, rather than pg. I have modified the hook_test.go file to use pgx as follows:
I think an example like this might be helpful to include in this project. If there is a better mechanism (perhaps using the pgx/stdlib ?), I would be very interested to know that.
The text was updated successfully, but these errors were encountered: