Skip to content

Fix compatibility between the cache compute methods and a load #5348

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

Closed
wants to merge 1 commit into from

Conversation

ben-manes
Copy link
Contributor

@ben-manes ben-manes commented Dec 5, 2020

The asMap().compute implementation did not take into account that the present value may be loading. A load does not block other writes to that entry and takes into account that it may be clobbered, causing it to automatically discard itself. This is a known design choice that breaks linearizability assumptions (#1881). The compute should check if a load is in progress and call the appropriate internal removal method.

Because a zombie entry remained in the cache and still is marked as loading, the loader could discover entry and try to wait for it to materialize. When the computation is a removal, indicated by a null value, the loader would see this as the zombie's result. Since a cache loader may not return null it would throw an exception to indicate a user bug.

A new ComputingValueReference resolves both issues by indicating that the load has completed. The compute's removeEntry will then actually remove this entry and the loader will not wait on the zombie. Instead if it observes the entry, it will neither receive a non-null value or wait for it to load, but rather try to load anew under the lock. This piggybacks on the reference collection support where an entry is present but its value was garbage collected, causing the load to proceed. By the time the lock is obtained the compute method's entry was removed and the load proceeds as normal (so no unnecessary notification is produced).

fixes #5342
fixes #2827
resolves underlying cause of #2108

The asMap().compute implementation did not take into account that the
present value may be loading. A load does not block other writes to
that entry and takes into account that it may be clobbered, causing
it to automatically discard itself. This is a known design choice that
breaks linearizability assumptions (google#1881). The compute should check
if a load is in progress and call the appropriate internal removal
method.

Because a zombie entry remained in the cache and still is marked as
loading, the loader could discover entry and try to wait for it to
materialize. When the computation is a removal, indicated by a null
value, the loader would see this as the zombie's result. Since a cache
loader may not return null it would throw an exception to indicate
a user bug.

A new ComputingValueReference resolves both issues by indicating
that the load has completed. The compute's removeEntry will then
actually remove this entry and the loader will not wait on the
zombie. Instead if it observes the entry, it will neither receive
a non-null value or wait for it to load, but rather try to load
anew under the lock. This piggybacks on the reference collection
support where an entry is present but its value was garbage
collected, causing the load to proceed. By the time the lock is
obtained the compute method's entry was removed and the load
proceeds as normal (so no unnecessary notification is produced).

fixes google#5342
fixes google#2827
resolves underlying cause of google#2108
@netdpb netdpb self-requested a review February 9, 2021 15:22
@netdpb netdpb self-assigned this Feb 9, 2021
copybara-service bot pushed a commit that referenced this pull request Feb 12, 2021
Fixes #5348
Fixes #5342
Fixes #2827
Resolves underlying cause of #2108

RELNOTES=Fix compatibility between the cache compute methods and a load.
PiperOrigin-RevId: 356510914
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants