-
Notifications
You must be signed in to change notification settings - Fork 8.7k
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
bugfix: fix deadlock and lock optimization #1605
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #1605 +/- ##
=============================================
+ Coverage 47.14% 47.15% +<.01%
Complexity 1785 1785
=============================================
Files 364 364
Lines 13260 13251 -9
Branches 1644 1642 -2
=============================================
- Hits 6252 6248 -4
+ Misses 6326 6324 -2
+ Partials 682 679 -3
Continue to review full report at Codecov.
|
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.
LGTM
good! |
Long lockingTransactionId = null;
synchronized (bucketLockMap) {
lockingTransactionId = bucketLockMap.get(pk);
if (lockingTransactionId == null) {
//No existing lock
bucketLockMap.put(pk, transactionId);
}
} |
@Justice-love Long previousTransactionId = bucketLockMap.putIfAbsent(pk, transactionId);
if (previousTransactionId == null) {
//No existing lock
Set<String> keysInHolder = bucketHolder.get(bucketLockMap);
if (keysInHolder == null) {
bucketHolder.putIfAbsent(bucketLockMap, new ConcurrentSet<String>());
keysInHolder = bucketHolder.get(bucketLockMap);
}
keysInHolder.add(pk);
} else if (previousTransactionId == transactionId) {
// Locked by me
continue;
} else {
LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + previousTransactionId);
try {
// Release all acquired locks.
branchSession.unlock();
} catch (TransactionException e) {
throw new FrameworkException(e);
}
return false;
} |
i known, there is no problem code like this. but i think appropriate range of synchronized block is the basic rule and also can resolved this problem. |
@Justice-love |
@BeiKeJieDeLiuLangMao |
@BeiKeJieDeLiuLangMao If a branch session could unlock itself in parallel, there will be a problem without the synchronized in releaseLock. Do you think it's possible? |
@ggndnn @Override
public boolean releaseLock(List<RowLock> rowLock) {
ConcurrentHashMap<ConcurrentHashMap<String, Long>, Set<String>> lockHolder = branchSession.getLockHolder();
if (lockHolder == null || lockHolder.size() == 0) {
return true;
}
Iterator<Map.Entry<ConcurrentHashMap<String, Long>, Set<String>>> it = lockHolder.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<ConcurrentHashMap<String, Long>, Set<String>> entry = it.next();
ConcurrentHashMap<String, Long> bucket = entry.getKey();
Set<String> keys = entry.getValue();
for (String key : keys) {
bucket.remove(key, branchSession.getTransactionId());
}
}
lockHolder.clear();
return true;
} And i am thinking about the problem what your talk about. If concurrently invoke |
@BeiKeJieDeLiuLangMao
|
@ggndnn
So, use |
@BeiKeJieDeLiuLangMao |
@ggndnn I reviewed the code about lock and unlock.
Now let's pay attention to unlock process:
Now let's consider follow situation:
Finally, i think the whole process is safe. I will commit the code what i show you before. And as you said, ConcurrentHashMap's api uses synchronized block, but only happe when hash conflict, bucketMap's hash conflict, very low possibility i think. |
@BeiKeJieDeLiuLangMao By the way. I said synchronized, i was just a joke, as I said, never mind. ConcurrentMap is good enough, though we used synchronized, jvm may use spinlock first. And hash conflict is not the condition whether synchronized block was used, I think your meaning was something like Your code looks good to me now. Maybe someone else needs further review. |
server/src/main/java/io/seata/server/lock/memory/MemoryLocker.java
Outdated
Show resolved
Hide resolved
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 single responsibility principle, first || second is enough.
I think LockManagerTest#acquireLock_failed
already do what you want to do.
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.
LGTM now. Thanks @BeiKeJieDeLiuLangMao.
The Unit testing is passed. |
@BeiKeJieDeLiuLangMao You need to associate your github account. |
@xingfudeshi |
Please re-push the commit. |
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.
LGTM.
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.
ConcurrentHashMap -> ConcurrentMap
server/src/main/java/io/seata/server/lock/DefaultLockManager.java
Outdated
Show resolved
Hide resolved
Signed-off-by: 贝克街的流浪猫 <517375685@qq.com>
Signed-off-by: 贝克街的流浪猫 <517375685@qq.com>
Signed-off-by: 贝克街的流浪猫 <517375685@qq.com>
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.
LGTM
Signed-off-by: 贝克街的流浪猫 <517375685@qq.com>
Ⅰ. Describe what this PR did
Fix deadlock of MemoryLocker & sort locks by PK before acquire.
Ⅱ. Does this pull request fix one issue?
Fixed #1603.
Ⅲ. Why don't you add test cases (unit test/integration test)?
No, i add one, it is
io.seata.server.lock.LockManagerTest#deadlockTest
.Ⅳ. Describe how to verify it
Run
io.seata.server.lock.LockManagerTest#deadlockTest
.Ⅴ. Special notes for reviews
All description is in #1603.