Skip to content

replace unmaintained fxhash by maintained rustc-hash#578

Open
ggirol-rc wants to merge 3 commits intotikv:masterfrom
ggirol-rc:remove_unmaintained_fxhash
Open

replace unmaintained fxhash by maintained rustc-hash#578
ggirol-rc wants to merge 3 commits intotikv:masterfrom
ggirol-rc:remove_unmaintained_fxhash

Conversation

@ggirol-rc
Copy link
Contributor

@ggirol-rc ggirol-rc commented Feb 16, 2026

replace unmaintained fxhash by maintained rustc-hash

see https://rustsec.org/advisories/RUSTSEC-2025-0057

This uncovered that some tests were sensitive to the order of HashSets, so I made them deterministic.

Summary by CodeRabbit

  • Bug Fixes

    • Configuration display is now deterministic and consistent across runs.
  • Chores

    • Replaced an internal hashing implementation to improve consistency.
  • Tests

    • Integration tests hardened to handle nondeterministic ordering of configuration members and updated to allow expected-state adjustments during comparisons.

@ti-chi-bot
Copy link

ti-chi-bot bot commented Feb 16, 2026

Welcome @ggirol-rc! It looks like this is your first PR to tikv/raft-rs 🎉

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaced fxhash with rustc-hash across manifests and type aliases, switched DefaultHashBuilder to use rustc_hash::FxHasher, made Configuration Display deterministic by sorting voters, and updated tests to sort voter/learner lists and add a mut binding where required.

Changes

Cohort / File(s) Summary
Workspace Manifests
Cargo.toml, harness/Cargo.toml
Removed fxhash and added rustc-hash in dependencies/dev-dependencies.
Hasher type aliases
src/lib.rs, harness/tests/integration_cases/.../test_raft.rs
Swapped fxhash::FxHasherrustc_hash::FxHasher in BuildHasherDefault aliases and test HashSet typedefs.
Deterministic Display & tests
src/quorum/majority.rs, harness/tests/integration_cases/.../test_raw_node.rs
Configuration::Display now sorts voters before formatting; tests sort voters/learners before equality checks and add mut to a loop binding.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I swapped a hasher, hopped through the mesh,
Voters in order — tidy and fresh.
Tests now tidy their lists with a cheer,
A rabbit’s small tweak keeps outputs clear. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: replacing the unmaintained fxhash dependency with the maintained rustc-hash crate, which is the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can disable poems in the walkthrough.

Disable the reviews.poem setting to disable the poems in the walkthrough.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
harness/tests/integration_cases/test_raft.rs (1)

32-32: HashSet duplication is necessary due to the private type scope in src/lib.rs.

The HashSet type alias in src/lib.rs is private (not exported), so it cannot be imported by test code. This duplication in test_raft.rs is acceptable and necessary. However, if the maintainers decide to expose internal hashing infrastructure as part of the public API, consider re-exporting DefaultHashBuilder and the type aliases to ensure consistency across the codebase.

@ggirol-rc ggirol-rc force-pushed the remove_unmaintained_fxhash branch from d7a1993 to d2980bf Compare February 16, 2026 13:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
harness/tests/integration_cases/test_raw_node.rs (2)

323-326: Consider sorting all order-sensitive fields, not just voters.

Only voters is sorted before comparison, but ConfState also has learners, voters_outgoing, and learners_next fields that come from HashSet iteration and could be non-deterministic. Current test data happens to use single-element or empty collections for those fields in this code path, so this works today — but it's fragile if test cases are added later.

♻️ Suggested defensive sorting
         let mut cs = cs.unwrap();
         exp.voters.sort();
         cs.voters.sort();
+        exp.learners.sort();
+        cs.learners.sort();
+        exp.voters_outgoing.sort();
+        cs.voters_outgoing.sort();
+        exp.learners_next.sort();
+        cs.learners_next.sort();
         assert_eq!(exp, cs);

371-375: Same here — consider sorting all list fields for robustness.

Only learners is sorted, but voters, voters_outgoing, and learners_next in exp2/cs could also have non-deterministic ordering. Current test data is safe (single-element voters, empty outgoing/next), but a helper that normalizes all fields would be more maintainable.

♻️ Suggested defensive sorting
         let mut cs = raw_node.apply_conf_change(&leave_cc).unwrap();
         cs.learners.sort();
+        cs.voters.sort();
+        cs.voters_outgoing.sort();
+        cs.learners_next.sort();
         let mut exp2 = exp2.unwrap();
         exp2.learners.sort();
+        exp2.voters.sort();
+        exp2.voters_outgoing.sort();
+        exp2.learners_next.sort();
         assert_eq!(cs, exp2);

@ggirol-rc ggirol-rc force-pushed the remove_unmaintained_fxhash branch from d2980bf to 43a6807 Compare February 16, 2026 14:45
@ggirol-rc ggirol-rc force-pushed the remove_unmaintained_fxhash branch 2 times, most recently from e144779 to c3650fb Compare March 2, 2026 08:59
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
harness/tests/integration_cases/test_raw_node.rs (1)

