-
Notifications
You must be signed in to change notification settings - Fork 267
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
fix: Prevent data race in email sending #494
base: main
Are you sure you want to change the base?
Conversation
This PR will fix #474 |
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.
It would be better if the PR's title is shorter.
} | ||
|
||
// SendWithContext sends an email through Twilio SendGrid with context.Context | ||
func (cl *Client) SendWithContext(ctx context.Context, email *mail.SGMailV3, headers map[string]string) (*rest.Response, error) { |
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.
This change breaks backward compatibility.
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 wanted to keep backward compatibility, but I broke it to remove the data race, I removed the public "Request" field from the client structure.
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.
It looks like the maintainers are not concerned with keeping backward compatibility and do not adhere to semver.org. For example, the function SetHost
was removed, but the major version was not updated: https://github.com/sendgrid/sendgrid-go/blob/main/CHANGELOG.md#2023-12-01-version-3140.
Therefore, I think it should be acceptable for them if you change the parameters.
However, I suggest mentioning this change in the first few sentences of the PR's description. This way, anyone who comes across this PR will know how to fix their code without having to dive deeply into it.
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.
Maybe it's a good idea to add the -race
flag to the CI so it gets tested every time:
go test ./... -v -race
Yeah, it's not the topic of the PR. But it will prove changes without cloning them locally and running go test
with -race
.
Better to use Mailgun - https://github.com/mailgun/mailgun-go |
The -race flag is already set in the test start. Please check the pull request. |
I carefully reviewed the changes in the pull request and did not find any instances where the -race flag is added to go test. Allow me to explain my proposal more clearly. I suggest modifying this line in the Line 7 in 16f25e4
to: go test -race -coverprofile=profile.out -covermode=atomic "$d" Additionally, I found that in 2018, someone removed the -race flag that was previously present in this commit: e6c23d8. The reason given was that "given the way we present output, we are redundantly running tests with -race, which will make tests run unnecessarily longer, especially for older versions of Go." However, I believe this concern may no longer be relevant for modern versions of Go. Thank you for considering this suggestion. |
Now I understand your point. It is necessary to add the -race flag in go.coverage.sh. |
Fixes
When trying to send several emails at the same time, the emails are not sent because the client uses a shared built-in field
rest.Request
, which is overwritten by different goroutines.Here is a test I wrote to show this:
Started the test using the following command:
go test ./... -v -run=Test_test_client_send_is_thread_safe -race
Here is the console output after running the test before applying my fixes:
After my fixes, the test passes, emails are successfully sent, and the client is now thread-safe.
I replaced the embedded Request in Client with two private fields: apiKey and emailOptions. When creating a client, either apiKey or Username and Password are required. Backward compatibility is broken, but this is a necessary step to eliminate Data Race and make internal fields private.
New Implementation:
I also created the SendWithHeaders method to support additional headers (used in testing). Additionally, I refactored the client creation methods (NewSendClient and NewTwilioEmailSendClient) accordingly.
NewApiKeySendClient:
NewEmailSendClient:
Checklist