-
Notifications
You must be signed in to change notification settings - Fork 1.7k
fix(net): harden shared state concurrency #6970
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
base: release_v4.8.3
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,10 @@ | |
| import com.google.common.cache.Cache; | ||
| import com.google.common.cache.CacheBuilder; | ||
| import java.util.Arrays; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Service; | ||
|
|
@@ -20,7 +20,7 @@ public class WitnessProductBlockService { | |
| private Cache<Long, BlockCapsule> historyBlockCapsuleCache = CacheBuilder.newBuilder() | ||
| .initialCapacity(200).maximumSize(200).build(); | ||
|
|
||
| private Map<String, CheatWitnessInfo> cheatWitnessInfoMap = new HashMap<>(); | ||
| private Map<String, CheatWitnessInfo> cheatWitnessInfoMap = new ConcurrentHashMap<>(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MUST] Replacing the outer map with ConcurrentHashMap only makes individual map operations safe. The cache get→put at lines 27–39 can let two conflicting blocks for the same witness and height both observe null and overwrite each other; containsKey→put→clear/add can also lose counts, while NodeInfoService may iterate the inner HashSet concurrently. Use one atomic section for detection and update, expose an immutable or synchronized snapshot to readers, and add a real concurrency test.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block processing itself is serialized. Although the subsequent double-production detection runs outside that lock and therefore still has a theoretical interleaving window, triggering it requires a particular thread scheduling sequence. This functionality provides auxiliary records and reporting of witness double production; it does not participate in consensus decisions. Considering the scope of the impact and the maintenance cost, this PR will retain the change that makes the outer map safe for concurrent access without introducing additional synchronization or snapshot mechanisms. |
||
|
|
||
| public void validWitnessProductTwoBlock(BlockCapsule block) { | ||
| try { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package org.tron.core.metrics.blockchain; | ||
|
|
||
| import java.lang.reflect.Method; | ||
| import java.util.List; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
| import org.tron.common.parameter.CommonParameter; | ||
| import org.tron.core.metrics.MetricsKey; | ||
| import org.tron.core.metrics.MetricsUtil; | ||
|
|
||
| public class BlockChainMetricManagerTest { | ||
|
|
||
| @Test | ||
| @SuppressWarnings("unchecked") | ||
| public void missingDuplicateWitnessBlockNumberDefaultsToZero() throws Exception { | ||
| CommonParameter parameter = CommonParameter.getInstance(); | ||
| boolean nodeMetricsEnabled = parameter.isNodeMetricsEnable(); | ||
| String witness = "missing-block-number-" + System.nanoTime(); | ||
| parameter.setNodeMetricsEnable(true); | ||
| try { | ||
| MetricsUtil.counterInc(MetricsKey.BLOCKCHAIN_DUP_WITNESS + witness); | ||
|
|
||
| Method getDupWitness = BlockChainMetricManager.class.getDeclaredMethod("getDupWitness"); | ||
| getDupWitness.setAccessible(true); | ||
| List<DupWitnessInfo> dupWitnesses = (List<DupWitnessInfo>) getDupWitness.invoke( | ||
| new BlockChainMetricManager()); | ||
|
|
||
| DupWitnessInfo dupWitness = dupWitnesses.stream() | ||
| .filter(info -> witness.equals(info.getAddress())) | ||
| .findFirst() | ||
| .orElse(null); | ||
| Assert.assertNotNull(dupWitness); | ||
| Assert.assertEquals(0L, dupWitness.getBlockNum()); | ||
| } finally { | ||
| parameter.setNodeMetricsEnable(nodeMetricsEnabled); | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
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.
[MUST] Holding blockLock only on this call path does not make the check-then-set in syncNext() atomic; SyncService.processBlock() still calls syncNext() without that lock. Two threads can both observe syncChainRequested == null and send requests, after which a valid second response may be treated as BAD_MESSAGE. Put the check, summary creation, state update, and send under a lock shared by every caller, and add a same-peer concurrency test.
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.
In the current workflow,
processBlock()requests the next chain summary once the pending sync queue size falls within the batch threshold, usually before background processing drains the queue. This change already coordinates ChainInventory response handling and background block processing using the sameblockLock. Given the added complexity of expanding the synchronization scope, we will retain the current approach in this PR. If a duplicate-request scenario can be reproduced, we can address that specific path in a follow-up.