-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Expiring cache #2331
Expiring cache #2331
Conversation
@jaydoane nice works, small and easy to understand! Made a few comments in-line. Was also wondering if you thought about building it more as a library, such that it would plug into to another application's supervisor with something like:
Then we wouldn't need a way to disable or enable the whole application and it would be started only as needed. What do you think? If you'd rather keep it as is, that'll work just. Just thought to explore another option. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work. Some minor feedback.
|
||
-spec lookup(Name :: binary(), Key :: binary()) -> | ||
not_found | {fresh, Val :: binary()} | {stale, Val :: binary()} | expired. | ||
lookup(Name, Key) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea that @nickva mentions above about adding an optional transaction. I think it should also apply to the lookup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b939216 enables optional transactions for both insert and lookup operations.
]). | ||
|
||
|
||
-define(XC, 53). % coordinate with fabric2.hrl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit nervous that we have it here instead of fabric2.hrl, it could be easy for someone else to later use the namespace for something else. Also XC
isn't a particluarly clean name. I have no idea what it means. Could you expand it a little.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it's not a great solution. I just copied this from couch_jobs, but maybe we should just make it a practice to directly modify fabric2.hrl
as necessary?
Also, expanded XC
to EXPIRING_CACHE
in 7144ceb
|
||
-include_lib("couch/include/couch_db.hrl"). | ||
-include_lib("couch/include/couch_eunit.hrl"). | ||
-include_lib("eunit/include/eunit.hrl"). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance you could make this one of the new shiny exunit tests instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would love nothing more than to use elixir here, but the expiring cache has no HTTP interface, so unfortunately it would be extremely inconvenient :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its possible to do that now https://github.com/apache/couchdb/tree/prototype/fdb-layer/test/elixir#how-to-write-exunit-tests
You don't need an http interface to use the exunit tests.
@nickva this is an interesting idea, and I hadn't considered adding the cache to an existing supervisor tree, as your snippet seems to imply. I'm guessing that
The main drawback I could think of with such an approach is that unexpired entries of abandoned/changed |
@jaydoane I was thinking it would be like having N expiring caches, one for each application that needs it. Each would have its own timeouts, etc. Presumably the expiring cache for OAuth2 vs some other auth mechanism could need different expiry and stale timeouts. The model would then look like:
So there would be one for OAuth2 with something like |
@nickva I think the main issue with this schema is that if application EDIT: It occurs to me that it's possible to clear all keys under the |
timer_ref => schedule_remove_expired(Period, MaxJitter), | ||
oldest_ts => 0, | ||
elapsed => 0, | ||
largest_elapsed => 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does largest_elapsed
track? Is it a stat for debugging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, since we don't currently have a way to collect metrics, it's just a simple way to see how long it's taking (in the worst case) to delete expired entries. If it gets close to 5 seconds, we might need to decrease the period, and/or lower the batch size.
-spec insert(Tx :: jtx(), Name :: binary(), Key :: binary(), Value :: binary(), | ||
StaleTS :: millisecond(), ExpiresTS :: millisecond()) -> ok. | ||
insert(Tx, Name, Key, Value, StaleTS, ExpiresTS) -> | ||
couch_jobs_fdb:tx(couch_jobs_fdb:get_jtx(Tx), fun(JTx) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, maybe add some guards on the types of arguments.
is_binary/1
for Name
, Key
and Value
and is_integer/1
for StaleTs
and ExpiresTS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
rel/overlay/etc/default.ini
Outdated
|
||
[expiring_cache] | ||
; | ||
; How often to remove expired entries | ||
; period_msec = 5000 | ||
; | ||
; Jitter applied when removing expired entries | ||
; max_jitter_msec = 1000 | ||
; | ||
; Maximum number of entries removed in a single transaction | ||
; batch_size = 1000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it's a library, let's skip updating the default.ini changes. Each application can then have its own section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh, I forgot to remove this when I converted to a library. Done.
@jaydoane Looks great. See a few minor notes. To run tests I had to add it to the subdirs in rebar:
Otherwise tests ran and passed. Created an expiring cache manually and saw that entries were being returned as fresh, stale, expired then cleaned up and returned as non_found.
Nice work! +1 (after the minor fixes from the comments) |
This is a library for creating an FDB backed key value cache, where each entry has a `stale` and `expires` time associated with it. Once the current time exceeds the `expires` time, the entry is automatically removed. The `stale` time can be used to indicate that a refresh is necessary, while still returning a non-expired value. It is potentially useful for implementing e.g. caches to external systems of record, such as OAuth 2.
515e8a3
to
2f7957c
Compare
Overview
This implements an FDB backed key value cache, where each entry has a "stale" and "expires" time associated with it. Once the current time exceeds the "expires" time, the entry is automatically removed.
Potentially useful for implementing e.g. interfaces to external systems such as OAuth 2.
Testing recommendations
Related Issues or Pull Requests
Checklist
rel/overlay/etc/default.ini