Skip to content

Commit

Permalink
v1.5.3
Browse files Browse the repository at this point in the history
  • Loading branch information
stfnmllr committed Sep 20, 2023
1 parent 19c5721 commit 96de8e1
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 56 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Release Notes

### Minor revisions

#### v1.5.3
- performance improvements

#### v1.5.2
- fixed race condition in connection conn and stmt methods in case of context cancelling

Expand Down
17 changes: 3 additions & 14 deletions driver/connattrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ const (
// connAttrs is holding connection relevant attributes.
type connAttrs struct {
mu sync.RWMutex
_host string
_timeout time.Duration
_pingInterval time.Duration
_bufferSize int
Expand All @@ -72,7 +71,6 @@ type connAttrs struct {
_cesu8Decoder func() transform.Transformer
_cesu8Encoder func() transform.Transformer
_emptyDateAsNull bool
_databaseName string
_logger *slog.Logger
}

Expand Down Expand Up @@ -103,7 +101,6 @@ func (c *connAttrs) clone() *connAttrs {
defer c.mu.RUnlock()

return &connAttrs{
_host: c._host,
_timeout: c._timeout,
_pingInterval: c._pingInterval,
_bufferSize: c._bufferSize,
Expand All @@ -121,7 +118,6 @@ func (c *connAttrs) clone() *connAttrs {
_cesu8Decoder: c._cesu8Decoder,
_cesu8Encoder: c._cesu8Encoder,
_emptyDateAsNull: c._emptyDateAsNull,
_databaseName: c._databaseName,
_logger: c._logger,
}
}
Expand Down Expand Up @@ -192,9 +188,6 @@ func (c *connAttrs) setDfv(dfv int) {
c._dfv = dfv
}

// Host returns the host of the connector.
func (c *connAttrs) Host() string { c.mu.RLock(); defer c.mu.RUnlock(); return c._host }

// Timeout returns the timeout of the connector.
func (c *connAttrs) Timeout() time.Duration { c.mu.RLock(); defer c.mu.RUnlock(); return c._timeout }

Expand Down Expand Up @@ -444,13 +437,6 @@ func (c *connAttrs) SetEmptyDateAsNull(emptyDateAsNull bool) {
c._emptyDateAsNull = emptyDateAsNull
}

// DatabaseName returns the tenant database name of the connector.
func (c *connAttrs) DatabaseName() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c._databaseName
}

// Logger returns the Logger instance of the connector.
func (c *connAttrs) Logger() *slog.Logger {
c.mu.RLock()
Expand All @@ -462,5 +448,8 @@ func (c *connAttrs) Logger() *slog.Logger {
func (c *connAttrs) SetLogger(logger *slog.Logger) {
c.mu.Lock()
defer c.mu.Unlock()
if logger == nil {
logger = slog.Default()
}
c._logger = logger
}
44 changes: 18 additions & 26 deletions driver/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,10 @@ func isAuthError(err error) bool {
return hdbErrors.Code() == p.HdbErrAuthenticationFailed
}

func connect(ctx context.Context, metrics *metrics, connAttrs *connAttrs, authAttrs *authAttrs) (driver.Conn, error) {
// if database name fetch tenant database host
if connAttrs._databaseName != "" {
if err := fetchHost(ctx, metrics, connAttrs); err != nil {
return nil, err
}
}

func connect(ctx context.Context, host string, metrics *metrics, connAttrs *connAttrs, authAttrs *authAttrs) (driver.Conn, error) {
// can we connect via cookie?
if auth := authAttrs.cookieAuth(); auth != nil {
conn, err := newSession(ctx, metrics, connAttrs, auth)
conn, err := newSession(ctx, host, metrics, connAttrs, auth)
if err == nil {
return conn, nil
}
Expand All @@ -226,14 +219,13 @@ func connect(ctx context.Context, metrics *metrics, connAttrs *connAttrs, authAt
authAttrs.invalidateCookie() // cookie auth was not successful - do not try again with the same data
}

const maxRetry = 1
numRetry := 0
refreshed := false
lastVersion := authAttrs.version.Load()

for {
authHnd := authAttrs.authHnd()

conn, err := newSession(ctx, metrics, connAttrs, authHnd)
conn, err := newSession(ctx, host, metrics, connAttrs, authHnd)
if err == nil {
if method, ok := authHnd.Selected().(auth.CookieGetter); ok {
authAttrs.setCookie(method.Cookie())
Expand All @@ -243,7 +235,7 @@ func connect(ctx context.Context, metrics *metrics, connAttrs *connAttrs, authAt
if !isAuthError(err) {
return nil, err
}
if numRetry >= maxRetry {
if refreshed {
return nil, err
}

Expand All @@ -257,7 +249,7 @@ func connect(ctx context.Context, metrics *metrics, connAttrs *connAttrs, authAt
}
lastVersion = version

numRetry++
refreshed = true
}
}

Expand Down Expand Up @@ -295,8 +287,8 @@ func (nvs namedValues) LogValue() slog.Value {
// unique connection number.
var connNo atomic.Uint64

func newConn(ctx context.Context, metrics *metrics, attrs *connAttrs) (*conn, error) {
netConn, err := attrs._dialer.DialContext(ctx, attrs._host, dial.DialerOptions{Timeout: attrs._timeout, TCPKeepAlive: attrs._tcpKeepAlive})
func newConn(ctx context.Context, host string, metrics *metrics, attrs *connAttrs) (*conn, error) {
netConn, err := attrs._dialer.DialContext(ctx, host, dial.DialerOptions{Timeout: attrs._timeout, TCPKeepAlive: attrs._tcpKeepAlive})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -339,24 +331,24 @@ func newConn(ctx context.Context, metrics *metrics, attrs *connAttrs) (*conn, er
return c, nil
}

func fetchHost(ctx context.Context, metrics *metrics, attrs *connAttrs) error {
c, err := newConn(ctx, metrics, attrs)
func fetchRedirectHost(ctx context.Context, host, databaseName string, metrics *metrics, attrs *connAttrs) (string, error) {
c, err := newConn(ctx, host, metrics, attrs)
if err != nil {
return err
return "", err
}
defer c.Close()
dbi, err := c._dbConnectInfo(ctx, attrs._databaseName)
dbi, err := c._dbConnectInfo(ctx, databaseName)
if err != nil {
return err
return "", err
}
if !dbi.IsConnected { // if databaseName == "SYSTEMDB" and isConnected == true host and port are initial
attrs._host = net.JoinHostPort(dbi.Host, strconv.Itoa(dbi.Port))
if dbi.IsConnected { // if databaseName == "SYSTEMDB" and isConnected == true host and port are initial
return host, nil
}
return nil
return net.JoinHostPort(dbi.Host, strconv.Itoa(dbi.Port)), nil
}

func newSession(ctx context.Context, metrics *metrics, attrs *connAttrs, authHnd *p.AuthHnd) (driver.Conn, error) {
c, err := newConn(ctx, metrics, attrs)
func newSession(ctx context.Context, host string, metrics *metrics, attrs *connAttrs, authHnd *p.AuthHnd) (driver.Conn, error) {
c, err := newConn(ctx, host, metrics, attrs)
if err != nil {
return nil, err
}
Expand Down
60 changes: 45 additions & 15 deletions driver/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@ import (
"database/sql/driver"
"os"
"path"
"sync"

"github.com/SAP/go-hdb/driver/internal/protocol/x509"
)

type redirectCacheKey struct {
host, databaseName string
}

var redirectCache sync.Map

/*
A Connector represents a hdb driver in a fixed configuration.
A Connector can be passed to sql.OpenDB (starting from go 1.10) allowing users to bypass a string based data source name.
*/
type Connector struct {
_host string
_databaseName string

*connAttrs
*authAttrs

metrics *metrics

connHook func(driver.Conn) driver.Conn
}

// NewConnector returns a new Connector instance with default values.
Expand Down Expand Up @@ -102,27 +110,53 @@ func NewDSNConnector(dsnStr string) (*Connector, error) {
// NativeDriver returns the concrete underlying Driver of the Connector.
func (c *Connector) NativeDriver() Driver { return stdHdbDriver }

// Connect implements the database/sql/driver/Connector interface.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
conn, err := connect(ctx, c.metrics, c.connAttrs.clone(), c.authAttrs)
// Host returns the host of the connector.
func (c *Connector) Host() string { return c._host }

// DatabaseName returns the tenant database name of the connector.
func (c *Connector) DatabaseName() string { return c._databaseName }

func (c *Connector) redirect(ctx context.Context) (driver.Conn, error) {
connAttrs := c.connAttrs.clone()

if redirectHost, found := redirectCache.Load(redirectCacheKey{host: c._host, databaseName: c._databaseName}); found {
if conn, err := connect(ctx, redirectHost.(string), c.metrics, connAttrs, c.authAttrs); err == nil {
return conn, nil
}
}

redirectHost, err := fetchRedirectHost(ctx, c._host, c._databaseName, c.metrics, connAttrs)
if err != nil {
return nil, err
}
if c.connHook != nil {
conn = c.connHook(conn)
conn, err := connect(ctx, redirectHost, c.metrics, connAttrs, c.authAttrs)
if err != nil {
return nil, err
}

redirectCache.Store(redirectCacheKey{host: c._host, databaseName: c._databaseName}, redirectHost)

return conn, err
}

// Connect implements the database/sql/driver/Connector interface.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
if c._databaseName != "" {
return c.redirect(ctx)
}
return connect(ctx, c._host, c.metrics, c.connAttrs.clone(), c.authAttrs)
}

// Driver implements the database/sql/driver/Connector interface.
func (c *Connector) Driver() driver.Driver { return stdHdbDriver }

func (c *Connector) clone() *Connector {
return &Connector{
connAttrs: c.connAttrs.clone(),
authAttrs: c.authAttrs.clone(),
metrics: c.metrics,
connHook: c.connHook,
_host: c._host,
_databaseName: c._databaseName,
connAttrs: c.connAttrs.clone(),
authAttrs: c.authAttrs.clone(),
metrics: c.metrics,
}
}

Expand All @@ -132,7 +166,3 @@ func (c *Connector) WithDatabase(databaseName string) *Connector {
nc._databaseName = databaseName
return nc
}

// SetConnHook sets a function for intercepting connection creation.
// This is for internal use only and might be changed or disabled in future.
func (c *Connector) SetConnHook(fn func(driver.Conn) driver.Conn) { c.connHook = fn }
2 changes: 1 addition & 1 deletion driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// DriverVersion is the version number of the hdb driver.
const DriverVersion = "1.5.2"
const DriverVersion = "1.5.3"

// DriverName is the driver name to use with sql.Open for hdb databases.
const DriverName = "hdb"
Expand Down

0 comments on commit 96de8e1

Please sign in to comment.