Hello, newbie in rust and using moka future cache, my first post here, sorry in advance if I do something wrong.
If I declare:
delay: Cache<i64, SystemTime>
And create a new instance:
delay: Cache::builder().max_capacity(10).time_to_live(Duration::from_secs(60)).build()
And insert into it:
let now = SystemTime::now();
let closure = || now;
let creation_time = entry.delay.get_with(key, async { closure() }).await;
This works perfectly.
But if I change the declaration to:
delay: Cache<i64, SystemTime, BuildHasherDefault<NoHashHasher<i64>>>
And create a new instance:
delay: Cache::builder().max_capacity(10).time_to_live(Duration::from_secs(60)).build_with_hasher(BuildNoHashHasher::<i64>::default())
And try to insert a value exactly in the same way, I get the following error:
thread 'main' panicked at C:\Users\joanb.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\nohash-hasher-0.2.0\src\lib.rs:244:9:
NoHashHasher: second write attempt detected.
I would like to understand what's wrong here.
Thanks!,
Joan.
bjorn3
April 30, 2025, 10:18am
2
NoHashHasher
only allows a Hasher::write
call, but SystemTime
uses multiple Hasher::write
calls like pretty much every type other than primitive values. According to the documentation of NoHashHasher
:
NoHashHasher
does not implement any hashing algorithm and can only be used with types which can be mapped directly to a numeric value. Out of the box NoHashHasher
is enabled for u8
, u16
, u32
, u64
, usize
, i8
, i16
, i32
, i64
, and isize
. Types that should be used with NoHashHasher
need to implement IsEnabled
and by doing so assert that their Hash
impl invokes only one of the Hasher::write_{u8, u16, u32, u64, usize, i8, i16, i32, i64, isize}
methods exactly once .
In other words, you can't use NoHashHasher
with SystemTime
.
2 Likes
Thanks a lot bjorn, I will use SystemTime as_millis to insert a u64.
Hi bjorn,
Not able to put this working. I have elaborated a simpler test:
struct Test {
cache: Cache<i8, u8, BuildHasherDefault<NoHashHasher<i8>>>
}
async fn test() {
let test = Test { cache: Cache::builder().build_with_hasher(BuildNoHashHasher::<i8>::default()) };
let closure = || 10;
test.cache.get_with(-1, async { closure() }).await;
}
When I run this sample:
NoHashHasher: second write attempt detected.
What am i doing wrong?
Thanks,
Joan.
bjorn3
April 30, 2025, 12:20pm
5
It seems like moka
uses Arc<T>
as hashmap key, which is not supported by NoHashHasher
.
Ok thanks, I will ask the moka guy, insert and get methods work fine:
test.cache.insert(-1, 10).await;
println!("result = {}", test.cache.get(&-1).await.unwrap());
Prints:
result = 10
kpreid
April 30, 2025, 4:16pm
7
Even if a type works with NoHashHasher
, that doesn't mean it will work well .
Hashing is not just about squeezing large values into small ones. A good hash value should have effectively random bits — any difference in the input should change (on average) half of the bits of the output. A timestamp value does not make a good hash, because often only the low bits are different. If you do this, it is likely that your hash table will end up with a large number of collisions and be very inefficient.
You should only use NoHashHasher
when your keys are already hashes, to avoid redundant hashing.
2 Likes
Hi kpreid,
That was exactly the idea, our rust app already receives hashes as java longs (i64). Here I'm missing something, obviously I don't understand how this works.
delay: Cache<i64, SystemTime, BuildHasherDefault<NoHashHasher<i64>>>
Here the cache key is of type i64, this i64 is the hash our app is receiving from java. When I perform:
let now = SystemTime::now();
let closure = || now;
let creation_time = delay.get_with(key, async { closure() }).await;
What I though is the "nohashhasher" did nothing with the key, so it used the same i64 (the key I'm passing to the "get_with" method) as a hash. So, in case "get_with" does not found the "key", it performs a "delay.insert(key, now)".
So I don't understand why the value (SystemTime) matters, when only the keys are hashed.
Thanks,
Joan.
kpreid
April 30, 2025, 9:01pm
9
Assuming this is the right Cache
documentation , it can't be hashing the values because there is no V: Hash
bound. Please run your code with RUST_BACKTRACE=1
:
RUST_BACKTRACE=1 cargo test
This will get a full backtrace along with the panic message, which will help us identify where the failure arises from.
1 Like
Hi kpreid,
Here it is:
thread 'main' panicked at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nohash-hasher-0.2.0/src/lib.rs:244:9:
NoHashHasher: second write attempt detected.
stack backtrace:
0: 0x564861013130 - std::backtrace_rs::backtrace::libunwind::trace::h2b8934d68a0c74cb
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9
1: 0x564861013130 - std::backtrace_rs::backtrace::trace_unsynchronized::h72d137991fa6eab8
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14
2: 0x564861013130 - std::sys::backtrace::_print_fmt::h3e831e32e6da920b
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:66:9
3: 0x564861013130 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h6d42cc84fc840290
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:39:26
4: 0x56486103c8b3 - core::fmt::rt::Argument::fmt::h7239af9e2199f2a8
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/fmt/rt.rs:177:76
5: 0x56486103c8b3 - core::fmt::write::h5af61a909e3ec64d
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/fmt/mod.rs:1449:21
6: 0x56486100fb83 - std::io::Write::write_fmt::h5a7b54aa6e4a315d
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/io/mod.rs:1890:15
7: 0x564861012f82 - std::sys::backtrace::BacktraceLock::print::h555579e7396c26ac
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:42:9
8: 0x56486101401f - std::panicking::default_hook::{{closure}}::h9128866118196224
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:298:22
9: 0x564861013e8a - std::panicking::default_hook::h52e9e7314e0255f6
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:325:9
10: 0x5648610149c2 - std::panicking::rust_panic_with_hook::h541791bcc774ef34
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:831:13
11: 0x56486101489c - std::panicking::begin_panic::{{closure}}::h4d8ebad7787f6bf4
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:768:9
12: 0x564861013639 - std::sys::backtrace::__rust_end_short_backtrace::h52f9c099bc75a494
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:168:18
13: 0x564860bddd00 - std::panicking::begin_panic::h815831b19d7a8c5e
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:767:5
14: 0x564860d10f20 - <nohash_hasher::NoHashHasher<T> as core::hash::Hasher>::write_u64::h16922aaf4dc7114f
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nohash-hasher-0.2.0/src/lib.rs:244:9
15: 0x564860d73310 - core::hash::impls::<impl core::hash::Hash for u64>::hash::h08a2ef60fd9ed5d4
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/hash/mod.rs:812:21
16: 0x564860d73310 - <core::any::TypeId as core::hash::Hash>::hash::hf0c62bab57987059
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/any.rs:771:18
17: 0x564860da759d - core::hash::impls::<impl core::hash::Hash for (T,B)>::hash::h5ffa370d5ebe774c
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/hash/mod.rs:894:27
18: 0x564860c89203 - moka::cht::map::bucket::hash::hde0bca7fa111abc6
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/cht/map/bucket.rs:664:5
19: 0x564860bfa647 - moka::cht::segment::HashMap<K,V,S>::hash::hcf34822119ff408d
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/cht/segment.rs:506:9
20: 0x564860d68f94 - moka::future::value_initializer::waiter_key_hash::h15b1716423e6335b
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/future/value_initializer.rs:648:18
21: 0x564860d69acb - moka::future::value_initializer::ValueInitializer<K,V,S>::try_init_or_read::{{closure}}::h1ea579f2c6c1d784
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/future/value_initializer.rs:173:31
22: 0x564860d5efaa - moka::future::cache::Cache<K,V,S>::insert_with_hash_and_fun::{{closure}}::hd09cce13b56fbc15
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/future/cache.rs:1600:14
23: 0x564860d5f8b3 - moka::future::cache::Cache<K,V,S>::get_or_insert_with_hash_and_fun::{{closure}}::h3ff74e267f7d263b
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/future/cache.rs:1551:18
24: 0x564860d610ee - moka::future::cache::Cache<K,V,S>::get_with::{{closure}}::hd2f96cbe470337ef
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/moka-0.12.10/src/future/cache.rs:1059:14
25: 0x564860d93360 - ventusproxycache::main::{{closure}}::h66e56e6c85ba0477
at /opt/ventusproxycache/src/main.rs:38:50
26: 0x564860c04c2f - tokio::runtime::park::CachedParkThread::block_on::{{closure}}::hd869f3bec380022e
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/park.rs:284:60
27: 0x564860c03dc6 - tokio::task::coop::with_budget::h24aaa5aefff0d9c0
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/task/coop/mod.rs:167:5
28: 0x564860c03dc6 - tokio::task::coop::budget::h75747ce3f5072b82
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/task/coop/mod.rs:133:5
29: 0x564860c03dc6 - tokio::runtime::park::CachedParkThread::block_on::h45d608d898283ddd
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/park.rs:284:31
30: 0x564860d5a793 - tokio::runtime::context::blocking::BlockingRegionGuard::block_on::h00959c53e4f4a55c
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/context/blocking.rs:66:9
31: 0x564860c08888 - tokio::runtime::scheduler::multi_thread::MultiThread::block_on::{{closure}}::h0020be83b29067ed
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/scheduler/multi_thread/mod.rs:87:13
32: 0x564860cfb2cb - tokio::runtime::context::runtime::enter_runtime::h353d824a475f6b94
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/context/runtime.rs:65:16
33: 0x564860c08803 - tokio::runtime::scheduler::multi_thread::MultiThread::block_on::h6626552185f14d3e
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/scheduler/multi_thread/mod.rs:86:9
34: 0x564860d12009 - tokio::runtime::runtime::Runtime::block_on_inner::hb546136f577b8de5
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/runtime.rs:370:45
35: 0x564860d1233f - tokio::runtime::runtime::Runtime::block_on::h554d9dd3bb0f9931
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.44.2/src/runtime/runtime.rs:342:13
36: 0x564860d0974a - ventusproxycache::main::hcfd48eb1d32d729e
at /opt/ventusproxycache/src/main.rs:46:5
37: 0x564860cc740b - core::ops::function::FnOnce::call_once::h762c2cfef58185a2
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ops/function.rs:250:5
38: 0x564860cfe12e - std::sys::backtrace::__rust_begin_short_backtrace::h19e48550eda66b1f
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:152:18
39: 0x564860c1e961 - std::rt::lang_start::{{closure}}::hfd11b799d6af7c9c
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/rt.rs:199:18
40: 0x5648610097a0 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h11180d46f4bd77b0
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ops/function.rs:284:13
41: 0x5648610097a0 - std::panicking::try::do_call::hd4e634b5516dff98
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:587:40
42: 0x5648610097a0 - std::panicking::try::hb26f372c4276d7b4
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:550:19
43: 0x5648610097a0 - std::panic::catch_unwind::hcff84ccd1cc2f0a5
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panic.rs:358:14
44: 0x5648610097a0 - std::rt::lang_start_internal::{{closure}}::h7f5cb21b2420f132
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/rt.rs:168:24
45: 0x5648610097a0 - std::panicking::try::do_call::he996326bc7b05062
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:587:40
46: 0x5648610097a0 - std::panicking::try::h17585bee78ecbda5
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:550:19
47: 0x5648610097a0 - std::panic::catch_unwind::h366ac34033c6e5d6
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panic.rs:358:14
48: 0x5648610097a0 - std::rt::lang_start_internal::h15895544e2012228
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/rt.rs:164:5
49: 0x564860c1e947 - std::rt::lang_start::hd56d6607c3cedf2b
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/rt.rs:198:5
50: 0x564860d097ee - main
51: 0x7f1732e025d0 - __libc_start_call_main
52: 0x7f1732e02680 - __libc_start_main_alias_1
53: 0x564860bdf915 - _start
54: 0x0 - <unknown>
It seems that moka
is internally using an (Arc<K>, TypeId)
tuple for some purpose, here: https://docs.rs/moka/0.12.10/src/moka/future/value_initializer.rs.html#637 . So, you cannot use NoHashHasher
because there will always be at least 2 values hashed.
Perhaps there is some mode of operation or usage restriction under which this isn't done and you can use NoHashHasher
, or perhaps it would be feasible to add this capability to the library. But those are questions best directed to the developers of moka
.
Thanks kpreid, I will ask the moka's author.