Skip to content

Commit

Permalink
Cache API based on Ok/Error tuples (Nebulex v3)
Browse files Browse the repository at this point in the history
- Remove deprecated module `Nebulex.Hook`
- Use NimbleOptions for defining and validating cache options
- Replace `:ttl` field by `:exp` in local adapter
- Replace `Mock` with `Mimic`
- Add `:dynamic_cache` to set the cache instance dynamically in runtime
- Improve docs.
  • Loading branch information
cabol committed Nov 26, 2022
1 parent f7a91a8 commit 1cb7913
Show file tree
Hide file tree
Showing 89 changed files with 4,930 additions and 3,434 deletions.
4 changes: 2 additions & 2 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@
## Refactoring Opportunities
#
{Credo.Check.Refactor.CondStatements, []},
{Credo.Check.Refactor.CyclomaticComplexity, []},
{Credo.Check.Refactor.CyclomaticComplexity, [max_complexity: 40]},
{Credo.Check.Refactor.FunctionArity, []},
{Credo.Check.Refactor.LongQuoteBlocks, [max_line_count: 300, ignore_comments: true]},
{Credo.Check.Refactor.LongQuoteBlocks, [max_line_count: 200]},
# {Credo.Check.Refactor.MapInto, []},
{Credo.Check.Refactor.MatchInCondition, []},
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
Expand Down
27 changes: 26 additions & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
locals_without_parens = [
# Nebulex.Helpers
unwrap_or_raise: 1,
wrap_ok: 1,
wrap_error: 1,
wrap_error: 2,

# Nebulex.Cache
dynamic_cache: 2,

# Nebulex.Caching
cache_ref: 1,
cache_ref: 2,

# Tests
deftests: 1,
deftests: 2,
setup_with_cache: 1,
setup_with_cache: 2,
setup_with_dynamic_cache: 2,
setup_with_dynamic_cache: 3
]

[
import_deps: [:stream_data],
inputs: ["{mix,.formatter}.exs", "{config,lib,test,benchmarks}/**/*.{ex,exs}"],
line_length: 100
line_length: 100,
locals_without_parens: locals_without_parens,
export: [locals_without_parens: locals_without_parens]
]
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,25 @@ on:
jobs:
nebulex_test:
name: 'Nebulex Test (Elixir ${{ matrix.elixir }} OTP ${{ matrix.otp }})'
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}

strategy:
matrix:
include:
- elixir: 1.14.x
otp: 25.x
os: 'ubuntu-latest'
style: true
coverage: true
sobelow: true
dialyzer: true
- elixir: 1.13.x
otp: 24.x
os: 'ubuntu-latest'
- elixir: 1.11.x
otp: 23.x
os: 'ubuntu-20.04'
inch-report: true
- elixir: 1.9.x
otp: 22.x

env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
Expand Down Expand Up @@ -101,7 +102,7 @@ jobs:
id: plt-cache
with:
path: priv/plts
key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plt-v1
key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plt-v3-1
if: ${{ matrix.dialyzer }}

