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

Cleanup of entries should not be skipped when using default settings #536

Merged

Conversation

madmajestro
Copy link
Contributor

By default (apc.smart = 0), apc_cache_default_expunge() should not skip cleanup of old entries. This may cause insertion of new entries to fail unnecessarily, which is unexpected behavior.

By default (apc.smart = 0), apc_cache_default_expunge() should not skip
cleanup of old entries. This may cause insertion of new entries to
fail unnecessarily, which is unexpected behavior.
Copy link
Contributor

@LionsAd LionsAd left a comment

Choose a reason for hiding this comment

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

FWIW, here is a peer-review: Looks great to me!

And is fully opt-out via apc.smart=1.

Easier to review with:

https://github.com/krakjoe/apcu/pull/536/files?w=1

@madmajestro
Copy link
Contributor Author

@LionsAd
Thanks for your review and your approval!

Just to be sure, since you mentioned apc.smart = 1 as a fully opt-out...
Using apc.smart = 1 you will get the same behavior as before with apc.smart = 1. But that's not what apc.smart = 0 (default) did before. Therefore, strictly speaking, it not really a fully opt-out.

The main purpose of this PR is to remove the skip that can occur, when apc.smart = 0 AND apc_sma_get_avail_mem(cache->sma) >= (cache->sma->size / 2).

I think this skip was intended as some kind of optimization, but when the expunge() hook is called, in the vast majority of cases this skip does not occur. So it optimizes almost nothing. But when it occurs, it causes unexpected behavior (PHP functions like apcu_store() are not able to store the new entry). That's why I think it makes sense to remove this skip.

@LionsAd
Copy link
Contributor

LionsAd commented Mar 11, 2025

Yes, that is what I meant that with the old smart = 1 behavior, you have the same behavior.

And I agree that it’s better to purge some items from time to time than to completely run out of memory.

@nikic
Copy link
Collaborator

nikic commented Mar 11, 2025

To check my understanding, this is only about the case where you're trying to store something that's larger than half the entire cache size?

apc_cache_wlocked_remove_entry(cache, entry);
continue;
}
if (cache->smart > 0L && apc_sma_get_avail_mem(cache->sma) >= (size_t) (cache->smart * size)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this even do anything useful? We're only going to call expunge if there is not enough space for size, right?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah no, this is checking for total free size, not available single block. But then doesn't setting smart > 0 still result in expunge being skipped sometimes even though it may have been possible to free up consecutive space?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it does and I'd like to change how smart works to address this issue as well. I made a suggestion how smart could be changed in #533. Then I opened this pr (#536) because i wasn't sure if it is a good idea to change the behavior of smart. But finally i think we should do it, because the current implementation seems to be not very useful and will cause unexpected behavior.

If you think it makes sense to change the behavior of smart, I could make the entire change (including smart) here or open a new PR for it. Which do you prefer?

@nikic
Copy link
Collaborator

nikic commented Mar 11, 2025

To check my understanding, this is only about the case where you're trying to store something that's larger than half the entire cache size?

Okay, I think I misunderstood, and what this is trying to fix is a high-fragmentation case where half the cache is empty, but not enough space for a consecutive allocation?

@madmajestro
Copy link
Contributor Author

Exactly!

In #533, I solved both problems (no skipping in high fragmentation situations + usefulness of smart setting), but it's a bit verbose...

Therefore, if this PR (#536) is merged, I'd like to provide a solution for the smart setting in another PR. I will close #533 then.

@LionsAd
Copy link
Contributor

LionsAd commented Mar 12, 2025

While I am not the maintainer, my 2c is that this change is independently useful and the smart == 1 behavior could be tweaked in a follow-up.

@madmajestro
Copy link
Contributor Author

Friendly ping.

@nikic nikic merged commit 0649eb1 into krakjoe:master Mar 15, 2025
31 checks passed
@nikic
Copy link
Collaborator

nikic commented Mar 15, 2025

I went ahead and merged this for now. I'm not particularly happy with the smart behavior here, but TBH I don't know what the correct behavior is even supposed to be there.

I'm also somewhat concerned about fully expunging the cache in high fragmentation scenarios where there is still a lot of free space. I think that ideally, we'd make the persistence representation position-independent (via pointer unswizzling), which would then allow relocating the cache entries to reduce fragmentation. This would be a large change though.

Another option to consider would be to change the allocator to reduce fragmentation (via size binning?) The current implementation is naive.

@madmajestro madmajestro deleted the aka-expunge-should-not-skip-by-default branch March 16, 2025 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants