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

handler sql error #15

Merged
merged 1 commit into from
Jan 30, 2018
Merged

handler sql error #15

merged 1 commit into from
Jan 30, 2018

Conversation

xuzhenglun
Copy link
Collaborator

Now if any SQL error happens, After hook will not be called.
It's cause OpenTracing span can't be finished normally. And the span will disappear on OpenTracing dashboard(I'm using Jaeger).
It makes lot's of inconvenient because the only thing I really care about is the error message.

@coveralls
Copy link

coveralls commented Dec 7, 2017

Coverage Status

Coverage increased (+2.5%) to 72.143% when pulling d7e9749 on xuzhenglun:master into b4a12ba on gchaincl:master.

@keegancsmith
Copy link
Collaborator

Related to #10

You are breaking the public API here. If we merged this PR everyones build would break. I think some point we need a nice new API to cover this use case + better performance.

but in the meantime, can I suggest changing the PR to adding a

type ErrorHook interface {
  AfterError(ctx context.Context, query string, args ...interface{}, err error) (context.Context, error)
}

then if hooks implements ErrorHook, you can call AfterError. What do you think @gchaincl?

@qustavo
Copy link
Owner

qustavo commented Dec 8, 2017

@keegancsmith love the ErrorHook interface it's more specific and doesn't breaks the current API.
Do we need the Before,After controls?, wouldn't just Error be enough?

@keegancsmith
Copy link
Collaborator

Don't need Before. Just calling it Error rather than AfterError is likely clear enough since I can't think of a way to report an error before anything has happened.

@qustavo
Copy link
Owner

qustavo commented Dec 9, 2017

@keegancsmith now I miss understood your point, I assumed that having an AfterError implied an BeforeError too. What about OnError?

@xuzhenglun
Copy link
Collaborator Author

Ok, I will do some changes. 😄

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.4%) to 68.182% when pulling 3accd84 on xuzhenglun:master into b4a12ba on gchaincl:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-1.4%) to 68.182% when pulling 3accd84 on xuzhenglun:master into b4a12ba on gchaincl:master.

@xuzhenglun
Copy link
Collaborator Author

Should I hook the error happens in PrepareContext?
If I do so, OnError can be called before After. Is that weird?

@keegancsmith
Copy link
Collaborator

I think OnError should only ever be called on errors returned while executing the statement/query on the wrapped driver. So this is some ugly code and does a type check while inside the ExecContext impl (which isn't performant), but something like this

func (conn *ExecerContext) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
    var err error

    list := namedToInterface(args)

    // Exec `Before` Hooks
    if ctx, err = conn.hooks.Before(ctx, query, list...); err != nil {
        return nil, err
    }

    results, err := conn.execContext(ctx, query, args)
    if err != nil {
        if errorHook, ok := conn.hooks.(ErrorHook); ok {
            if _, err2 := errorHook.OnError(ctx, err, query, list...); err2 != nil {
                return results, err2
            }
        }
        return results, err
    }

    if ctx, err = conn.hooks.After(ctx, query, list...); err != nil {
        return nil, err
    }

    return results, err
}

@keegancsmith
Copy link
Collaborator

Oh I see that is what you did, didn't notice you pushed a commit. Let me review what you have.

