Skip to content

Commit

Permalink
Require value and ttl when storing Item struct
Browse files Browse the repository at this point in the history
  • Loading branch information
tfwright committed Feb 8, 2024
1 parent b132019 commit 5595f14
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 29 deletions.
56 changes: 28 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,37 +54,37 @@ Notice the `name: :my_cache` option. The resulting process will be registered un
# Note: all of these requests run in the caller process, without going through
# the started process.

ConCache.put(:my_cache, key, value) # inserts value or overwrites the old one
ConCache.insert_new(:my_cache, key, value) # inserts value or returns {:error, :already_exists}
ConCache.get(:my_cache, key)
ConCache.delete(:my_cache, key)
ConCache.put(:my_cache, :key, "value") # inserts value or overwrites the old one
ConCache.insert_new(:my_cache, :key, "value") # inserts value or returns {:error, :already_exists}
ConCache.get(:my_cache, :key)
ConCache.delete(:my_cache, :key)
ConCache.size(:my_cache)

ConCache.update(:my_cache, key, fn(old_value) ->
ConCache.update(:my_cache, :key, fn(old_value) ->
# This function is isolated on a row level. Modifications such as update, put, delete,
# on this key will wait for this function to finish.
# Modifications on other items are not affected.
# Reads are always dirty.

{:ok, new_value}
{:ok, "new_value"}
end)

# Similar to update, but executes provided function only if item exists.
# Otherwise returns {:error, :not_existing}
ConCache.update_existing(:my_cache, key, fn(old_value) ->
{:ok, new_value}
ConCache.update_existing(:my_cache, :key, fn(old_value) ->
{:ok, "new_value"}
end)

# Returns existing value, or calls function and stores the result.
# If many processes simultaneously invoke this function for the same key, the function will be
# executed only once, with all others reading the value from cache.
ConCache.get_or_store(:my_cache, key, fn() ->
initial_value
ConCache.get_or_store(:my_cache, :key, fn() ->
"initial_value"
end)

# Similar to get_or_store/3 but works with :ok/:error tuples.
# The value is cached only if the function returns an :ok tuple.
ConCache.fetch_or_store(:my_cache, key, fn ->
ConCache.fetch_or_store(:my_cache, :key, fn ->
case call_api() do
# The processed value will be cached and returned as an :ok tuple.
{:ok, data} -> {:ok, process_data(data)}
Expand All @@ -97,13 +97,13 @@ end)
Dirty modifiers operate directly on ETS record without trying to acquire the row lock:

```elixir
ConCache.dirty_put(:my_cache, key, value)
ConCache.dirty_insert_new(:my_cache, key, value)
ConCache.dirty_delete(:my_cache, key)
ConCache.dirty_update(:my_cache, key, fn(old_value) -> ... end)
ConCache.dirty_update_existing(:my_cache, key, fn(old_value) -> ... end)
ConCache.dirty_get_or_store(:my_cache, key, fn() -> ... end)
ConCache.dirty_fetch_or_store(:my_cache, key, fn() -> ... end)
ConCache.dirty_put(:my_cache, :key, "value")
ConCache.dirty_insert_new(:my_cache, :key, "value")
ConCache.dirty_delete(:my_cache, :key)
ConCache.dirty_update(:my_cache, :key, fn(old_value) -> ... end)
ConCache.dirty_update_existing(:my_cache, :key, fn(old_value) -> ... end)
ConCache.dirty_get_or_store(:my_cache, :key, fn() -> ... end)
ConCache.dirty_fetch_or_store(:my_cache, :key, fn() -> ... end)
```

### Callback
Expand All @@ -113,8 +113,8 @@ You can register your own function which will be invoked after an element is sto
```elixir
{ConCache, [name: :my_cache, callback: fn(data) -> ... end]}

ConCache.put(:my_cache, key, value) # fun will be called with {:update, cache_pid, key, value}
ConCache.delete(:my_cache, key) # fun will be called with {:delete, cache_pid, key}
ConCache.put(:my_cache, :key, "value") # fun will be called with {:update, cache_pid, key, value}
ConCache.delete(:my_cache, :key) # fun will be called with {:delete, cache_pid, key}
```

The delete callback is invoked before the item is deleted, so you still have the chance to fetch the value from the cache and do something with it.
Expand Down Expand Up @@ -145,26 +145,26 @@ However, the item lifetime is renewed on every modification. Reads don't extend
In addition, you can manually renew item's ttl:

```elixir
ConCache.touch(:my_cache, key)
ConCache.touch(:my_cache, :key)
```

And you can override ttl for each item:
If you would like to set a custom ttl for specific key, you can pass a `Concache.Item`` struct instead of a raw value:

```elixir
ConCache.put(:my_cache, key, %ConCache.Item{value: value, ttl: ttl})
ConCache.put(:my_cache, :key, %ConCache.Item{value: "value", ttl: :timer.seconds(25)})

ConCache.update(:my_cache, key, fn(old_value) ->
{:ok, %ConCache.Item{value: new_value, ttl: ttl}}
ConCache.update(:my_cache, :key, fn(old_value) ->
{:ok, %ConCache.Item{value: "new_value", ttl: :timer.seconds(25)}}
end)
```

And you can update an item without resetting the item's ttl:

```elixir
ConCache.put(:my_cache, key, %ConCache.Item{value: value, ttl: :no_update})
ConCache.put(:my_cache, :key, %ConCache.Item{value: "value", ttl: :no_update})

ConCache.update(:my_cache, key, fn(old_value) ->
{:ok, %ConCache.Item{value: new_value, ttl: :no_update}}
ConCache.update(:my_cache, :key, fn(old_value) ->
{:ok, %ConCache.Item{value: "new_value", ttl: :no_update}}
end)
```

Expand Down
3 changes: 2 additions & 1 deletion lib/con_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ defmodule ConCache.Item do
@moduledoc """
This struct can be used in place of naked values to set per-item TTL values.
"""
defstruct value: nil, ttl: :infinity
@enforce_keys [:value, :ttl]
defstruct [:value, :ttl]

@type t :: %ConCache.Item{
value: ConCache.value(),
Expand Down

0 comments on commit 5595f14

Please sign in to comment.