* MM-19548: Add a deadlock retry function for SaveChannel
A deadlock has been seen to occur in the upsertPublicChannelT method
during bulk import.
Here is a brief excerpt:
*** (1) TRANSACTION:
TRANSACTION 3141, ACTIVE 1 sec inserting
INSERT INTO
PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
VALUES
(?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
DeleteAt = ?,
TeamId = ?,
DisplayName = ?,
Name = ?,
Header = ?,
Purpose = ?
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 76 page no 4 n bits 104 index Name of table `mydb`.`PublicChannels` trx id 3141 lock_mode X locks gap before rec insert intention waiting
** (2) TRANSACTION:
TRANSACTION 3140, ACTIVE 1 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
MySQL thread id 50, OS thread handle 140641523848960, query id 3226 172.17.0.1 mmuser update
INSERT INTO
PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
VALUES
(?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
DeleteAt = ?,
TeamId = ?,
DisplayName = ?,
Name = ?,
Header = ?,
Purpose = ?
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 76 page no 4 n bits 104 index Name of table `mydb`.`PublicChannels` trx id 3140 lock_mode X locks gap before rec
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 76 page no 4 n bits 104 index Name of table `mydb`.`PublicChannels` trx id 3140 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)
Following is my analysis:
From the deadlock output, it can be seen that it's due to a gap lock.
And that's clear because the index is Name which is a multi-column index using Name and TeamId.
But interestingly, both transactions seem to be inserting the same data, which is what is puzzling me.
The multi-column index on Name and TeamId will guarantee that they are always unique. And from looking at the code,
it does not seem possible to me that it will try to insert the same data from 2 different transactions.
But even if they do, why does tx 2 try to acquire the same lock again when it already has that ?
Here is what I think the order of events happening
Tx 2 gets a gap lock.
Tx 1 tries to get the same gap lock.
Tx 2 tries to again get the same gap lock ?
The last step is what is puzzling me. Why does an UPSERT statement acquire 2 gap locks ? From my reading of https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html:
> INSERT ... ON DUPLICATE KEY UPDATE differs from a simple INSERT in that an exclusive lock rather than a shared lock is placed on the row to be updated when a duplicate-key error occurs. An exclusive index-record lock is taken for a duplicate primary key value. An exclusive next-key lock is taken for a duplicate unique key value.
From what I understand, the expectation is that there will be one X lock and one gap lock is taken.
But that's not what the deadlock output seems to say.
The general advice on the internet seems to be that deadlocks will happen and not all of them can be understood.
For now, we add a generic deadlock retry function at the store package which can be reused by other queries too.
P.S.: This is a verbatim copy of my investigation posted at https://dba.stackexchange.com/questions/268652/mysql-deadlock-upsert-query-acquiring-gap-lock-twice
Testing:
This is ofcourse hard to test because it is impossible to reproduce this. I have tested this by manually returning an error
and confirming that it indeed retries.
WARN[2020-06-19T11:18:24.9585676+05:30] A deadlock happened. Retrying. caller="sqlstore/channel_store.go:568" error="Error 1213: mydeadlock"
WARN[2020-06-19T11:18:24.959158+05:30] A deadlock happened. Retrying. caller="sqlstore/channel_store.go:568" error="Error 1213: mydeadlock"
WARN[2020-06-19T11:18:24.9595072+05:30] A deadlock happened. Retrying. caller="sqlstore/channel_store.go:568" error="Error 1213: mydeadlock"
WARN[2020-06-19T11:18:24.9595451+05:30] Deadlock happened 3 times. Giving up caller="sqlstore/channel_store.go:579"
ERRO[2020-06-19T11:18:24.9596426+05:30] Unable to save channel. caller="mlog/log.go:175" err_details="Error 1213: mydeadlock" err_where=CreateChannel http_code=500 ip_addr="::1" method=POST path=/api/v4/channels request_id=745bsj13b7f6mnmsbn3t97grbw user_id=xcof1ipipbrfxpfjf6x4p6kx9e
* Fix tests
* Address review comments
* Address review comments
* Add forgotten test
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
* MM-24674 Update channel members by group to look at distinct autoTimezone and manualTimezone.
Instead of just doing a blanket distinct on the column since that returns more entries than expected
* Use JSON extract instead of parsing text
* Use single quotes in mysql query too
* Dont need to prepend users on unambiguous column
* Use json extract instead of shorthand
* Dont count timezone if timezone default length
* CI
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* new job type created that checks for expired mobile sessions and pushes notifications.
* only send session expired notifications if ExtendSessionLengthWithActivity is enabled.
* includes schema change: field added to Sessions table
* Implement prefix/suffix search on teams page
* Make Channel Page prefix/suffix search as well
* address PR comments
* add tests
* fix styling
* take postgres into account
* add more api tests
* update test
* add team store test
* write store test for team and channel
* fix lint
* update description
* revert go mod
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* Migrate GetDeleted method from ChannelStore to return error interface
* Moving i18n translations to the correct place
Co-authored-by: Jesús Espino <jespinog@gmail.com>
* Migrate GetAllChannels and GetAllChannelsCount from ChannelStore to return plain errors
* Moving i18n translations to the correct place
Co-authored-by: Jesús Espino <jespinog@gmail.com>
* [MM-25714] Keeps track of the import lines of the posts while importing to report the right line on error
* Adding review comments
* Reverse the order of the error and error line params
* Enable gossip encryption
* Fix order
* Auto-generate key
* Update gorp fork to include BeginTx
* Add a test for InsertIfExists
And point gorp to a custom branch for now
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* Fixing reply count on new posts
* Fixing tests
* Fixing post reply count on getPostsAround
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* MM-25040: Only return team-associated groups if the team is group-constrained.
MM-25040: Prevents associating a group to a channel if the team doesn't have the group first.
* MM-25040: Fix lints.
* MM-25040: Still add the groupteam if the team is not group-constrained.
* MM-25040: Wraps groupteam upsert in else branch for efficiency.
* MM-25040: Removes unnecessary page iteration.
* MM-25040: Fix typo.
* MM-25040: Moves filtering to SQL.
* MM-25040: Updates tests, check pagination.
* MM-25040: Fix lint error.
* MM-25040: Adds some more group store tests.
* MM-25040: Fix for wrong test parameter.
* Fixing behavior of Replicas and SearchReplicas in canary environments
* Trying to fix tests
* Revert "Trying to fix tests"
This reverts commit 3531da961844f5cb8557efcd3b570b06f362c6df.
* Revert "Fixing behavior of Replicas and SearchReplicas in canary environments"
This reverts commit 0c05901c843e4ccd60c8320fb4e0123b1bacf430.
* Revert "Disable read/search db replicas in TE/E0 (#14400)"
This reverts commit ef5ac519d9.
* Making the store aware of the license
* Readding the unit tests
* Fixing sqlstor supplier tests
* Adding mutex to ensure license write consistency and fixing tests
* Fixing tests
* Fixing tests
* Shuting down server properly during tests
* Trying to fix tests
* Trying to fix the tests
* Skipping flaky tests
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* make Group.Name a pointer to allow null
* fix unit tests
* fix build error
* fix unit test
* ensure Name field not nil
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
We use the same optimization used in MM-23369 to prevent
mysql from using the index in the sort query.
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* MM-24135: Migrate AppError from SaveChannel/channel_store.go
This is the first POC of migration of store app errors to plain error.
We create a few basic error types in the store package and use
them to return the errors from store methods. In the app layer,
we inspect the error and re-create the exact app errors. This lets
us preserve the same error content, but yet move to plain errors.
Since this is a gradual migration, this means that the error inspection
code will be duplicated across the app layer whenever a store method
is invoked. But all of that should go away once we start propagating
the errors higher up the hierarchy.
There have been a significant amount of changes in the storetest and searchtest
layer, primarily because we have to rename the err variable now that it is of
a different type.
* Addressed review comments
* MM-24132: Migrate AppError from SaveDirectChannel/channel_store.go
This PR migrates 2 new methods SaveDirectChannel and CreateDirectChannel
to return error instead of AppError.
We also need to handle the error internally in SaveMultipleMember for now
until that is migrated too.
* MM-24133: Migrate AppError from bot_store.go
* Fix errors
* Fix err
* Fix bad return
* Fix vet errors
* Fix incorrect error check
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
The code was writing to master and immediately after that reading
from a replica causing it to fail intermittently.
Since this is not a very high-traffic table, it should be safe to read
from master always.
* MM-24135: Migrate AppError from SaveChannel/channel_store.go
This is the first POC of migration of store app errors to plain error.
We create a few basic error types in the store package and use
them to return the errors from store methods. In the app layer,
we inspect the error and re-create the exact app errors. This lets
us preserve the same error content, but yet move to plain errors.
Since this is a gradual migration, this means that the error inspection
code will be duplicated across the app layer whenever a store method
is invoked. But all of that should go away once we start propagating
the errors higher up the hierarchy.
There have been a significant amount of changes in the storetest and searchtest
layer, primarily because we have to rename the err variable now that it is of
a different type.
* Addressed review comments
* MM-24132: Migrate AppError from SaveDirectChannel/channel_store.go
This PR migrates 2 new methods SaveDirectChannel and CreateDirectChannel
to return error instead of AppError.
We also need to handle the error internally in SaveMultipleMember for now
until that is migrated too.
* Fix layers
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
* MM-24135: Migrate AppError from SaveChannel/channel_store.go
This is the first POC of migration of store app errors to plain error.
We create a few basic error types in the store package and use
them to return the errors from store methods. In the app layer,
we inspect the error and re-create the exact app errors. This lets
us preserve the same error content, but yet move to plain errors.
Since this is a gradual migration, this means that the error inspection
code will be duplicated across the app layer whenever a store method
is invoked. But all of that should go away once we start propagating
the errors higher up the hierarchy.
There have been a significant amount of changes in the storetest and searchtest
layer, primarily because we have to rename the err variable now that it is of
a different type.
* Addressed review comments
* Made all appError origins to be CreateChannel
* Remove typed internal error
* Fix translations
* fix layer generation
Co-authored-by: mattermod <mattermod@users.noreply.github.com>