- name: Create PLTs
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ erl_crash.dump
/priv
.sobelow*
/config
Elixir*
27 changes: 0 additions & 27 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,6 @@ All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v2.4.2](https://github.com/cabol/nebulex/tree/v2.4.2) (2022-11-04)

[Full Changelog](https://github.com/cabol/nebulex/compare/v2.4.1...v2.4.2)

**Closed issues:**

- Adapter configuration per-env?
[#171](https://github.com/cabol/nebulex/issues/171)
- On-change handler for write-through decorators
[#165](https://github.com/cabol/nebulex/issues/165)
- Document test env setup with decorators?
[#155](https://github.com/cabol/nebulex/issues/155)
- Managing Failovers in the cluster
[#131](https://github.com/cabol/nebulex/issues/131)

**Merged pull requests:**

- Make Multilevel adapter apply deletes in reverse order
[#174](https://github.com/cabol/nebulex/pull/174)
([martosaur](https://github.com/martosaur))
- Use import Bitwise instead of use Bitwise
[#172](https://github.com/cabol/nebulex/pull/172)
([ryvasquez](https://github.com/ryvasquez))
- Fix result of getting value by non existent key
[#166](https://github.com/cabol/nebulex/pull/166)
([fuelen](https://github.com/fuelen))

## [v2.4.1](https://github.com/cabol/nebulex/tree/v2.4.1) (2022-07-10)

[Full Changelog](https://github.com/cabol/nebulex/compare/v2.4.0...v2.4.1)
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ for more information.
[cachex]: https://github.com/whitfin/cachex
[redis]: https://redis.io/
[memcached]: https://memcached.org/
[nbx_caching]: http://hexdocs.pm/nebulex/Nebulex.Caching.html
[nbx_caching]: http://hexdocs.pm/nebulex/Nebulex.Caching.Decorators.html
[cache_patterns]: http://hexdocs.pm/nebulex/cache-usage-patterns.html
[cache_topologies]: https://docs.oracle.com/middleware/1221/coherence/develop-applications/cache_intro.htm

Expand All @@ -47,20 +47,19 @@ Partitioned | [Nebulex.Adapters.Partitioned][pa] | Built-In
Replicated | [Nebulex.Adapters.Replicated][ra] | Built-In
Multilevel | [Nebulex.Adapters.Multilevel][ma] | Built-In
Nil (special adapter that disables the cache) | [Nebulex.Adapters.Nil][nil] | Built-In
Cachex | Nebulex.Adapters.Cachex | [nebulex_adapters_cachex][nbx_cachex]
Redis | NebulexRedisAdapter | [nebulex_redis_adapter][nbx_redis]
Cachex | Nebulex.Adapters.Cachex | [nebulex_adapters_cachex][nbx_cachex]
Distributed with Horde | Nebulex.Adapters.Horde | [nebulex_adapters_horde][nbx_horde]

[la]: http://hexdocs.pm/nebulex/Nebulex.Adapters.Local.html
[pa]: http://hexdocs.pm/nebulex/Nebulex.Adapters.Partitioned.html
[ra]: http://hexdocs.pm/nebulex/Nebulex.Adapters.Replicated.html
[ma]: http://hexdocs.pm/nebulex/Nebulex.Adapters.Multilevel.html
[nil]: http://hexdocs.pm/nebulex/Nebulex.Adapters.Nil.html
[nbx_cachex]: https://github.com/cabol/nebulex_adapters_cachex
[nbx_redis]: https://github.com/cabol/nebulex_redis_adapter
[nbx_cachex]: https://github.com/cabol/nebulex_adapters_cachex
[nbx_horde]: https://github.com/eliasdarruda/nebulex_adapters_horde


For example, if you want to use a built-in cache, add to your `mix.exs` file:

```elixir
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/bench_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ defmodule BenchHelper do
"take" => fn input ->
cache.take(input)
end,
"has_key?" => fn input ->
cache.has_key?(input)
"exists?" => fn input ->
cache.exists?(input)
end,
"count_all" => fn _input ->
cache.count_all()
Expand Down
1 change: 1 addition & 0 deletions coveralls.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
},

"skip_files": [
"lib/nebulex/cache/options.ex",
"test/support/*",
"test/dialyzer/*"
]
Expand Down
7 changes: 4 additions & 3 deletions guides/cache-usage-patterns.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Cache Usage Patterns via Nebulex.Caching
# Cache Usage Patterns via Nebulex.Caching.Decorators

There are several common access patterns when using a cache. **Nebulex**
supports most of these patterns by means of [Nebulex.Caching][nbx_caching].
supports most of these patterns by means of
[Nebulex.Caching.Decorators][nbx_caching].

[nbx_caching]: http://hexdocs.pm/nebulex/Nebulex.Caching.html
[nbx_caching]: http://hexdocs.pm/nebulex/Nebulex.Caching.Decorators.html

> Most of the following documentation about caching patterns it based on
[EHCache Docs][EHCache]
Expand Down
66 changes: 43 additions & 23 deletions guides/creating-new-adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,10 @@ mix test
54) test put_all/2 puts the given entries using different data types at once (NebulexMemoryAdapterTest)
test/nebulex_memory_adapter_test.exs:128
** (UndefinedFunctionError) function NebulexMemoryAdapter.TestCache.delete_all/0 is undefined or private. Did you mean:

* delete/1
* delete/2

stacktrace:
(nebulex_memory_adapter 0.1.0) NebulexMemoryAdapter.TestCache.delete_all()
test/nebulex_memory_adapter_test.exs:9: NebulexMemoryAdapterTest.__ex_unit_setup_0/1
Expand All @@ -256,21 +256,25 @@ defmodule NebulexMemoryAdapter do
@behaviour Nebulex.Adapter
@behaviour Nebulex.Adapter.Queryable

import Nebulex.Helpers

@impl Nebulex.Adapter
defmacro __before_compile__(_env), do: :ok

@impl Nebulex.Adapter
def init(_opts) do
child_spec = Supervisor.child_spec({Agent, fn -> %{} end}, id: {Agent, 1})

{:ok, child_spec, %{}}
end

@impl Nebulex.Adapter.Queryable
def execute(adapter_meta, :delete_all, query, opts) do
deleted = Agent.get(adapter_meta.pid, &map_size/1)

Agent.update(adapter_meta.pid, fn _state -> %{} end)

deleted
wrap_ok deleted
end
end
```
Expand Down Expand Up @@ -304,23 +308,26 @@ defmodule NebulexMemoryAdapter do
@behaviour Nebulex.Adapter.Entry
@behaviour Nebulex.Adapter.Queryable

import Nebulex.Helpers

@impl Nebulex.Adapter
defmacro __before_compile__(_env), do: :ok

@impl Nebulex.Adapter
def init(_opts) do
child_spec = Supervisor.child_spec({Agent, fn -> %{} end}, id: {Agent, 1})

{:ok, child_spec, %{}}
end

@impl Nebulex.Adapter.Entry
def get(adapter_meta, key, _opts) do
Agent.get(adapter_meta.pid, &Map.get(&1, key))
def fetch(adapter_meta, key, _opts) do
wrap_ok Agent.get(adapter_meta.pid, &Map.get(&1, key))
end

@impl Nebulex.Adapter.Entry
def get_all(adapter_meta, keys, _opts) do
Agent.get(adapter_meta.pid, &Map.take(&1, keys))
wrap_ok Agent.get(adapter_meta.pid, &Map.take(&1, keys))
end

@impl Nebulex.Adapter.Entry
Expand All @@ -331,48 +338,58 @@ defmodule NebulexMemoryAdapter do
put(adapter_meta, key, value, ttl, :put, opts)
true
end
|> wrap_ok()
end

def put(adapter_meta, key, value, ttl, :replace, opts) do
if get(adapter_meta, key, []) do
put(adapter_meta, key, value, ttl, :put, opts)

true
else
false
end
|> wrap_ok()
end

def put(adapter_meta, key, value, _ttl, _on_write, _opts) do
Agent.update(adapter_meta.pid, &Map.put(&1, key, value))
true

wrap_ok true
end

@impl Nebulex.Adapter.Entry
def put_all(adapter_meta, entries, ttl, :put_new, opts) do
if get_all(adapter_meta, Map.keys(entries), []) == %{} do
put_all(adapter_meta, entries, ttl, :put, opts)

true
else
false
end
|> wrap_ok()
end

def put_all(adapter_meta, entries, _ttl, _on_write, _opts) do
entries = Map.new(entries)

Agent.update(adapter_meta.pid, &Map.merge(&1, entries))
true

wrap_ok true
end

@impl Nebulex.Adapter.Entry
def delete(adapter_meta, key, _opts) do
Agent.update(adapter_meta.pid, &Map.delete(&1, key))
wrap_ok Agent.update(adapter_meta.pid, &Map.delete(&1, key))
end

@impl Nebulex.Adapter.Entry
def take(adapter_meta, key, _opts) do
value = get(adapter_meta, key, [])

delete(adapter_meta, key, [])
value

wrap_ok value
end

@impl Nebulex.Adapter.Entry
Expand All @@ -381,48 +398,51 @@ defmodule NebulexMemoryAdapter do
Map.update(state, key, default + amount, fn v -> v + amount end)
end)

get(adapter_meta, key, [])
wrap_ok get(adapter_meta, key, [])
end

@impl Nebulex.Adapter.Entry
def has_key?(adapter_meta, key) do
Agent.get(adapter_meta.pid, &Map.has_key?(&1, key))
def exists?(adapter_meta, key, _opts) do
wrap_ok Agent.get(adapter_meta.pid, &Map.has_key?(&1, key))
end

@impl Nebulex.Adapter.Entry
def ttl(_adapter_meta, _key) do
nil
def ttl(_adapter_meta, _key, _opts) do
wrap_ok nil
end

@impl Nebulex.Adapter.Entry
def expire(_adapter_meta, _key, _ttl) do
true
def expire(_adapter_meta, _key, _ttl, _opts) do
wrap_ok true
end

@impl Nebulex.Adapter.Entry
def touch(_adapter_meta, _key) do
true
def touch(_adapter_meta, _key, _opts) do
wrap_ok true
end

@impl Nebulex.Adapter.Queryable
def execute(adapter_meta, :delete_all, _query, _opts) do
deleted = execute(adapter_meta, :count_all, nil, [])

Agent.update(adapter_meta.pid, fn _state -> %{} end)

deleted
wrap_ok deleted
end

def execute(adapter_meta, :count_all, _query, _opts) do
Agent.get(adapter_meta.pid, &map_size/1)
wrap_ok Agent.get(adapter_meta.pid, &map_size/1)
end

def execute(adapter_meta, :all, _query, _opts) do
Agent.get(adapter_meta.pid, &Map.values/1)
wrap_ok Agent.get(adapter_meta.pid, &Map.values/1)
end

@impl Nebulex.Adapter.Queryable
def stream(_adapter_meta, :invalid_query, _opts) do
raise Nebulex.QueryError, message: "foo", query: :invalid_query

wrap_error Nebulex.QueryError, message: "foo", query: :invalid_query
end

def stream(adapter_meta, _query, opts) do
Expand All @@ -438,7 +458,7 @@ defmodule NebulexMemoryAdapter do
&Map.keys/1
end

Agent.get(adapter_meta.pid, fun)
wrap_ok Agent.get(adapter_meta.pid, fun)
end
end
```
Expand Down
Loading

0 comments on commit 1cb7913

Please sign in to comment.