Runtime error when using NoHashHasher in moka cache

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.

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.

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

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.

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.