Skip to content

Commit 94f1d4e

Browse files
committed
[#36] Improve file2tab and tab2file to be compliant with ETS functions
1 parent 69f30f7 commit 94f1d4e

21 files changed

+557
-419
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ data
3030
edoc
3131
priv/*.so
3232
*.crashdump
33-
rebar.lock
3433
*_plt
3534
doc
36-
docs
35+
docs

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
language: erlang
22
otp_release:
3-
- 19.2
3+
- 19.3
44
- 18.3
5-
- 18.0
65
before_script:
76
- epmd -daemon
87
script:

Makefile

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,15 @@ ifeq ($(EPMD_PROC_NUM),)
3939
endif
4040

4141
test: check_rebar check_epmd
42-
$(REBAR) ct --name [email protected]
43-
$(REBAR) cover
42+
$(REBAR) do ct --name [email protected], cover
4443
rm -rf test/*.beam
4544

4645
local_test: check_rebar check_epmd
47-
$(REBAR) ct --suite=test/task_SUITE,test/state_SUITE,test/local_SUITE
48-
$(REBAR) cover
46+
$(REBAR) do ct --suite=test/task_SUITE,test/state_SUITE,test/local_SUITE, cover
4947
rm -rf test/*.beam
5048

5149
dist_test: check_rebar check_epmd
52-
$(REBAR) ct --name [email protected] --suite=test/dist_SUITE
53-
$(REBAR) cover
50+
$(REBAR) do ct --name [email protected] --suite=test/dist_SUITE, cover
5451
rm -rf test/*.beam
5552

5653
shell: check_rebar

README.md

Lines changed: 75 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,29 @@
55

66
ETS tables on steroids!
77

8-
[**Shards**](https://github.com/cabol/shards) is an **Erlang/Elixir** library/tool compatible with the ETS API,
9-
that implements [Sharding/Partitioning](https://en.wikipedia.org/wiki/Partition_(database)) support on top of
10-
ETS totally transparent and out-of-box. **Shards** might be probably the **simplest** option to scale-out ETS tables.
8+
[**Shards**](https://github.com/cabol/shards) is an **Erlang/Elixir** library/tool
9+
compatible with the ETS API, that implements [Sharding/Partitioning](https://en.wikipedia.org/wiki/Partition_(database)) support
10+
on top of ETS totally transparent and out-of-box. **Shards** might be probably
11+
the **simplest** option to scale-out ETS tables.
1112

1213
[Additional documentation on cabol.github.io](http://cabol.github.io/posts/2016/04/14/sharding-support-for-ets.html).
1314

1415

1516
## Introduction
1617

17-
Why might we need **Sharding** on ETS tables? Well, the main reason is to keep the lock contention under control,
18-
in order to scale-out ETS tables (linearly) and support higher levels of concurrency without lock issues
19-
(specially write-locks) – which most of the cases might cause significant performance degradation.
18+
Why might we need **Sharding** on ETS tables? Well, the main reason is to keep
19+
the lock contention under control, in order to scale-out ETS tables (linearly)
20+
and support higher levels of concurrency without lock issues; specially
21+
write-locks – which most of the cases might cause significant performance
22+
degradation.
2023

21-
Therefore, one of the most common and proven strategies to deal with these problems is [Sharding/Partitioning](https://en.wikipedia.org/wiki/Partition_(database))
22-
– the principle is pretty similar to [DHTs](https://en.wikipedia.org/wiki/Distributed_hash_table).
24+
Therefore, one of the most common and proven strategies to deal with these problems
25+
is [Sharding/Partitioning](https://en.wikipedia.org/wiki/Partition_(database))
26+
the principle is pretty similar to [DHTs](https://en.wikipedia.org/wiki/Distributed_hash_table).
2327

24-
Here is where **Shards** comes in. **Shards** makes it extremely easy to achieve all this, with **zero** effort.
25-
It provides an API compatible with [ETS](http://erlang.org/doc/man/ets.html) – with few exceptions.
26-
You can check the list of compatible ETS functions that **Shards** provides [HERE](https://github.com/cabol/shards/issues/1).
28+
Here is where **Shards** comes in. **Shards** makes it extremely easy to achieve
29+
all this, with **zero** effort. It provides an API compatible with [ETS](http://erlang.org/doc/man/ets.html) – with few exceptions. You can check
30+
the list of compatible ETS functions that **Shards** provides [HERE](https://github.com/cabol/shards/issues/1).
2731

2832

2933
## Usage
@@ -34,7 +38,7 @@ In your `rebar.config`:
3438

3539
```erlang
3640
{deps, [
37-
{shards, "0.4.3"}
41+
{shards, "0.4.4"}
3842
]}.
3943
```
4044

@@ -80,8 +84,8 @@ Option | Description | Default
8084
`{n_shards, pos_integer()}` | Allows to set the desired number of shards. | By default, the number of shards is calculated from the total online schedulers: `erlang:system_info(schedulers_online)`
8185
`{scope, l | g}` | Defines `shards` scope, in other words, if sharding will be applied locally (`l`) or global/distributed (`g`) | `l`
8286
`{restart_strategy, one_for_one | one_for_all}` | Allows to configure the restart strategy for `shards_owner_sup`. | `one_for_one`
83-
`{pick_shard_fun, pick_fun()}` | Function to pick the **shard** on which the `key` will be handled locally – used by `shards_local`. See [shards_state](./src/shards_state.erl). | `shards_local:pick/3`
84-
`{pick_node_fun, pick_fun()}` | Function to pick the **node** on which the `key` will be handled globally/distributed – used by `shards_dist`. See [shards_state](./src/shards_state.erl). | `shards_local:pick/3`
87+
`{pick_shard_fun, pick_fun()}` | Function to pick the **shard** on which the `key` will be handled locally – used by `shards_local`. See [shards_state](./src/shards_state.erl). | `shards_lib:pick/3`
88+
`{pick_node_fun, pick_fun()}` | Function to pick the **node** on which the `key` will be handled globally/distributed – used by `shards_dist`. See [shards_state](./src/shards_state.erl). | `shards_lib:pick/3`
8589
`{nodes, [node()]}` | Allows to set a list of nodes to auto setup a distributed table – the table is created in all given nodes and then all nodes are joined. This option only has effect if the option `{scope, g}` has been set. | `[]`
8690
`{sup_name, atom()}` | Allows to define a custom name for `shards_sup`. | `shards_sup`
8791

@@ -124,10 +128,11 @@ mytab2
124128
ok
125129
```
126130

127-
You will see the process tree of `shards` application. When you create a new "table," what happens behind
128-
is: `shards` creates a supervision tree dedicated only to that group of shards that will represent
129-
your logical table in multiple physical ETS tables and everything is handled auto-magically by `shards`,
130-
you only have to use the API like if you were working with a common ETS table.
131+
You will see the process tree of `shards` application. When you create a new "table,"
132+
what happens behind is: `shards` creates a supervision tree dedicated only to that
133+
group of shards that will represent your logical table in multiple physical ETS
134+
tables and everything is handled auto-magically by `shards`, you only have to use
135+
the API like if you were working with a common ETS table.
131136

132137
### Playing with shards
133138

@@ -181,8 +186,8 @@ See how `shards` gets shrinks.
181186

182187
## State
183188

184-
In the previous section we saw something about the `state`, how it can be fetched at any time.
185-
But, what is the `state`?
189+
In the previous section we saw something about the `state`, how it can be fetched
190+
at any time. But, what is the `state`?
186191

187192
There are different properties that have to be stored somewhere for `shards` to work
188193
correctly. Remember, `shards` has a logic on top of `ETS`, for example, compute the
@@ -198,11 +203,11 @@ The `shards` state is defined as:
198203

199204
```erlang
200205
-record(state, {
201-
module = shards_local :: module(),
202-
sup_name = shards_sup :: atom(),
203-
n_shards = ?N_SHARDS :: pos_integer(),
204-
pick_shard_fun = fun shards_local:pick/3 :: pick_fun(),
205-
pick_node_fun = fun shards_local:pick/3 :: pick_fun()
206+
module = shards_local :: module(),
207+
sup_name = shards_sup :: atom(),
208+
n_shards = ?N_SHARDS :: pos_integer(),
209+
pick_shard_fun = fun shards_lib:pick/3 :: pick_fun(),
210+
pick_node_fun = fun shards_lib:pick/3 :: pick_fun()
206211
}).
207212
```
208213

@@ -215,26 +220,31 @@ you can fetch the state, get any property and also other functions.
215220

216221
The module `shards` is a wrapper on top of two main modules:
217222

218-
* [shards_local](./src/shards_local.erl): Implements Sharding on top of ETS tables, but locally (on a single Erlang node).
219-
* [shards_dist](./src/shards_dist.erl): Implements Sharding but across multiple distributed Erlang nodes, which must
220-
run `shards` locally, since `shards_dist` uses `shards_local` internally. We'll cover
221-
the distributed part later.
223+
* [shards_local](./src/shards_local.erl): Implements Sharding on top of ETS tables,
224+
but locally (on a single Erlang node).
225+
* [shards_dist](./src/shards_dist.erl): Implements Sharding but across multiple
226+
distributed Erlang nodes, which must run `shards` locally, since `shards_dist`
227+
uses `shards_local` internally. We'll cover the distributed part later.
222228

223-
When you use `shards` on top of `shards_local`, a call to the control ETS table owned by `shards_owner_sup`
224-
must be done, in order to recover the [<i class="icon-refresh"></i> State](#state), mentioned previously.
225-
Most of the `shards_local` functions receive the **State** as a parameter, so it must be fetched before
226-
to call it. You can check how the `shards` module is implemented [HERE](./src/shards.erl).
229+
When you use `shards` on top of `shards_local`, a call to the control ETS table
230+
owned by `shards_owner_sup` must be done, in order to recover the
231+
[<i class="icon-refresh"></i> State](#state), mentioned previously.
227232

228-
If any microsecond matters to you, you can skip the call to the control ETS table by calling
229-
`shards_local` directly – there are two options to do it.
233+
Most of the `shards_local` functions receive the **State** as a parameter, so it
234+
must be fetched before to call it. You can check how the `shards` module is
235+
implemented [HERE](./src/shards.erl).
236+
237+
If any microsecond matters to you, you can skip the call to the control ETS
238+
table by calling `shards_local` directly – there are two options to do it.
230239

231240
### Option 1
232241

233-
The first option is getting the `state`, and passing it as an argument. Now the question is:
234-
how to get the **State**? Well, it's extremely easy, you can get the `state` when you call
235-
`shards:new/2` for the first time, or you can call `shards:state/1`, `shards_state:get/1` or
236-
`shards_state:new/0,1,2,3,4` at any time you want, and then it might be stored within
237-
the calling process, or wherever you want. E.g.:
242+
The first option is getting the `state`, and passing it as an argument. Now the
243+
question is: how to get the **State**? Well, it's extremely easy, you can get
244+
the `state` when you call `shards:new/2` for the first time, or you can call
245+
`shards:state/1`, `shards_state:get/1` or `shards_state:new/0,1,2,3,4` at any
246+
time you want, and then it might be stored within the calling process, or
247+
wherever you want. E.g.:
238248

239249
```erlang
240250
% take a look at the 2nd element of the returned tuple, that is the state
@@ -260,12 +270,13 @@ true
260270

261271
### Option 2
262272

263-
The second option is to call `shards_local` directly without the `state`, but this is only
264-
possible if you have created a table with default `shards` options – such as `n_shards`,
265-
`pick_shard_fun` and `pick_node_fun`. If you can take this option, it might be significantly
266-
better, since in this case no additional calls are needed, not even to recover the `state`
267-
(like in the previous option), because a new `state` is created with default values.
268-
Therefore, the call is mapped directly to an **ETS** function. E.g.:
273+
The second option is to call `shards_local` directly without the `state`, but
274+
this is only possible if you have created a table with default `shards` options
275+
– such as `n_shards`, `pick_shard_fun` and `pick_node_fun`. If you can take this
276+
option, it might be significantly better, since in this case no additional calls
277+
are needed, not even to recover the `state` (like in the previous option),
278+
because a new `state` is created with default values. Therefore, the call is
279+
mapped directly to an **ETS** function. E.g.:
269280

270281
```erlang
271282
% create a table without set n_shards, pick_shard_fun or pick_node_fun
@@ -279,15 +290,15 @@ true
279290
[{1,1}]
280291
```
281292

282-
Most of the cases this is not necessary, `shards` wrapper is more than enough, it adds only a
283-
few microseconds of latency. In conclusion, **Shards** gives you the flexibility to do it,
284-
but it's your call!
293+
Most of the cases this is not necessary, `shards` wrapper is more than enough,
294+
it adds only a few microseconds of latency. In conclusion, **Shards** gives you
295+
the flexibility to do it, but it's your call!
285296

286297

287298
## Distributed Shards
288299

289-
So far, we've seen how **Shards** works locally, now let's see how **Shards** works but
290-
distributed.
300+
So far, we've seen how **Shards** works locally, now let's see how **Shards**
301+
works but distributed.
291302

292303
**1.** Let's start 3 Erlang consoles running shards:
293304

@@ -412,14 +423,20 @@ And again, let's check it out from any node:
412423

413424
## Examples and/or Projects using Shards
414425

415-
* [ExShards](https://github.com/cabol/ex_shards)**Elixir** wrapper for `shards` with extra and nicer functions.
426+
* [ExShards](https://github.com/cabol/ex_shards) – Elixir wrapper for
427+
`shards` with extra and nicer functions.
428+
429+
* [Nebulex](https://github.com/cabol/nebulex) – Local and Distributed Caching
430+
Tool for Elixir.
416431

417-
* [KVX](https://github.com/cabol/kvx) – Simple/basic **Elixir** in-memory Key/Value Store using `shards` (default adapter).
432+
* [KVX](https://github.com/cabol/kvx) – Simple/basic Elixir in-memory Key/Value
433+
Store using `shards` (default adapter).
418434

419-
* [ErlBus](https://github.com/cabol/erlbus) uses `shards` to scale-out **Topics/Pids** table(s),
420-
which can be too large and with high concurrency level.
435+
* [ErlBus](https://github.com/cabol/erlbus) uses `shards` to scale-out
436+
Topics/Pids table(s), which can be too large and with high concurrency level.
421437

422-
* [Cacherl](https://github.com/ferigis/cacherl) uses `shards` to implement a Distributed Cache.
438+
* [Cacherl](https://github.com/ferigis/cacherl) uses `shards` to implement a
439+
Distributed Cache.
423440

424441

425442
## Running Tests
@@ -435,7 +452,8 @@ You can find tests results in `_build/test/logs`, and coverage in `_build/test/c
435452

436453
$ make doc
437454

438-
> **Note:** Once you run the previous command, a new folder `doc` is created, and you'll have a pretty nice HTML documentation.
455+
> **Note:** Once you run the previous command, a new folder `doc` is created,
456+
and you'll have a pretty nice HTML documentation.
439457

440458

441459
## Copyright and License

rebar.config

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@
2222

2323
{cover_enabled, true}.
2424

25-
%% == Dependencies ==
26-
27-
{deps, []}.
28-
2925
%% == Common Test ==
3026

3127
{ct_compile_opts, [
@@ -62,17 +58,14 @@
6258
]}
6359
]}.
6460

65-
%% == Shell ==
66-
67-
{shell, [{apps, [shards]}]}.
68-
6961
%% == Dialyzer ==
7062

7163
{dialyzer, [
7264
{warnings, [
7365
no_return,
7466
unmatched_returns,
75-
error_handling
67+
error_handling,
68+
unknown
7669
]},
7770
{plt_apps, top_level_deps},
7871
{plt_extra_apps, []},
@@ -81,3 +74,7 @@
8174
{base_plt_location, "."},
8275
{base_plt_prefix, "shards"}
8376
]}.
77+
78+
%% == Shell ==
79+
80+
{shell, [{apps, [shards]}]}.

rebar.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[].

src/shards.app.src

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{application, shards, [
22
{description, "ETS with Sharding support."},
3-
{vsn, "0.4.3"},
3+
{vsn, "0.5.0"},
44
{registered, []},
5-
{mod, {shards, []}},
5+
{mod, {shards_app, []}},
66
{applications, [kernel, stdlib]},
77
{env,[]},
88
{modules, []},
9-
{maintainers, ["Carlos Andres Bolanos"]},
9+
{maintainers, ["Carlos A Bolanos"]},
1010
{licenses, ["MIT"]},
1111
{build_tools, ["rebar3", "make"]},
1212
{links, [

0 commit comments

Comments
 (0)