323-326: Consider centralizing ConfState normalization for assertions.

Line 323–Line 326 and Line 371–Line 375 normalize different fields inline. A small helper would reduce repetition and avoid missing fields in future cases.

♻️ Suggested consolidation
+fn normalize_conf_state(cs: &mut ConfState) {
+    cs.voters.sort_unstable();
+    cs.learners.sort_unstable();
+    cs.voters_outgoing.sort_unstable();
+    cs.learners_next.sort_unstable();
+}
...
-        let mut cs = cs.unwrap();
-        exp.voters.sort();
-        cs.voters.sort();
+        let mut cs = cs.unwrap();
+        normalize_conf_state(&mut exp);
+        normalize_conf_state(&mut cs);
         assert_eq!(exp, cs);
...
-        let mut cs = raw_node.apply_conf_change(&leave_cc).unwrap();
-        cs.learners.sort();
+        let mut cs = raw_node.apply_conf_change(&leave_cc).unwrap();
+        normalize_conf_state(&mut cs);
         let mut exp2 = exp2.unwrap();
-        exp2.learners.sort();
+        normalize_conf_state(&mut exp2);
         assert_eq!(cs, exp2);

Also applies to: 371-375

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@harness/tests/integration_cases/test_raw_node.rs` around lines 323 - 326,
Tests repeatedly mutate ConfState before assertions by sorting fields (e.g.,
exp.voters.sort(); cs.voters.sort()); extract this into a small helper like
normalize_confstate(conf: &mut ConfState) that sorts all comparable collections
(voters, learners, maybe learners_next/outgoing if present) and call it for both
exp and cs before assert_eq!, replacing the inline sorts in the blocks that
reference exp and cs to avoid duplication and missing fields in future.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@harness/tests/integration_cases/test_raw_node.rs`:
- Around line 323-326: Tests repeatedly mutate ConfState before assertions by
sorting fields (e.g., exp.voters.sort(); cs.voters.sort()); extract this into a
small helper like normalize_confstate(conf: &mut ConfState) that sorts all
comparable collections (voters, learners, maybe learners_next/outgoing if
present) and call it for both exp and cs before assert_eq!, replacing the inline
sorts in the blocks that reference exp and cs to avoid duplication and missing
fields in future.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e144779 and c3650fb.

📒 Files selected for processing (6)
  • Cargo.toml
  • harness/Cargo.toml
  • harness/tests/integration_cases/test_raft.rs
  • harness/tests/integration_cases/test_raw_node.rs
  • src/lib.rs
  • src/quorum/majority.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml

Copy link
Member

@BusyJay BusyJay left a comment

Choose a reason for hiding this comment

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

I'm worried that changing hash method may change the output result order and potentially lead to inconsistency between replicas that runs old and new versions.

use crate::test_util::*;

type HashSet<K> = std::collections::HashSet<K, std::hash::BuildHasherDefault<fxhash::FxHasher>>;
type HashSet<K> = std::collections::HashSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
Copy link
Member

Choose a reason for hiding this comment

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

How about type HashSet<K> = rustc_hash::FxHashSet<K>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed


impl std::fmt::Display for Configuration {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// tests assume Display is deterministic
Copy link
Member

Choose a reason for hiding this comment

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

Don't sort here, as no production code requires deterministic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved the sorting code to a #[cfg(test)] codepath

@ggirol-rc ggirol-rc force-pushed the remove_unmaintained_fxhash branch from c3650fb to 8df7e72 Compare March 2, 2026 14:36
@ggirol-rc
Copy link
Contributor Author

I'm worried that changing hash method may change the output result order and potentially lead to inconsistency between replicas that runs old and new versions.

fwiw I could have a successful leader election between one node running this MR and two nodes running raft-rs 0.7.0

Signed-off-by: Guillaume Girol <guillaume.girol@rubycat.eu>
tests assume that the order of elements is deterministic. Without this
you can be unlucky and get a tests failure like:

```
assertion failed: `(left == right)`'
  left: `"voters=(2 3 1)&&(1) autoleave\n1: StateProbe match=0 next=0\n2: StateProbe match=0 next=1\n3: StateProbe match=0 next=1\n"`
 right: `"voters=(1 2 3)&&(1) autoleave\n1: StateProbe match=0 next=0\n2: StateProbe match=0 next=1\n3: StateProbe match=0 next=1\n"`

Differences (-left|+right):
-voters=(2 3 1)&&(1) autoleave
+voters=(1 2 3)&&(1) autoleave
 1: StateProbe match=0 next=0
 2: StateProbe match=0 next=1
 3: StateProbe match=0 next=1
```

Signed-off-by: Guillaume Girol <guillaume.girol@rubycat.eu>
@ggirol-rc ggirol-rc force-pushed the remove_unmaintained_fxhash branch from 8df7e72 to 8e4cef1 Compare March 19, 2026 15:58
@ggirol-rc
Copy link
Contributor Author

Is there anything actionable which can move this forward?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants