-
-
Notifications
You must be signed in to change notification settings - Fork 415
BeforeConnect Hook #1875
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
base: v10
Are you sure you want to change the base?
BeforeConnect Hook #1875
Changes from 2 commits
68e8bb8
7b867af
98f3ff4
6186baf
22a4c8d
5a91137
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,6 +108,30 @@ func TestDBConnectWithStartupNotice(t *testing.T) { | |
| require.NoError(t, db.Ping(context.Background()), "must successfully ping database with long application name") | ||
| } | ||
|
|
||
| func TestBeforeConnect(t *testing.T) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add another test that tests the before connect with multiple go routines to make sure that a race condition is not triggered by accident. A race condition in a before connection hook for an ORM could cripple an application.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a good way to do this would be to set the options to have a max pool of 1. And then try to send multiple queries concurrently in a few go routines. This should trigger a concurrent call of the BeforeConnect method; and given the |
||
| pwd := "dynamic-passwords-from-xkcd" | ||
| opt := pgOptions() | ||
| opt.BeforeConnect = func(ctx context.Context, o *pg.Options) error { | ||
| o.Password = pwd | ||
| return nil | ||
| } | ||
|
|
||
| db := pg.Connect(opt) | ||
| defer db.Close() | ||
|
|
||
| var val int | ||
| _, err := db.QueryOne(pg.Scan(&val), "SELECT 1") | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| if val != 1 { | ||
| t.Fatalf(`got %q, wanted 1`, val) | ||
| } | ||
| if pwd != opt.Password { | ||
| t.Fatalf(`got %s, wanted %s`, opt.Password, pwd) | ||
| } | ||
| } | ||
|
|
||
| func TestOnConnect(t *testing.T) { | ||
| opt := pgOptions() | ||
| opt.OnConnect = func(ctx context.Context, db *pg.Conn) error { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we should copy the options to avoid data race (since this hooks is called concurrently):
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and then reassign to db.opt?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you re assign to db.opt then you need to make that re assignment atomic (*thread safe?). What if this code is being executed in multiple go routines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also if we replace the db.opt here this can lead to a few interesting issues depending on how the user is using the library. What if they change the DB address entirely? what if they add TLS? Beyond simply changing the password or the user that a connection is issued with; changing other variables available in the options object could have a lot of unintended consequences that are currently unknown.
If at all possible we might just want to limit being able to change authentication parameters if we do update the original
db.opts. Maybe limiting it to changing the user, password and TLS config.If they for some reason changed something like the connection pool settings, how would that affect the pool that is already running?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@justcompile I don't think you need to replace
db.opt. Just clone it (when needed for BeforeConnect) and use below in startup:That will not cover all options but is good enough for this particular use case. We can improve it later if needed.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the concurrency issue mentioned & the points around unintended side effects of a user changing the Addr or Pool options, I was looking at something like the above.
But if that's overkill, I can go with the opts.Clone approach.
I'm also locking the mux here rather than within
applyUpdatedConnectionOptionsso that if multiple go routines are callingBeforeConnectit's a bit more deterministicThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep as is
BeforeConnect(ctx context.Context, o *pg.Options) error. It will not work for all options but that is fine. It is an advanced feature. Add add a warning to the comment.