Bump hashbrown, foldhash; Fix some compile errors in master (#3722)

# Description of Changes

There were mentions of `hashbrown` in the repo that did not go through
`spacetimedb_data_structures::map`.
This caused compile errors on master when running certain tests locally.
These have been replaced with the proper imports.

The PR also bump hashbrown to 0.16.1 and foldhash to 0.2.0.

# API and ABI breaking changes

None

# Expected complexity level and risk

2

# Testing

Covered by existing tests.
This commit is contained in:
Mazdak Farrokhzad
2025-11-25 13:17:24 +01:00
committed by GitHub
parent 75d35d324b
commit ed2a18cff7
22 changed files with 86 additions and 72 deletions
Generated
+20 -12
View File
@@ -2184,6 +2184,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "foreign-types"
version = "0.3.2"
@@ -2577,18 +2583,20 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"rayon",
"serde",
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"rayon",
"serde",
"serde_core",
]
[[package]]
@@ -3156,7 +3164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown 0.16.0",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
@@ -4430,7 +4438,7 @@ checksum = "9c2294f53fe0d6a715ad1877300fe5432033db1e5804800f82ee84840bd09894"
dependencies = [
"allocator-api2",
"bumpalo",
"hashbrown 0.16.0",
"hashbrown 0.16.1",
"oxc_data_structures",
"oxc_estree",
"rustc-hash",
@@ -7132,13 +7140,15 @@ dependencies = [
name = "spacetimedb-bench"
version = "1.9.0"
dependencies = [
"ahash 0.8.12",
"anyhow",
"anymap",
"byte-unit",
"clap 4.5.50",
"criterion",
"foldhash",
"foldhash 0.2.0",
"futures",
"hashbrown 0.16.1",
"iai-callgrind",
"iai-callgrind-macros",
"iai-callgrind-runner",
@@ -7449,7 +7459,6 @@ dependencies = [
"fs_extra",
"futures",
"futures-util",
"hashbrown 0.15.5",
"hex",
"hostname",
"http 1.3.1",
@@ -7547,7 +7556,7 @@ version = "1.9.0"
dependencies = [
"ahash 0.8.12",
"crossbeam-queue",
"hashbrown 0.15.5",
"hashbrown 0.16.1",
"nohash-hasher",
"serde",
"smallvec",
@@ -7738,7 +7747,7 @@ version = "1.9.0"
dependencies = [
"decorum",
"ethnum",
"hashbrown 0.15.5",
"hashbrown 0.16.1",
"smallvec",
]
@@ -7912,7 +7921,6 @@ dependencies = [
"derive_more 0.99.20",
"enum-as-inner",
"enum-map",
"hashbrown 0.15.5",
"indexmap 2.12.0",
"insta",
"itertools 0.12.1",
+2 -2
View File
@@ -179,7 +179,7 @@ env_logger = "0.10"
ethnum = { version = "1.5.0", features = ["serde"] }
flate2 = "1.0.24"
flume = { version = "0.11", default-features = false, features = ["async"] }
foldhash = "0.1.4"
foldhash = "0.2.0"
fs-err = "2.9.0"
fs_extra = "1.3.0"
fs2 = "0.4.3"
@@ -189,7 +189,7 @@ futures-util = "0.3"
getrandom02 = { package = "getrandom", version = "0.2" }
git2 = "0.19"
glob = "0.3.1"
hashbrown = { version = "0.15", default-features = false, features = ["equivalent", "inline-more"] }
hashbrown = { version = "0.16.1", default-features = false, features = ["equivalent", "inline-more", "rayon", "serde"] }
headers = "0.4"
heck = "0.4"
hex = "0.4.3"
+2
View File
@@ -52,6 +52,7 @@ spacetimedb-standalone = { path = "../standalone" }
spacetimedb-table = { path = "../table" }
spacetimedb-testing = { path = "../testing" }
ahash.workspace = true
anyhow.workspace = true
anymap.workspace = true
byte-unit.workspace = true
@@ -59,6 +60,7 @@ clap.workspace = true
criterion.workspace = true
futures.workspace = true
foldhash.workspace = true
hashbrown.workspace = true
lazy_static.workspace = true
log.workspace = true
mimalloc.workspace = true
+3 -3
View File
@@ -1,12 +1,12 @@
use core::{hint::black_box, iter::repeat_with, time::Duration};
use core::{hash::BuildHasherDefault, hint::black_box, iter::repeat_with, time::Duration};
use criterion::{
criterion_group, criterion_main,
measurement::{Measurement as _, WallTime},
BenchmarkGroup, Criterion,
};
use foldhash::{HashSet, HashSetExt};
use hashbrown::{hash_map::Entry, HashMap};
use itertools::Itertools as _;
use spacetimedb_data_structures::map::{Entry, HashMap};
use spacetimedb_sats::layout::Size;
use spacetimedb_table::indexes::{PageIndex, PageOffset, RowPointer, SquashedOffset};
use spacetimedb_table::table_index::unique_direct_index::UniqueDirectIndex;
@@ -191,7 +191,7 @@ impl Index for IBTree {
}
#[derive(Clone)]
struct IAHash(HashMap<K, RowPointer>);
struct IAHash(HashMap<K, RowPointer, BuildHasherDefault<ahash::AHasher>>);
impl Index for IAHash {
const NAME: &'static str = "IAHash";
fn new() -> Self {
-1
View File
@@ -60,7 +60,6 @@ flume.workspace = true
fs2.workspace = true
futures.workspace = true
futures-util.workspace = true
hashbrown = { workspace = true, features = ["rayon", "serde"] }
hex.workspace = true
hostname.workspace = true
http.workspace = true
+2 -3
View File
@@ -1,5 +1,6 @@
use crate::host::module_host::UpdatesRelValue;
use anyhow::Result;
use hashbrown::HashMap;
use spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};
use spacetimedb_execution::{Datastore, DeltaStore, Row};
use spacetimedb_lib::metrics::ExecutionMetrics;
use spacetimedb_primitives::ColList;
@@ -7,8 +8,6 @@ use spacetimedb_sats::product_value::InvalidFieldError;
use spacetimedb_subscription::SubscriptionPlan;
use spacetimedb_vm::relation::RelValue;
use crate::host::module_host::UpdatesRelValue;
/// Evaluate a subscription over a delta update.
/// Returns `None` for empty updates.
///
@@ -1253,7 +1253,6 @@ mod tests {
use crate::subscription::query::compile_read_only_query;
use crate::subscription::TableUpdateType;
use core::fmt;
use hashbrown::HashMap;
use itertools::Itertools;
use pretty_assertions::assert_matches;
use spacetimedb_client_api_messages::energy::EnergyQuanta;
@@ -1262,6 +1261,7 @@ mod tests {
TableUpdate, Unsubscribe, UnsubscribeMulti,
};
use spacetimedb_commitlog::{commitlog, repo};
use spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};
use spacetimedb_datastore::system_tables::{StRowLevelSecurityRow, ST_ROW_LEVEL_SECURITY_ID};
use spacetimedb_durability::{Durability, EmptyHistory, TxOffset};
use spacetimedb_execution::dml::MutDatastore;
@@ -12,14 +12,16 @@ use crate::subscription::delta::eval_delta;
use crate::subscription::websocket_building::BuildableWebsocketFormat;
use crate::worker_metrics::WORKER_METRICS;
use core::mem;
use hashbrown::hash_map::OccupiedError;
use hashbrown::{HashMap, HashSet};
use parking_lot::RwLock;
use prometheus::IntGauge;
use spacetimedb_client_api_messages::websocket::{
BsatnFormat, CompressableQueryUpdate, FormatSwitch, JsonFormat, QueryId, QueryUpdate, SingleQueryUpdate,
};
use spacetimedb_data_structures::map::{Entry, IntMap};
use spacetimedb_data_structures::map::HashCollectionExt as _;
use spacetimedb_data_structures::map::{
hash_map::{Entry, OccupiedError},
HashMap, HashSet, IntMap,
};
use spacetimedb_datastore::locking_tx_datastore::state_view::StateView;
use spacetimedb_durability::TxOffset;
use spacetimedb_expr::expr::CollectViews;
+12 -15
View File
@@ -1,24 +1,21 @@
use super::module_subscription_manager::QueriedTableIndexIds;
use itertools::Either;
use smallvec::SmallVec;
use spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};
use spacetimedb_datastore::{
locking_tx_datastore::{state_view::StateView, TxId},
traits::TxData,
};
use spacetimedb_execution::{Datastore, DeltaStore, Row};
use spacetimedb_lib::{query::Delta, AlgebraicValue, ProductValue};
use spacetimedb_primitives::{IndexId, TableId};
use spacetimedb_table::table::{IndexScanRangeIter, TableScanIter};
use std::{
collections::BTreeMap,
ops::{Deref, RangeBounds},
sync::Arc,
};
use hashbrown::HashMap;
use itertools::Either;
use smallvec::SmallVec;
use spacetimedb_execution::{Datastore, DeltaStore, Row};
use spacetimedb_lib::{query::Delta, AlgebraicValue, ProductValue};
use spacetimedb_primitives::{IndexId, TableId};
use spacetimedb_table::table::{IndexScanRangeIter, TableScanIter};
use spacetimedb_datastore::{
locking_tx_datastore::{state_view::StateView, TxId},
traits::TxData,
};
use super::module_subscription_manager::QueriedTableIndexIds;
/// If an index is defined on a set of columns,
/// and if that index is used in a subscription query,
/// we build the very same index for each delta table.
+22 -11
View File
@@ -1,23 +1,34 @@
use core::hash::{BuildHasher, BuildHasherDefault};
pub use hashbrown::hash_map::Entry;
use nohash_hasher::BuildNoHashHasher;
pub use hashbrown::Equivalent;
pub use nohash_hasher::IsEnabled as ValidAsIdentityHash;
pub mod hash_set {
pub use super::HashSet;
pub use hashbrown::hash_set::*;
}
pub mod hash_map {
pub use super::HashMap;
pub use hashbrown::hash_map::*;
}
pub type DefaultHashBuilder = BuildHasherDefault<ahash::AHasher>;
// TODO(centril): expose two maps instead,
// one `map::fast::HashMap` and one `map::ddos::HashMap`.
// In the first case we won't care about DDoS protection at all and can use `foldhash::fast`.
// In the lattr, we can use e.g., randomized AHash.
pub type HashMap<K, V, S = DefaultHashBuilder> = hashbrown::HashMap<K, V, S>;
pub type HashSet<T, S = DefaultHashBuilder> = hashbrown::HashSet<T, S>;
pub type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultHashBuilder>;
pub type HashSet<T> = hashbrown::HashSet<T, DefaultHashBuilder>;
/// A version of [`HashMap<K, V>`] using the identity hash function,
/// which is valid for any key type that can be converted to a `u64` without truncation.
pub type IntMap<K, V> = HashMap<K, V, BuildNoHashHasher<K>>;
pub type IntMap<K, V> = hashbrown::HashMap<K, V, BuildNoHashHasher<K>>;
/// A version of [`HashSet<K>`] using the identity hash function,
/// which is valid for any key type that can be converted to a `u64` without truncation.
pub type IntSet<K> = HashSet<K, BuildNoHashHasher<K>>;
pub type IntSet<K> = hashbrown::HashSet<K, BuildNoHashHasher<K>>;
pub trait HashCollectionExt {
/// Returns a new collection with default capacity, using `S::default()` to build the hasher.
@@ -27,22 +38,22 @@ pub trait HashCollectionExt {
fn with_capacity(capacity: usize) -> Self;
}
impl<K, V, S: BuildHasher + Default> HashCollectionExt for HashMap<K, V, S> {
impl<K, V, S: BuildHasher + Default> HashCollectionExt for hashbrown::HashMap<K, V, S> {
fn new() -> Self {
HashMap::with_hasher(S::default())
Self::with_hasher(S::default())
}
fn with_capacity(capacity: usize) -> Self {
HashMap::with_capacity_and_hasher(capacity, S::default())
Self::with_capacity_and_hasher(capacity, S::default())
}
}
impl<K, S: BuildHasher + Default> HashCollectionExt for HashSet<K, S> {
impl<K, S: BuildHasher + Default> HashCollectionExt for hashbrown::HashSet<K, S> {
fn new() -> Self {
HashSet::with_hasher(S::default())
Self::with_hasher(S::default())
}
fn with_capacity(capacity: usize) -> Self {
HashSet::with_capacity_and_hasher(capacity, S::default())
Self::with_capacity_and_hasher(capacity, S::default())
}
}
@@ -738,8 +738,7 @@ impl TxMetrics {
// For each table, collect the extra stats, that we don't have in `tx_data`.
let table_stats = tx_data
.map(|tx_data| {
let mut table_stats =
<HashMap<_, _, _> as HashCollectionExt>::with_capacity(tx_data.num_tables_affected());
let mut table_stats = HashMap::with_capacity(tx_data.num_tables_affected());
for (table_id, _) in tx_data.table_ids_and_names() {
let stats = committed_state.get_table(table_id).map(|table| TableStats {
row_count: table.row_count,
+2 -2
View File
@@ -6,11 +6,11 @@ license-file = "LICENSE"
description = "Provides the trait `MemoryUsage`"
[features]
hashbrown = ["dep:hashbrown"]
hash_map = ["dep:hashbrown"]
smallvec = ["dep:smallvec"]
ethnum = ["dep:ethnum"]
decorum = ["dep:decorum"]
default = ["hashbrown", "smallvec", "ethnum", "decorum"]
default = ["hash_map", "smallvec", "ethnum", "decorum"]
[dependencies]
hashbrown = { workspace = true, optional = true }
+2 -2
View File
@@ -103,7 +103,7 @@ impl<T: MemoryUsage> MemoryUsage for Vec<T> {
}
}
#[cfg(feature = "hashbrown")]
#[cfg(feature = "hash_map")]
impl<K: MemoryUsage + Eq + core::hash::Hash, V: MemoryUsage, S: core::hash::BuildHasher> MemoryUsage
for hashbrown::HashMap<K, V, S>
{
@@ -112,7 +112,7 @@ impl<K: MemoryUsage + Eq + core::hash::Hash, V: MemoryUsage, S: core::hash::Buil
}
}
#[cfg(feature = "hashbrown")]
#[cfg(feature = "hash_map")]
impl<K: MemoryUsage + Eq + core::hash::Hash, S: core::hash::BuildHasher> MemoryUsage for hashbrown::HashSet<K, S> {
fn heap_usage(&self) -> usize {
self.allocation_size() + self.iter().map(|k| k.heap_usage()).sum::<usize>()
-1
View File
@@ -27,7 +27,6 @@ unicode-normalization.workspace = true
petgraph.workspace = true
serde_json.workspace = true
smallvec.workspace = true
hashbrown.workspace = true
enum-as-inner.workspace = true
enum-map.workspace = true
insta.workspace = true
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::{def::*, error::PrettyAlgebraicType, identifier::Identifier};
use formatter::format_plan;
use spacetimedb_data_structures::{
error_stream::{CollectAllErrors, CombineErrors, ErrorStream},
map::HashSet,
map::{HashCollectionExt as _, HashSet},
};
use spacetimedb_lib::{
db::raw_def::v9::{RawRowLevelSecurityDefV9, TableType},
+3 -3
View File
@@ -25,10 +25,10 @@ use crate::schema::{Schema, TableSchema};
use crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate};
use deserialize::ArgsSeed;
use enum_map::EnumMap;
use hashbrown::{Equivalent, HashMap};
use indexmap::IndexMap;
use itertools::Itertools;
use spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors, ErrorStream};
use spacetimedb_data_structures::map::{Equivalent, HashMap};
use spacetimedb_lib::db::raw_def;
use spacetimedb_lib::db::raw_def::v9::{
Lifecycle, RawColumnDefaultValueV9, RawConstraintDataV9, RawConstraintDefV9, RawIdentifier, RawIndexAlgorithm,
@@ -1442,7 +1442,7 @@ impl ModuleDefLookup for ViewDef {
}
}
fn to_raw<Def, RawDef, Name, A>(data: HashMap<Name, Def, A>) -> Vec<RawDef>
fn to_raw<Def, RawDef, Name>(data: HashMap<Name, Def>) -> Vec<RawDef>
where
Def: ModuleDefLookup + Into<RawDef>,
Name: Eq + Ord + 'static,
@@ -1457,7 +1457,7 @@ mod tests {
use super::*;
use proptest::prelude::*;
use spacetimedb_data_structures::expect_error_matching;
use spacetimedb_data_structures::{expect_error_matching, map::HashCollectionExt as _};
use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9Builder;
proptest! {
+1 -2
View File
@@ -1,6 +1,5 @@
use crate::error::IdentifierError;
use hashbrown::Equivalent;
use spacetimedb_data_structures::map::HashSet;
use spacetimedb_data_structures::map::{Equivalent, HashSet};
use spacetimedb_sats::{de, ser};
use std::fmt::{self, Debug, Display};
use std::ops::Deref;
+2 -2
View File
@@ -12,7 +12,7 @@ use petgraph::{
use smallvec::SmallVec;
use spacetimedb_data_structures::{
error_stream::{CollectAllErrors, CombineErrors, ErrorStream},
map::{HashMap, HashSet},
map::{hash_set, HashMap, HashSet},
};
use spacetimedb_lib::{AlgebraicType, ProductTypeElement};
use spacetimedb_sats::{
@@ -632,7 +632,7 @@ impl NodeIndexable for TypespaceForGenerateBuilder<'_> {
}
}
impl<'a> IntoNodeIdentifiers for &'a TypespaceForGenerateBuilder<'a> {
type NodeIdentifiers = std::iter::Cloned<hashbrown::hash_set::Iter<'a, spacetimedb_sats::AlgebraicTypeRef>>;
type NodeIdentifiers = std::iter::Cloned<hash_set::Iter<'a, spacetimedb_sats::AlgebraicTypeRef>>;
fn node_identifiers(self) -> Self::NodeIdentifiers {
self.is_def.iter().cloned()
+1 -1
View File
@@ -31,7 +31,7 @@ blake3_pure = ["blake3/pure"]
[dependencies]
spacetimedb-data-structures = { workspace = true, features = ["memory-usage"] }
spacetimedb-memory-usage = { workspace = true, features = ["hashbrown", "ethnum", "smallvec"] }
spacetimedb-memory-usage = { workspace = true, features = ["hash_map", "ethnum", "smallvec"] }
spacetimedb-primitives.workspace = true
spacetimedb-sats = { workspace = true, features = ["blake3"] }
spacetimedb-lib = { workspace = true, features = ["memory-usage"] }
+1 -1
View File
@@ -12,7 +12,7 @@
//! It is not optimize and is mainly intended for testing purposes.
use blake3::hash;
use spacetimedb_data_structures::map::{Entry, HashMap};
use spacetimedb_data_structures::map::{hash_map::Entry, HashMap};
use spacetimedb_lib::{de::Deserialize, ser::Serialize};
use spacetimedb_memory_usage::MemoryUsage;
+1 -1
View File
@@ -17,7 +17,7 @@ use super::indexes::{PageIndex, PageOffset, RowHash, RowPointer, SquashedOffset}
use crate::static_assert_size;
use core::{hint, slice};
use spacetimedb_data_structures::map::{
Entry,
hash_map::Entry,
IntMap, // No need to hash a hash.
};
use spacetimedb_sats::memory_usage::MemoryUsage;
+2 -3
View File
@@ -10,7 +10,7 @@ use bytes::Bytes;
use core::any::type_name;
use core::hash::Hash;
use futures_channel::mpsc;
use spacetimedb_data_structures::map::{DefaultHashBuilder, Entry, HashCollectionExt, HashMap};
use spacetimedb_data_structures::map::{hash_map::Entry, HashCollectionExt, HashMap};
use std::marker::PhantomData;
use std::sync::Arc;
@@ -120,8 +120,7 @@ impl<'r, Row> TableAppliedDiff<'r, Row> {
}
// Compute the PK -> Row map for deletes.
let mut delete_pks =
<HashMap<_, _, DefaultHashBuilder> as HashCollectionExt>::with_capacity(self.deletes.len());
let mut delete_pks = HashMap::with_capacity(self.deletes.len());
for (&bsatn, &row) in self.deletes.iter() {
let pk = derive_pk(row);
delete_pks.insert(pk, (bsatn, row));