sqlhooks.go Outdated
@@ -110,6 +119,9 @@ func (conn *ExecerContext) ExecContext(ctx context.Context, query string, args [

results, err := conn.execContext(ctx, query, args)
if err != nil {
if h, ok := conn.hooks.(OnError); ok {
ctx, err = h.OnError(ctx, err, query, list...)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think you should override err. Otherwise OnError could supress errors that happen by returning nil for an error, which is likely won't most people will do.

I'd expect that if OnError did return a non-nil err, you return that error, otherwise you would return the original err.

Copy link
Owner

Choose a reason for hiding this comment

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

This is a good point, while error overriding/suppressing can generate confusions it might also be helpful if you want to bypass driver specific errors. I'm not sure here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If err is suppressed, hook must generate another results.
If both of them are nil, it is easy to panic. maybe I really need to change it to just like @keegancsmith said.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think of Hooks as something which doesn't influence behaviour, rather observes it. We currently can mutate what ctx is, but that is more to do with it being useful to store some "request" data in it that we can then access in After. I think being able to cancel out err will lead to API which is easy to make a mistake in. Especially since we could then return nil, nil.

Copy link
Owner

@qustavo qustavo Dec 12, 2017

Choose a reason for hiding this comment

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

@keegancsmith I agree, but do you think it might be useful to suppress or modify errors?
I'm not talking about implementing it in this Hook or not, but if it's useful we might need to come up with some way to do it.

.gitignore Outdated
@@ -22,3 +22,6 @@ _testmain.go
*.exe
*.test
*.prof

.idea
Copy link
Owner

Choose a reason for hiding this comment

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

Do you mind to not add these?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok, sorry for that. I will remove it.

sqlhooks.go Outdated
@@ -110,6 +119,9 @@ func (conn *ExecerContext) ExecContext(ctx context.Context, query string, args [

results, err := conn.execContext(ctx, query, args)
if err != nil {
if h, ok := conn.hooks.(OnError); ok {
ctx, err = h.OnError(ctx, err, query, list...)
}
Copy link
Owner

Choose a reason for hiding this comment

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

This is a good point, while error overriding/suppressing can generate confusions it might also be helpful if you want to bypass driver specific errors. I'm not sure here.

@coveralls
Copy link

coveralls commented Dec 11, 2017

Coverage Status

Coverage decreased (-4.0%) to 65.625% when pulling cbccdd4 on xuzhenglun:master into b4a12ba on gchaincl:master.

@qustavo
Copy link
Owner

qustavo commented Dec 14, 2017

I think the question here is if Error hooks should just observe the errors or actually be able to modify it.
I like the approach @xuzhenglun has taken here, WDYT @keegancsmith ?

@keegancsmith
Copy link
Collaborator

Yeah this is the approach I think is the correct one. It technically allows you to modify the error, but if you return nil as error it returns the original error.

So all thats missing is some docstrings, but LGTM

@keegancsmith
Copy link
Collaborator

keegancsmith commented Dec 14, 2017

Should we bother with returning a context from OnError? It in no way affects anything

@qustavo
Copy link
Owner

qustavo commented Dec 15, 2017

@keegancsmith right, it's useless

@coveralls
Copy link

coveralls commented Dec 15, 2017

Coverage Status

Coverage decreased (-2.7%) to 66.879% when pulling aaebe3c on xuzhenglun:master into b4a12ba on gchaincl:master.

@qustavo qustavo mentioned this pull request Jan 26, 2018
Copy link
Collaborator

@keegancsmith keegancsmith left a comment

Choose a reason for hiding this comment

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

LGTM I finally gave this a proper review. Sorry for the delayed review asking for changes, but we want to merge this in and tag a new release of sqlhooks :)

If you don't have time to make these changes, I'm happy to do them.

defer span.Finish()
span.SetTag("error", true)
span.LogFields(
log.String("error_msg", err.Error()),
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is log.Error(err) for logging an error field.

@@ -28,3 +28,8 @@ func (h *Hook) After(ctx context.Context, query string, args ...interface{}) (co
h.log.Printf("Query: `%s`, Args: `%q`. took: %s", query, args, time.Since(ctx.Value("started").(time.Time)))
return ctx, nil
}

func (h *Hook) OnError(ctx context.Context, err error, query string, args ...interface{}) error {
h.log.Printf("Error: %v, Took: %s", err, time.Since(ctx.Value("started").(time.Time)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Including the query and args may be useful, like we do above for After

sqlhooks.go Outdated
@@ -9,12 +9,27 @@ import (
// Hook is the hook callback signature
type Hook func(ctx context.Context, query string, args ...interface{}) (context.Context, error)

// ErrorHandler is the error handling callback signature
type ErrorHandler func(ctx context.Context, err error, query string, args ...interface{}) error
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can I suggest calling this ErrorHook to be more consistent with Hook above?

sqlhooks.go Outdated
// Hooks instances may be passed to Wrap() to define an instrumented driver
type Hooks interface {
Before(ctx context.Context, query string, args ...interface{}) (context.Context, error)
After(ctx context.Context, query string, args ...interface{}) (context.Context, error)
}

// ErrHook instances will be called if any error happens
type ErrHook interface {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The idiomatic name for an interface with a single function is the function name with er suffix. EG fmt.Stringer has a String function. So in this case the interface name would be OnErrorer.

sqlhooks.go Outdated
return e
}
return err
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

you could simplify the below code if you took a Hook instead of an ErrHook. Then if the Hook implements the error hook interface you use it, otherwise you just return the err.

@xuzhenglun
Copy link
Collaborator Author

OK, I will makes those changes tomorrow. My time zone is UTC+8, I really have to sleep now.

@xuzhenglun
Copy link
Collaborator Author

Sorry for my delayed change because of work busy recently. Please let me know if anything Improperly.

Copy link
Collaborator

@keegancsmith keegancsmith left a comment

Choose a reason for hiding this comment

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

LGTM. I'll let @gchaincl take a look and merge.

@qustavo qustavo merged commit 928fba3 into qustavo:master Jan 30, 2018
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.

4 participants