Skip to content

Adjust Conn.QueryContext #212

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

alexbrainman
Copy link
Owner

The change rewrites function code.

Fixes #199

The change rewrites function code.

Fixes #199
conn.go Outdated
case <-ctx.Done():
// context has been cancelled or has expired, cancel the statement
if err := os.Cancel(); err != nil {
return nil, err

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this line gets hit, nothing will ever read from rowsChan/errorChan - won't that leave the wrapQuery goroutine permanently blocked?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapQuery method does not use channels anymore. It only calls ODBC APIs. If os.Cancel (which is also an ODBC API), did not unblock the ODBC API call execution, then I do not know what else can unblock it.

Perhaps I do not understand the code or your question.

Copy link

@Shamus03 Shamus03 Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "the wrapQuery goroutine" I meant "the goroutine that calls wrapQuery" - this chunk of code:

go func() {
	err := c.wrapQuery(ctx, os, dargs)
	if err != nil {
		errorChan <- err
		return
	}
	os.usedByRows = true
	rowsChan <- &Rows{os: os}
}()

Since errorChan and rowsChan are both unbuffered channels, unless something eventually closes or reads from them, this chunk of code could potentially block forever.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining. You are correct. I adjusted waitQuery code to read from errorChan and rowsChan channels. Hopefully this addresses your concern.

conn.go Outdated
select {
// ignore the error and return context.DeadlineExceeded instead
case <-errorChan:
return nil, context.DeadlineExceeded

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this bubble ctx.Err()? The context may not have been canceled due to a deadline - a context.Context can be canceled several ways

Suggested change
return nil, context.DeadlineExceeded
return nil, ct.Err()

Copy link
Owner Author

@alexbrainman alexbrainman Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this in my original version of my code, but then this test failed here

t.Fatalf("Unexpected error value: should=%s, is=%s", context.DeadlineExceeded, err)

The error reported in the test was MSSQL driver specific. And I did not want to change the test.

Should I adjust the test instead?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the test should be adjusted. Consider an HTTP handler:

func handleWebRequest(r *http.Request, w http.ResponseWriter) {
    ctx := r.Context()
    _, err := db.QueryContext(ctx, "select some_long_query")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    // other http stuff
}

Followed by something like

curl http://localhost/my_handler
# hit ctrl+c shortly after to cancel the request

I would expect the error there to be context.Canceled, or at least not context.DeadlineExceeded

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used your original suggestion. Hopefully it is good enough.

@Shamus03
Copy link

Thank you for making this change! We have been stuck on an older version of this package for a really long time!

Copy link
Owner Author

@alexbrainman alexbrainman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for review.

Copy link
Owner Author

@alexbrainman alexbrainman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed some changes.

PTAL.

Thank you.

Alex

conn.go Outdated
case <-ctx.Done():
// context has been cancelled or has expired, cancel the statement
if err := os.Cancel(); err != nil {
return nil, err
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining. You are correct. I adjusted waitQuery code to read from errorChan and rowsChan channels. Hopefully this addresses your concern.

conn.go Outdated
select {
// ignore the error and return context.DeadlineExceeded instead
case <-errorChan:
return nil, context.DeadlineExceeded
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used your original suggestion. Hopefully it is good enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

QueryContext raises panic: send on closed channel
2 participants