This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -22,6 +23,10 @@ type PostgresConfig struct {
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
ConnMaxIdleTime time.Duration
|
||||
// ApplicationName identifies this instance's pool connections in
|
||||
// pg_stat_activity (e.g. "PGSQL_BROKER_INSTANCE1"). The LISTEN
|
||||
// connection appends "_LISTENER" to this value.
|
||||
ApplicationName string
|
||||
}
|
||||
|
||||
// PostgresAdapter implements DBAdapter for PostgreSQL
|
||||
@@ -170,7 +175,7 @@ func (p *PostgresAdapter) Query(ctx context.Context, query string, args ...inter
|
||||
|
||||
// Listen starts listening on a PostgreSQL notification channel
|
||||
func (p *PostgresAdapter) Listen(ctx context.Context, channel string, handler NotificationHandler) error {
|
||||
connStr := p.buildConnectionString()
|
||||
connStr := p.buildConnectionStringWithAppName(p.config.ApplicationName + "_LISTENER")
|
||||
|
||||
reportProblem := func(ev pq.ListenerEventType, err error) {
|
||||
if err != nil {
|
||||
@@ -211,7 +216,11 @@ func (p *PostgresAdapter) Listen(ctx context.Context, channel string, handler No
|
||||
p.logger.Info("stopping listener", "channel", channel)
|
||||
return
|
||||
case <-time.After(90 * time.Second):
|
||||
SafeGo(p.logger, "listener-ping-"+channel, func() { listener.Ping() })
|
||||
SafeGo(p.logger, "listener-ping-"+channel, func() {
|
||||
if err := listener.Ping(); err != nil {
|
||||
p.logger.Error("listener ping failed", "channel", channel, "error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -232,24 +241,42 @@ func (p *PostgresAdapter) Unlisten(ctx context.Context, channel string) error {
|
||||
return listener.Unlisten(channel)
|
||||
}
|
||||
|
||||
// buildConnectionString builds a PostgreSQL connection string
|
||||
// buildConnectionString builds a PostgreSQL connection string for the
|
||||
// pooled connection, using the adapter's own application name.
|
||||
func (p *PostgresAdapter) buildConnectionString() string {
|
||||
return p.buildConnectionStringWithAppName(p.config.ApplicationName)
|
||||
}
|
||||
|
||||
// buildConnectionStringWithAppName builds a PostgreSQL connection string
|
||||
// with the given application_name, so pooled and LISTEN connections can be
|
||||
// told apart in pg_stat_activity.
|
||||
func (p *PostgresAdapter) buildConnectionStringWithAppName(appName string) string {
|
||||
sslMode := p.config.SSLMode
|
||||
if sslMode == "" {
|
||||
sslMode = "disable"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s options='-c search_path=broker,public'",
|
||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s application_name=%s options='-c search_path=broker,public'",
|
||||
p.config.Host,
|
||||
p.config.Port,
|
||||
p.config.User,
|
||||
p.config.Password,
|
||||
p.config.Database,
|
||||
sslMode,
|
||||
quoteDSNValue(appName),
|
||||
)
|
||||
}
|
||||
|
||||
// quoteDSNValue escapes a value for use in a libpq keyword/value connection
|
||||
// string, single-quoting it and backslash-escaping embedded backslashes and
|
||||
// quotes per the libpq connection string format.
|
||||
func quoteDSNValue(v string) string {
|
||||
v = strings.ReplaceAll(v, `\`, `\\`)
|
||||
v = strings.ReplaceAll(v, `'`, `\'`)
|
||||
return "'" + v + "'"
|
||||
}
|
||||
|
||||
// Conn returns a single physical connection pinned out of the pool, for
|
||||
// session-scoped operations (e.g. pg_try_advisory_lock) that must survive
|
||||
// across calls and must not be silently reaped or handed to another caller
|
||||
|
||||
Reference in New Issue
Block a user