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

Support caching nil values #16

Merged
merged 6 commits into from
Feb 16, 2024
Merged

Conversation

GregMefford
Copy link
Contributor

Since Mentat.fetch specifically requires that the fallback function return {:commit, value} or {:ignore, value}, a nil should be able to be stored in cache and not be treated as a miss when fetch is called again later.

This bug went unnoticed for so long because it often behaves correctly if the intended value was nil, because the fallback function would probably also end up returning nil if called again later. However, this is very inefficient compared to actually caching and returning a nil value in this case and it also makes the metrics incorrect, firing a [:mentat, :miss] even though it actually had a nil value stored.

This change fixes those issues and allows a nil to be stored and retrieved from ETS just like any other value.

@@ -278,6 +270,17 @@ defmodule Mentat do
Supervisor.stop(name)
end

defp lookup(cache, key) do
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Initially, I copy/pasted the implementation from get/2 into fetch/4 and tweaked it like this, because it didn't have the information it needed just by calling get like it was doing before. Then, I extracted the result here because I thought it worked well for both and also simplified the implementation of get/2

@mhanberg
Copy link
Member

mhanberg commented Feb 7, 2024

The GHA workflows need updating so tests will actually run, the Elixir and OTP versions are far too old.

Other than that seems fine, will wait for @keathley to take a look.

lib/mentat.ex Outdated
val
end
{status, value} = lookup(cache, key)
:telemetry.execute([:mentat, :get], %{status: status}, %{key: key, cache: cache})

Choose a reason for hiding this comment

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

looks like the telemetry could stay in the case statement for lookup/2?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha you're totally right! I'll fix that while it's top of mind before we merge this.

@keathley
Copy link
Member

@mhanberg I'm good with this.

@keathley
Copy link
Member

Unrelated thought: Looking at the telemetry, I'm wondering why we never added span events to mentat. Seems like an easy enough change to make and might have surfaced this issue sooner. Either way maybe that would be a nice thing to do at some point in the future. Not super necessary. Just a nice to have.

@GregMefford
Copy link
Contributor Author

I'm wondering why we never added span events to mentat.

Yeah I had the same thought recently while cleaning up some internal metrics / dashboards that used to have cache read latency in them and was like "huh I wonder how we never noticed that we didn't have this?" My guess is just that it's usually fast when it hits, so we mainly only care about the hit to miss ratio, but you're right that it would be easy to add and maybe I will do it if someone else doesn't get to it first!

@mhanberg mhanberg merged commit 16dd1d9 into elixir-toniq:main Feb 16, 2024
3 checks passed
@GregMefford GregMefford deleted the cache_nil branch February 16, 2024 04:59
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.

4 participants