Skip to content

Commit

Permalink
v1.8.10
Browse files Browse the repository at this point in the history
  • Loading branch information
stfnmllr committed Mar 11, 2024
1 parent 5f423b6 commit 0a07797
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 28 deletions.
4 changes: 4 additions & 0 deletions RELEASENOTES.md
Expand Up @@ -5,6 +5,10 @@ Release Notes

### Minor revisions

#### v1.8.10
- fixed regression on authentication refresh
- updated dependencies

#### v1.8.8 - v1.8.9
- updated dependencies

Expand Down
15 changes: 7 additions & 8 deletions driver/authattrs.go
Expand Up @@ -12,6 +12,7 @@ import (
// authAttrs is holding authentication relevant attributes.
type authAttrs struct {
hasCookie atomic.Bool
version atomic.Uint64 // auth attributes version
mu sync.RWMutex
_username, _password string // basic authentication
_certKey *auth.CertKey // X509
Expand Down Expand Up @@ -98,28 +99,26 @@ func (c *authAttrs) callRefreshClientCertWithLock(refreshClientCert func() (clie
return refreshClientCert()
}

func (c *authAttrs) refresh() (bool, error) {
func (c *authAttrs) refresh() error {
c.cbmu.Lock() // synchronize refresh calls
defer c.cbmu.Unlock()

c.mu.Lock()
defer c.mu.Unlock()

refreshed := false

if c._refreshPassword != nil {
if password, ok := c.callRefreshPasswordWithLock(c._refreshPassword); ok {
if password != c._password {
c._password = password
refreshed = true
c.version.Add(1)
}
}
}
if c._refreshToken != nil {
if token, ok := c.callRefreshTokenWithLock(c._refreshToken); ok {
if token != c._token {
c._token = token
refreshed = true
c.version.Add(1)
}
}
}
Expand All @@ -128,14 +127,14 @@ func (c *authAttrs) refresh() (bool, error) {
if c._certKey == nil || !c._certKey.Equal(clientCert, clientKey) {
certKey, err := auth.NewCertKey(clientCert, clientKey)
if err != nil {
return refreshed, err
return err
}
c._certKey = certKey
refreshed = true
c.version.Add(1)
}
}
}
return refreshed, nil
return nil
}

func (c *authAttrs) invalidateCookie() { c.hasCookie.Store(false) }
Expand Down
67 changes: 65 additions & 2 deletions driver/authattrs_test.go
@@ -1,12 +1,16 @@
//go:build !unit

package driver

import (
"context"
"database/sql"
"sync"
"testing"
)

// test if concurrent refresh would deadlock.
func testConcurrentRefresh(t *testing.T) {
func testRefreshDeadlock(t *testing.T) {
const numConcurrent = 100

attrs := &authAttrs{}
Expand All @@ -30,14 +34,73 @@ func testConcurrentRefresh(t *testing.T) {
wg.Wait()
}

// test if refresh would work for getting connections cuncurrently.
func testRefresh(t *testing.T) {
const numConcurrent = 5 // limit to 5 as after 5 invalid attempts user is locked

connector := MT.NewConnector()

if connector._databaseName != "" {
// test does not work in case of redirectCache.Load() is successful, as connect is called twice,
// so that the password is most probably refreshed already on second call
t.Skip("to execute test, don't use database redirection")
}

password := connector.Password()
connector.SetPassword("invalid password")
passwordRefreshed := false
connector.SetRefreshPassword(func() (string, bool) {
if passwordRefreshed {
return "", false
}
passwordRefreshed = true
return password, true
})
db := sql.OpenDB(connector)
defer db.Close()

wg := new(sync.WaitGroup)
wg.Add(numConcurrent)
start := make(chan struct{})
connCh := make(chan *sql.Conn, numConcurrent)
errCh := make(chan error, numConcurrent)
for i := 0; i < numConcurrent; i++ {
go func(start <-chan struct{}, connCh chan *sql.Conn, errCh chan error, wg *sync.WaitGroup) {
defer wg.Done()
<-start
conn, err := db.Conn(context.Background())
if err != nil {
errCh <- err
} else {
connCh <- conn
}
}(start, connCh, errCh, wg)
}
// start connections concurrently
close(start)
// wait for all go routines to end
wg.Wait()
close(connCh)
close(errCh)
// close connections
for conn := range connCh {
conn.Close()
}
// check errors (especially authentication errors in case the password refreash didn't work for any connection)
for err := range errCh {
t.Fatal(err)
}
}

func TestAuthAttrs(t *testing.T) {
t.Parallel()

tests := []struct {
name string
fct func(t *testing.T)
}{
{"testConcurrentRefresh", testConcurrentRefresh},
{"testRefreshDeadlock", testRefreshDeadlock},
{"testRefresh", testRefresh},
}

for _, test := range tests {
Expand Down
15 changes: 6 additions & 9 deletions driver/connection.go
Expand Up @@ -227,7 +227,7 @@ func connect(ctx context.Context, host string, metrics *metrics, connAttrs *conn
authAttrs.invalidateCookie() // cookie auth was not successful - do not try again with the same data
}

refreshed := false
lastVersion := authAttrs.version.Load()
for {
authHnd := authAttrs.authHnd()

Expand All @@ -241,19 +241,16 @@ func connect(ctx context.Context, host string, metrics *metrics, connAttrs *conn
if !isAuthError(err) {
return nil, err
}
if refreshed {

if err := authAttrs.refresh(); err != nil {
return nil, err
}

ok, refreshErr := authAttrs.refresh()
if refreshErr != nil {
return nil, refreshErr
}
if !ok { // no connection retry if no refresh did happen
version := authAttrs.version.Load()
if version == lastVersion { // no connection retry in case no new version available
return nil, err
}

refreshed = true
lastVersion = version
}
}

Expand Down
2 changes: 1 addition & 1 deletion driver/driver.go
Expand Up @@ -10,7 +10,7 @@ import (
)

// DriverVersion is the version number of the hdb driver.
const DriverVersion = "1.8.9"
const DriverVersion = "1.8.10"

// DriverName is the driver name to use with sql.Open for hdb databases.
const DriverName = "hdb"
Expand Down
4 changes: 2 additions & 2 deletions driver/stats.tmpl
Expand Up @@ -3,8 +3,8 @@
{{end -}}
{{define "bounds" -}}{{range $k, $v := . -}}{{printf "%10.1f" $k}}{{end}}{{end -}}
openConnections {{.OpenConnections}}
openTransactions {{.OpenConnections}}
openStatements {{.OpenConnections}}
openTransactions {{.OpenTransactions}}
openStatements {{.OpenStatements}}
readBytes {{.ReadBytes}}
writtenBytes {{.WrittenBytes}}
timeUnit {{.TimeUnit}}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Expand Up @@ -14,8 +14,8 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.49.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/prometheus/common v0.50.0 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
golang.org/x/sys v0.18.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
8 changes: 4 additions & 4 deletions go.sum
Expand Up @@ -10,10 +10,10 @@ github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7km
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.49.0 h1:ToNTdK4zSnPVJmh698mGFkDor9wBI/iGaJy5dbH1EgI=
github.com/prometheus/common v0.49.0/go.mod h1:Kxm+EULxRbUkjGU6WFsQqo3ORzB4tyKvlWFOE9mB2sE=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
Expand Down

0 comments on commit 0a07797

Please sign in to comment.