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

[MI-3591] Add transactions in the functions to avoid race conditions #344

Merged
merged 10 commits into from
Oct 13, 2023

Conversation

ayusht2810
Copy link
Contributor

Summary

Add transactions in the functions to avoid race conditions

Ticket Link

#327
#330

Duplicate of #340

Copy link
Collaborator

@manojmalik20 manojmalik20 left a comment

Choose a reason for hiding this comment

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

Please fix CI before merging.

@ayusht2810 ayusht2810 merged commit f365ea5 into main Oct 13, 2023
1 of 2 checks passed
@ayusht2810 ayusht2810 deleted the MI-3591 branch October 13, 2023 04:42
@@ -207,15 +207,35 @@ func (p *Plugin) executeLinkCommand(args *model.CommandArgs, parameters []string
return p.cmdError(args.UserId, args.ChannelId, "Unable to create new link.")
}

err = p.store.SaveChannelSubscription(storemodels.ChannelSubscription{
tx, err := p.store.BeginTx()
Copy link
Member

Choose a reason for hiding this comment

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

I don't like this BeginTx here exposed outside the store. I think it's better to have the transactions inside the store. Transactions are an storage concern, no an application concern. So the right level of abstraction is at the store. Specially in this case where you are not doing more than one store call. Why is this transaction needed at all here, and why is not inside the SaveChannelSubscrition method?

@@ -403,72 +408,125 @@ func (s *SQLStore) MattermostToTeamsUserID(userID string) (string, error) {
}

func (s *SQLStore) GetPostInfoByMSTeamsID(chatID string, postID string) (*storemodels.PostInfo, error) {
query := s.getQueryBuilder().Select("mmPostID, msTeamsLastUpdateAt").From("msteamssync_posts").Where(sq.Eq{"msTeamsPostID": postID, "msTeamsChannelID": chatID})
tx, err := s.BeginTx()
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need a transaction here? I think there is no point on having here a transaction, there is an automatically generated transaction by the fact that you are running a query. And you are running a single query here, so you don't need to generate a transaction. Specially because this single query is a Select.

}
postInfo.MSTeamsLastUpdateAt = time.UnixMicro(lastUpdateAt)
return &postInfo, nil
}

func (s *SQLStore) GetPostInfoByMattermostID(postID string) (*storemodels.PostInfo, error) {
query := s.getQueryBuilder().Select("msTeamsPostID, msTeamsChannelID, msTeamsLastUpdateAt").From("msteamssync_posts").Where(sq.Eq{"mmPostID": postID})
tx, err := s.BeginTx()
Copy link
Member

Choose a reason for hiding this comment

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

Same here, and probably most of the cases in this file. The transaction is only needed when you operate with multiple queries, and any of those queries is a modification query.

postInfo.MSTeamsLastUpdateAt = time.UnixMicro(lastUpdateAt)
return &postInfo, nil
}

func (s *SQLStore) SetPostLastUpdateAtByMattermostID(postID string, lastUpdateAt time.Time) error {
query := s.getQueryBuilder().Update("msteamssync_posts").Set("msTeamsLastUpdateAt", lastUpdateAt.UnixMicro()).Where(sq.Eq{"mmPostID": postID})
func (s *SQLStore) SetPostLastUpdateAtByMattermostID(postID string, lastUpdateAt time.Time, tx *sql.Tx) error {
Copy link
Member

Choose a reason for hiding this comment

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

This is more a matter of style, I would use the tx as the first parameter if we want to pass the tx to the store methods. Because my expectation is that every query has the tx parameter at the beginning. but that is not the case here. Anyway, I would love to hide all the transaction complexity from the rest of the application. For example, not using transactions where there are not needed, and where they are needed use something special, like a "two steps function", something like a function that starts the operation, and returns a function that finish (or rollback) the operation depending on the parameter passed (maybe something like err == nil).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty/3:Hard Hard ticket Test Cases Includes unit test cases changes
Projects
None yet
3 participants