Fixed sqlserver not actually getting a lock if lock is already set #1186
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR fixes the SQL Server not actually getting a lock if lock is already set (e.g. by another instance).
As mentioned in this comment by @dhui, the SQL statement for
sp_getapplock
is not correct.I tested the locking with this script and added some debugging logging as seen in this commit (debug log is not actually part of this PR, I only used it for testing).
The result before applying the fixes was:
The first two lines are from
d1, err := p.Open(connectionString)
. Thend1
acquires the lock (line 3). Then_, err = p.Open(connectionString) // d2
should fail when trying to acquire the second lock, but it does not - the code succeeds but the lock is actually not acquired (line 5). The code actually fails whend2
tries to unlock - it can't because it hasn't acquired the lock in the first place (line 6+).This is exactly right, the SELECT statement is missing and this PR fixes this, along with some other improvements to the locking:
From the documentation the
Update
should be used when updating resources, which is not the case here. The documentation is not entirely clear to me, but from what I can tell, this is the same asExclusive
with added mechanism, that prevents deadlocks from read-lock-update pattern. I think theExclusive
is the right choice here, so the PR also changes that.From the documentation:
Since we are acquiring lock outside the transaction, the
Session
is the correct option to use. I actually tried using theTransaction
value as the LockOwner and the result was-999
, which "Indicates a parameter validation or other call error".I also set the timeout of the
sp_getapplock
to infinite, similar to what is used in the postgres implementation. Since migrations can take a long time (e.g. updating an index on a large database), I think that the only good options for timeout are either infinite or somehow configurable by the user. Since the second option is probably not feasible in the current architecture of this library, I opted for the first one. One PR is already open addressing this timeout, but it would still incorrectly obtain the lock if another instance was holding the lock for more than 10 seconds.This PR should close #1123 and #253.
I didn't manage to get the tests working locally, but I'm hoping the tests will pass in CI.
Lastly, the result of the above script with fixes applied (and non-infinite timeout for testing):
When
d2
tries to acquire the lock, it fails with-1: The lock request timed out
after timeout as expected.