Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] The lazyness of expiration #125

Closed
ClSlaid opened this issue May 17, 2022 · 9 comments
Closed

[question] The lazyness of expiration #125

ClSlaid opened this issue May 17, 2022 · 9 comments
Labels
question Further information is requested
Milestone

Comments

@ClSlaid
Copy link
Contributor

ClSlaid commented May 17, 2022

Hello maintainer, I'd like to know whether the expiration of cache is lazy or active.
By lazy I mean not until getting on the expired entry or other scenarios that have to remove it, the entry will be preserved in the cache;
Corresponsively by active I mean if the entry outlived its TTL or IDLE, the entry will be instantly evicted.
I've also used this crate to build a server-side connection pool, and I hope any outlived connections should be dropped immediately. Nothing about this was in the README, but by invalidate_entries_if() method I guess this cache should be lazy?

@tatsuya6502
Copy link
Member

tatsuya6502 commented May 18, 2022

Hi. Thank you for using Moka.

  1. The expiration is lazy by design, so expired entries will be preserved in the cache for a short period of time.

    • As of v0.8.3, they will be usually removed from the cache within 300 milliseconds after expiration, but this delay can get longer if the cache is receiving very heavy reads and/or writes from clients.
    • The 300 ms delay (actually a clean-up interval) is currently hard-coded, but we can make it configurable in the future.
    • If you are curious about internals, there is a (little, work-in-progress) doc about it: https://docs.rs/moka/latest/moka/#implementation-details
  2. There is also a known issue in an underlying library (crossbeam-epoch) that will prevent entries from dropping for much longer period after the removal from the cache.

    • Our cache internally uses a lock-free hash table to store the entries and it uses crossbeam-epoch for epoch-based garbage collection (GC): https://docs.rs/crossbeam-epoch/latest/crossbeam_epoch/
    • crossbeam-epoch's garbage collector is not optimal for some use cases and may keep ~100 garbages (objects waiting to be dropped) in the heap. We are hitting this issue and some entries will not be dropped immediately.

As for 1, please let me know whether you are okay with the current delay or not. We could make the delay configurable per cache.

As for 2, let me explore some ideas to resolve/mitigate the GC issue.

@ClSlaid
Copy link
Contributor Author

ClSlaid commented May 18, 2022

Hi. Thank you for using Moka.

1. The expiration is `lazy` by design, so expired entries will be preserved in the cache for a short period of time.
   
   * As of v0.8.3, they will be usually removed from the cache within 300 milliseconds after expiration, but this delay can get longer if the cache is receiving very heavy reads and/or writes from clients.
   * The 300 ms delay (actually a clean-up interval) is currently hard-coded, but we can make it configurable in the future.
   * If you are curious about internals, there is a (little, work-in-progress) doc about it: https://docs.rs/moka/latest/moka/#implementation-details

2. There is also a known issue in an underlying library (crossbeam-epoch) that will prevent entries from dropping for much longer period after the removal from the cache.
   
   * Our cache internally uses a lock-free hash table to store the entries and it uses crossbeam-epoch for epoch-based garbage collection (GC): https://docs.rs/crossbeam-epoch/latest/crossbeam_epoch/
   * crossbeam-epoch's garbage collector is not optimal for some use cases and may keep ~100 garbages (objects waiting to be dropped) in the heap. We are hitting this issue and some entries will not be dropped immediately.

As for 1, please let me know whether you are okay with the current delay or not. We could make the delay configurable per cache.

As for 2, let me explore some ideas to resolve/mitigate the GC issue.

Thanks for your reply, I think preserving an evicted connection for less than 3s is ok (after all the connection pool is just a toy project), and it seems hardly occur.

And I would like to take a deeper look into Moka's internal functionalities, as when I graduate from university. :)

@tatsuya6502
Copy link
Member

as when I graduate from university. :)

Congratulations! 🎉

I would like to take a deeper look into Moka's internal functionalities,

I wish I had more time to write design docs, so all you can read now are:

Moka's design is heavily inspired by the Caffeine library for Java. So you can learn the base design from Caffeine's publications: https://github.com/ben-manes/caffeine#in-the-news

I started to develop Moka because I needed a concurrent cache for the storage layer of a NoSQL database (as my hobby project) and found there was no such cache library in Rust that would meet my needs. I thought I will finish implementing Moka in a couple of months so I can go back to my original project quickly. I never thought it would take some years (but I am enjoying).

@tatsuya6502 tatsuya6502 added the question Further information is requested label Jul 9, 2022
@SimonSchneider
Copy link

crossbeam-epoch's garbage collector is not optimal for some use cases and may keep ~100 garbages (objects waiting to be dropped) in the heap. We are hitting this issue and some entries will not be dropped immediately.

This one could be something we are actually noticing. Our cache entries are quite large and not that many so having about ~100 of them hanging around might look like a memory leak to us. Is there any way to mitigate this?

@tatsuya6502
Copy link
Member

tatsuya6502 commented Jul 20, 2022

@SimonSchneider

Sorry for not getting back to you sooner.

I wonder if we could mitigate it by using the flush method of crossbeam_epoch::Guard.

https://docs.rs/crossbeam-epoch/0.9.9/crossbeam_epoch/struct.Guard.html#method.flush

Clears up the thread-local cache of deferred functions by executing them or moving into the global cache.

Call this method after deferring execution of a function if you want to get it executed as soon as possible. Flushing will make sure it is residing in in the global cache, so that any thread has a chance of taking the function and executing it.

We use some "deferred functions" to drop entries removed from cache.

Let me try it in next few days. I will measure performance impacts for both memory usage and speed.

@SimonSchneider
Copy link

No worries, thanks for getting back to me.

Thank you for looking into it! Sounds like an approach that could work.

@tatsuya6502
Copy link
Member

As for the flush stuff, I am working on it here: #169

@tatsuya6502
Copy link
Member

I merged #169 into the master branch. I will release it as a part of v0.9.3.

Closing this issue.

@tatsuya6502 tatsuya6502 added this to the v0.9.3 milestone Jul 24, 2022
@tatsuya6502
Copy link
Member

Sorry, it took longer than I thought. But I have published Moka v0.9.3 to crates.io.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants