Skip to content
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

Bug fix: Mark connection as not good when error on cancellation confirmation #773

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions mssql.go
Expand Up @@ -228,6 +228,8 @@ func (c *Conn) checkBadConn(ctx context.Context, err error, mayRetry bool) error
return nil
case io.EOF:
c.connectionGood = false
case ErrorCancelConfirmation:
c.connectionGood = false
case driver.ErrBadConn:
// It is an internal programming error if driver.ErrBadConn
// is ever passed to this function. driver.ErrBadConn should
Expand Down
4 changes: 4 additions & 0 deletions mssql_test.go
Expand Up @@ -127,6 +127,10 @@ func TestCheckBadConn(t *testing.T) {
{io.EOF, true, false, newRetryableError(io.EOF), false},
{io.EOF, false, true, io.EOF, false},
{io.EOF, true, true, io.EOF, false},
{ErrorCancelConfirmation, false, false, ErrorCancelConfirmation, false},
{ErrorCancelConfirmation, true, false, newRetryableError(ErrorCancelConfirmation), false},
{ErrorCancelConfirmation, false, true, ErrorCancelConfirmation, false},
{ErrorCancelConfirmation, true, true, ErrorCancelConfirmation, false},
{netErr, false, false, netErr, false},
{netErr, true, false, newRetryableError(netErr), false},
{netErr, false, true, netErr, false},
Expand Down
41 changes: 41 additions & 0 deletions queries_test.go
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"io"
"log"
Expand Down Expand Up @@ -1370,6 +1371,46 @@ func TestProcessQueryErrors(t *testing.T) {
}
}

type mockReadWriteCloser struct {
io.ReadWriteCloser
}

func (*mockReadWriteCloser) Read([]byte) (int, error) { return 0, errors.New("fake err") }

func TestProcessQueryCancelConfirmationError(t *testing.T) {
tl := testLogger{t: t}
defer tl.StopLogging()
conn := internalConnection(t, &tl)
defer conn.Close()

stmt, err := conn.prepareContext(context.Background(), "select 1")
if err != nil {
t.Fatal("prepareContext expected to succeed, but it failed with", err)
}
err = stmt.sendQuery(context.Background(), []namedValue{})
if err != nil {
t.Fatal("sendQuery expected to succeed, but it failed with", err)
}
// mock real connection to imitate situation when you write but dont get response
conn.sess.buf.transport = &mockReadWriteCloser{ReadWriteCloser: conn.sess.buf.transport}
// canceling context to try to send attention request
ctx, cancel := context.WithCancel(context.Background())
cancel()

_, err = stmt.processQueryResponse(ctx)
if err == nil {
t.Error("processQueryResponse expected to fail but it succeeded")
}
// should not fail with ErrBadConn because query was successfully sent to server
if err != ErrorCancelConfirmation {
t.Error("processQueryResponse expected to fail with ErrorCancelConfirmation error but failed with other error: ", err)
}

if conn.connectionGood {
t.Fatal("Connection should be in a bad state")
}
}

func TestProcessQueryNextErrors(t *testing.T) {
tl := testLogger{t: t}
defer tl.StopLogging()
Expand Down
4 changes: 3 additions & 1 deletion token.go
Expand Up @@ -85,6 +85,8 @@ const (
// TODO implement more flags
)

var ErrorCancelConfirmation = errors.New("did not get cancellation confirmation from the server")

// interface for all tokens
type tokenStruct interface{}

Expand Down Expand Up @@ -934,7 +936,7 @@ func (t tokenProcessor) nextToken() (tokenStruct, error) {
}
// we did not get cancellation confirmation, something is not
// right, this connection is not usable anymore
return nil, errors.New("did not get cancellation confirmation from the server")
return nil, ErrorCancelConfirmation
}
}

Expand Down