Skip to content

[Bug] Recognize IoTConsensusV2 class name aliases on ConfigNode - #18989

Open
luoluoyuyu wants to merge 1 commit into
apache:masterfrom
luoluoyuyu:fix/cn-normalize-consensus-protocol-alias
Open

luoluoyuyu wants to merge 1 commit into
apache:masterfrom
luoluoyuyu:fix/cn-normalize-consensus-protocol-alias

Conversation

@luoluoyuyu

@luoluoyuyu luoluoyuyu commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Description

Root cause

ConfigNode compared the raw value of data_region_consensus_protocol_class with ConsensusFactory.IOT_CONSENSUS_V2, while DataNode normalizes the configured value through ConsensusFactory.normalizeConsensusProtocolClass before using it.

When the configuration uses the legacy org.apache.iotdb.consensus.pipe.PipeConsensus name or the implementation class name org.apache.iotdb.consensus.pipe.IoTConsensusV2, ConfigNode concluded that IoTConsensusV2 was not configured:

  • createInitialConsensusPipes() never created the __consensus.* pipes when a database was created;
  • checkAndRepairConsensusPipes() returned immediately, so PipeMetaSyncer could never repair the missing pipes;
  • RouteBalancer disabled the automatic data region leader balance (IS_ENABLE_AUTO_LEADER_BALANCE_FOR_DATA_REGION became false).

Since the IoTConsensusV2 write path only writes the local state machine, the missing pipes silently degraded the data region replication factor to one: no error was reported, syncLag stayed at zero, and the region peers could all end up as followers.

The value persisted by SystemPropertiesUtils.storeSystemParameters() is already normalized, so the degradation originated at the configuration boundary; on a restart, ConfigNodeStartupCheck.checkSystemProperties() overwrote CONF with the persisted canonical value and the cluster silently healed.

Fix

  • Normalize once at the configuration boundary (ConfigNodeConfig#setDataRegionConsensusProtocolClass) so CONF never holds an alias regardless of the source (properties file, system properties, programmatic setup).
  • Normalize in ConfigNodeDescriptor when the value is read, and log a WARN when a legacy name is found, so the degradation is visible in the ConfigNode log.
  • Add RegionMaintainHandler#isIoTConsensusV2Configured() and use it at the four comparison sites (createNewRegionPeer, createInitialConsensusPipes, checkAndRepairConsensusPipes, isIoTConsensusV2DataRegion).
  • Normalize the cached value in RouteBalancer.
  • Normalize the value sent by a joining ConfigNode in ConfigManager#checkConfigNodeGlobalConfig, so a mixed-version cluster is not rejected during a rolling upgrade.

Tests

  • ConfigNodeConfigTest#testDataRegionConsensusProtocolClassNormalizesLegacyNames: both aliases are stored in canonical form; canonical and Ratis values are preserved.
  • RegionMaintainHandlerConsensusPipeTest#testCreatesMissingPipesWhenLegacyConsensusProtocolAliasConfigured: the periodic self-healing path creates the pipes when the legacy alias is configured.
  • RegionMaintainHandlerConsensusPipeTest#testCreatesInitialPipesWhenImplementationClassNameConfigured: database creation creates the initial pipes when the implementation class name is configured.

The three new tests fail on the unpatched code and pass with the fix. RegionMaintainHandlerTest (30 tests), RemovePeerRestartConsensusTest and RestartDuringRemovePeerTest (14 tests) and the ConfigNodePropertiesTest ArchUnit rule still pass. Both the en and the zh locale sources compile, and mvn spotless:check -pl iotdb-core/confignode passes.

Note on existing clusters

After upgrading, the pipes are recreated by PipeMetaSyncer on the ConfigNode leader, but data written while the pipes were missing only exists in the leader's state machine. Whether it is back-filled depends on the pipe/snapshot start semantics, so operators of an affected cluster should verify the replicas (for example with a backup or a replica consistency check) instead of assuming the history is repaired automatically.


This PR has:

  • been self-reviewed.
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths.
  • added documentation for new or modified features or behaviors.
  • been tested in a test IoTDB cluster.

Key changed/added classes (or packages if there are too many classes) in this PR
  • org.apache.iotdb.confignode.conf.ConfigNodeConfig
  • org.apache.iotdb.confignode.conf.ConfigNodeDescriptor
  • org.apache.iotdb.confignode.procedure.env.RegionMaintainHandler
  • org.apache.iotdb.confignode.manager.load.balancer.RouteBalancer
  • org.apache.iotdb.confignode.manager.ConfigManager
  • org.apache.iotdb.confignode.i18n.ConfigNodeMessages (en / zh)

ConfigNode compared the raw value of data_region_consensus_protocol_class with
ConsensusFactory.IOT_CONSENSUS_V2, while DataNode normalizes the configured value
through ConsensusFactory.normalizeConsensusProtocolClass before using it. When the
configuration used the legacy org.apache.iotdb.consensus.pipe.PipeConsensus name
or the implementation class name
org.apache.iotdb.consensus.pipe.IoTConsensusV2, ConfigNode concluded that
IoTConsensusV2 was not configured and therefore:

- createInitialConsensusPipes() never created the __consensus.* pipes on database
  creation;
- checkAndRepairConsensusPipes() returned immediately, so PipeMetaSyncer could
  never repair missing pipes;
- RouteBalancer disabled the automatic data region leader balance.

Since the IoTConsensusV2 write path only writes the local state machine, the
missing pipes silently degraded the data region replication factor to one: no
error was reported and syncLag stayed at zero.

Normalize the value once at the configuration boundary so CONF never holds an
alias, log the normalization when the configuration file uses an alias, keep the
comparison sites and the RouteBalancer cache defensive, and normalize the value
sent by a joining ConfigNode so a mixed-version cluster is not rejected.

@Caideyipi Caideyipi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review

The normalization is applied when ConfigNode reads its setting and when it compares a joining node's parameters, so both startup and mixed-version registration use the canonical class name. The pipe creation and repair checks follow the same alias handling, and the new tests cover both legacy names. I found no blocking issue in the changed paths.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants