Skip to content

Commit 4688a34

Browse files
committed
Overall updates
1 parent 84b3e42 commit 4688a34

File tree

12 files changed

+404
-93
lines changed

12 files changed

+404
-93
lines changed

.github/workflows/ci.yml

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,19 @@ jobs:
2222
- elixir: 1.18.x
2323
otp: 27.x
2424
os: 'ubuntu-latest'
25-
style: true
25+
lint: true
2626
coverage: true
2727
sobelow: true
2828
dialyzer: true
29-
doctor: true
3029
- elixir: 1.17.x
31-
otp: 26.x
32-
os: 'ubuntu-latest'
33-
- elixir: 1.16.x
34-
otp: 26.x
30+
otp: 27.x
3531
os: 'ubuntu-latest'
36-
- elixir: 1.15.x
32+
- elixir: 1.17.x
3733
otp: 25.x
3834
os: 'ubuntu-latest'
3935
- elixir: 1.14.x
40-
otp: 23.x
41-
os: 'ubuntu-20.04'
36+
otp: 24.x
37+
os: 'ubuntu-latest'
4238

4339
env:
4440
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
@@ -86,14 +82,22 @@ jobs:
8682
mix deps.get
8783
if: ${{ steps.mix-cache.outputs.cache-hit != 'true' }}
8884

89-
- name: Compile code
85+
- name: Compile deps
86+
run: mix deps.compile
87+
88+
- name: Check unused dependencies
89+
run: mix deps.unlock --check-unused
90+
if: ${{ matrix.lint }}
91+
92+
- name: Compile lint
9093
run: mix compile --warnings-as-errors
94+
if: ${{ matrix.lint }}
9195

9296
- name: Run style and code consistency checks
9397
run: |
9498
mix format --check-formatted
9599
mix credo --strict
96-
if: ${{ matrix.style }}
100+
if: ${{ matrix.lint }}
97101

98102
- name: Run tests
99103
run: |
@@ -114,6 +118,10 @@ jobs:
114118
flags: unittests-elixir-${{ matrix.elixir }}-otp-${{ matrix.otp }}
115119
if: ${{ matrix.coverage }}
116120

121+
- name: Run sobelow
122+
run: mix sobelow --skip --exit Low
123+
if: ${{ matrix.sobelow }}
124+
117125
- name: Restore PLT Cache
118126
uses: actions/cache@v3
119127
id: plt-cache

.tool-versions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
elixir 1.18.2-otp-27
2-
erlang 27.2.1
1+
elixir 1.18.3-otp-27
2+
erlang 27.3.3

LICENSE renamed to LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
MIT License
1+
# MIT License
22

33
Copyright (c) 2024 Carlos Andres Bolaños R.A.
44

README.md

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1-
# Nebulex Distributed
1+
# Nebulex Distributed :spider_web:
22
> Distributed cache topologies adapters for [Nebulex][Nebulex].
33
4-
[Nebulex]: https://github.com/cabol/nebulex
4+
[Nebulex]: http://github.com/elixir-nebulex/nebulex
55

6-
![CI](https://github.com/elixir-nebulex/nebulex_distributed/workflows/CI/badge.svg)
7-
[![Documentation](https://img.shields.io/badge/Documentation-ff69b4)](https://hexdocs.pm/nebulex_distributed)
8-
[![Hex Version](https://img.shields.io/hexpm/v/nebulex_distributed.svg)](https://hex.pm/packages/nebulex_distributed)
9-
[![Codecov](https://codecov.io/gh/elixir-nebulex/nebulex_distributed/graph/badge.svg)](https://codecov.io/gh/elixir-nebulex/nebulex_distributed/graph/badge.svg)
6+
![CI](http://github.com/elixir-nebulex/nebulex_distributed/workflows/CI/badge.svg)
7+
[![Codecov](http://codecov.io/gh/elixir-nebulex/nebulex_distributed/graph/badge.svg)](http://codecov.io/gh/elixir-nebulex/nebulex_distributed/graph/badge.svg)
8+
[![Hex.pm](http://img.shields.io/hexpm/v/nebulex_distributed.svg)](http://hex.pm/packages/nebulex_distributed)
9+
[![Documentation](http://img.shields.io/badge/Documentation-ff69b4)](http://hexdocs.pm/nebulex_distributed)
1010

11-
See the [online documentation][online_docs] for more information.
11+
## About
12+
13+
One of the goals of Nebulex is also to provide the ability to set up distributed
14+
cache topologies, but this feature will depend on the adapters. Here is where
15+
**"Nebulex Distributed"** comes in. It provides the following adapters to set up
16+
distributed topologies:
1217

13-
[online_docs]: https://hexdocs.pm/nebulex_distributed/
18+
* `Nebulex.Adapters.Multilevel` - Multi-level distributed cache topology.
19+
* `Nebulex.Adapters.Partitioned` - Partitioned cache topology.
20+
* `Nebulex.Adapters.Replicated` - Replicated cache topology (**WIP!**).
21+
22+
These adapters work more as wrappers for an existing local adapter and provide
23+
the distributed topology on top of it. You can optionally set the adapter for
24+
the primary cache storage with the option `:primary_storage_adapter`.
1425

1526
## Installation
1627

@@ -19,14 +30,14 @@ Add `:nebulex_distributed` to your list of dependencies in `mix.exs`:
1930
```elixir
2031
def deps do
2132
[
22-
{:nebulex_distributed, "~> 3.0"}
33+
{:nebulex_distributed, "~> 3.0.0-rc.1"}
2334
]
2435
end
2536
```
2637

27-
## Usage
38+
## Partitioned cache topology example
2839

29-
You can define a cache as follows:
40+
You can define a partitioned cache as follows:
3041

3142
```elixir
3243
defmodule MyApp.PartitionedCache do
@@ -65,8 +76,69 @@ def start(_type, _args) do
6576
end
6677
```
6778

79+
## Near cache topology example
80+
81+
To set up a near cache, you use the multilevel adapter, like so:
82+
83+
```elixir
84+
defmodule MyApp.Multilevel do
85+
use Nebulex.Cache,
86+
otp_app: :nebulex,
87+
adapter: Nebulex.Adapters.Multilevel
88+
89+
defmodule L1 do
90+
use Nebulex.Cache,
91+
otp_app: :nebulex,
92+
adapter: Nebulex.Adapters.Local
93+
end
94+
95+
defmodule L2 do
96+
use Nebulex.Cache,
97+
otp_app: :nebulex,
98+
adapter: Nebulex.Adapters.Partitioned
99+
end
100+
end
101+
```
102+
103+
Where the configuration for the cache and its levels must be in your
104+
application environment, usually defined in your `config/config.exs`:
105+
106+
```elixir
107+
config :my_app, MyApp.Multilevel,
108+
levels: [
109+
{
110+
MyApp.Multilevel.L1,
111+
gc_interval: :timer.hours(12),
112+
backend: :shards
113+
},
114+
{
115+
MyApp.Multilevel.L2,
116+
primary: [
117+
gc_interval: :timer.hours(12),
118+
backend: :shards
119+
]
120+
}
121+
]
122+
```
123+
124+
If your application was generated with a supervisor (by passing `--sup`
125+
to `mix new`) you will have a `lib/my_app/application.ex` file containing
126+
the application start callback that defines and starts your supervisor.
127+
You just need to edit the `start/2` function to start the cache as a
128+
supervisor on your application's supervisor:
129+
130+
```elixir
131+
def start(_type, _args) do
132+
children = [
133+
{MyApp.Multilevel, []},
134+
...
135+
]
136+
```
137+
68138
See the [online documentation][online_docs] for more information.
69139

140+
[online_docs]: http://hexdocs.pm/nebulex_distributed
141+
70142
## Testing
71143

72144
Since this adapter uses support modules and shared tests from `Nebulex`,
@@ -110,38 +182,39 @@ You will find the coverage report within `cover/excoveralls.html`.
110182
## Benchmarks
111183

112184
The adapter provides a set of basic benchmark tests using the library
113-
[benchee](https://github.com/PragTob/benchee), and they are located within
185+
[benchee](http://github.com/PragTob/benchee), and they are located within
114186
the directory [benchmarks](./benchmarks).
115187

116188
To run a benchmark test you have to run:
117189

118190
```
119-
$ MIX_ENV=test mix run benchmarks/{BENCH_TEST_FILE}
191+
mix run benchmarks/BENCH_TEST_FILE
120192
```
121193

122194
Where `BENCH_TEST_FILE` can be any of:
123195

124196
* `partitioned_bench.exs`: benchmark for the partitioned adapter using
125197
the `Nebulex.Adapters.Local` as primary storage.
198+
* `multilevel_bench.exs`: benchmark for the multilevel adapter.
126199

127200
## Contributing
128201

129202
Contributions to Nebulex are very welcome and appreciated!
130203

131-
Use the [issue tracker](https://github.com/elixir-nebulex/nebulex_distributed/issues)
204+
Use the [issue tracker](http://github.com/elixir-nebulex/nebulex_distributed/issues)
132205
for bug reports or feature requests. Open a
133-
[pull request](https://github.com/elixir-nebulex/nebulex_distributed/pulls)
206+
[pull request](http://github.com/elixir-nebulex/nebulex_distributed/pulls)
134207
when you are ready to contribute.
135208

136209
When submitting a pull request you should not update the
137210
[CHANGELOG.md](CHANGELOG.md), and also make sure you test your changes
138211
thoroughly, include unit tests alongside new or changed code.
139212

140-
Before to submit a PR it is highly recommended to run `mix check` and ensure
213+
Before to submit a PR it is highly recommended to run `mix test.ci` and ensure
141214
all checks run successfully.
142215

143216
## Copyright and License
144217

145218
Copyright (c) 2024 Carlos Andres Bolaños R.A.
146219

147-
`Nebulex.Adapters.Local` source code is licensed under the [MIT License](LICENSE).
220+
`nebulex_distributed` source code is licensed under the [MIT License](LICENSE).

benchmarks/multilevel_bench.exs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
## Benchmarks
2+
3+
_ = Application.start(:telemetry)
4+
5+
defmodule BenchCache do
6+
use Nebulex.Cache,
7+
otp_app: :nebulex,
8+
adapter: Nebulex.Adapters.Multilevel
9+
10+
defmodule L1 do
11+
use Nebulex.Cache,
12+
otp_app: :nebulex,
13+
adapter: Nebulex.Adapters.Local
14+
end
15+
16+
defmodule L2 do
17+
use Nebulex.Cache,
18+
otp_app: :nebulex,
19+
adapter: Nebulex.Adapters.Partitioned
20+
end
21+
end
22+
23+
# gc interval
24+
gc_interval = :timer.hours(1)
25+
26+
# start caches
27+
{:ok, local} =
28+
BenchCache.start_link(
29+
levels: [
30+
{BenchCache.L1, gc_interval: gc_interval},
31+
{BenchCache.L2, primary: [gc_interval: gc_interval]}
32+
]
33+
)
34+
35+
# samples
36+
keys = Enum.to_list(1..10_000)
37+
bulk = for x <- 1..100, do: {x, x}
38+
39+
# init caches
40+
Enum.each(1..5000, &BenchCache.put(&1, &1))
41+
42+
inputs = %{
43+
"Redis Cache" => BenchCache
44+
}
45+
46+
benchmarks = %{
47+
"fetch" => fn {cache, random} ->
48+
cache.fetch(random)
49+
end,
50+
"put!" => fn {cache, random} ->
51+
cache.put!(random, random)
52+
end,
53+
"put_new!" => fn {cache, random} ->
54+
cache.put_new!(random, random)
55+
end,
56+
"replace!" => fn {cache, random} ->
57+
cache.replace!(random, random)
58+
end,
59+
"put_all!" => fn {cache, _random} ->
60+
cache.put_all!(bulk)
61+
end,
62+
"delete!" => fn {cache, random} ->
63+
cache.delete!(random)
64+
end,
65+
"take" => fn {cache, random} ->
66+
cache.take(random)
67+
end,
68+
"has_key?" => fn {cache, random} ->
69+
cache.has_key?(random)
70+
end,
71+
"ttl" => fn {cache, random} ->
72+
cache.ttl(random)
73+
end,
74+
"expire!" => fn {cache, random} ->
75+
cache.expire!(random, 1000)
76+
end,
77+
"incr!" => fn {cache, _random} ->
78+
cache.incr!(:counter, 1)
79+
end,
80+
"update!" => fn {cache, random} ->
81+
cache.update!(random, 1, &Kernel.+(&1, 1))
82+
end,
83+
"get_and_update!" => fn {cache, random} ->
84+
cache.get_and_update!(random, fn
85+
nil -> {nil, 1}
86+
val -> {val, val * 2}
87+
end)
88+
end,
89+
"get_all!" => fn {cache, _random} ->
90+
cache.get_all!(in: [1, 2, 3, 4, 5, 6, 7, 8, 9])
91+
end,
92+
"count_all!" => fn {cache, _random} ->
93+
cache.count_all!()
94+
end,
95+
"delete_all!" => fn {cache, _random} ->
96+
cache.delete_all!(in: [1, 2, 3])
97+
end
98+
}
99+
100+
Benchee.run(
101+
benchmarks,
102+
inputs: inputs,
103+
before_scenario: fn cache ->
104+
{cache, Enum.random(keys)}
105+
end,
106+
formatters: [
107+
{Benchee.Formatters.Console, comparison: false, extended_statistics: true},
108+
{Benchee.Formatters.HTML, extended_statistics: true, auto_open: false}
109+
],
110+
print: [
111+
fast_warning: false
112+
]
113+
)
114+
115+
# stop caches s
116+
if Process.alive?(local), do: Supervisor.stop(local)

0 commit comments

Comments
 (0)