Partitioned/Sharded ETS tables.
shards
implements the ETS API to keep compatibility and make the usage
straightforward; exactly the same if you were using ets
.
shards
and it is 100% transparent for the client. Provides
a logical view of a single ETS table, but internally, it is composed by
N
number of partitions
and each partition has associated an ETS table.
shards
keeps the lock contention under
control enabling ETS tables to scale out and support higher levels of
concurrency without lock issues; specially write-locks, which most of the
cases might cause significant performance degradation.
When a table is created with shards:new/2
, a new supervision tree is
created to represent the partitioned table. There is a main supervisor
shards_partition_sup
that create an ETS table to store the metadata
and also starts the children which are the partitions to create. Each
partition is owned by shards_partition
(it is a gen_server
) and it
creates an ETS table for storing data mapped to that partition. The
supervision tree looks like:
[shards_partition]--><ETS-Table> / [shards_partition_sup]--<-[shards_partition]--><ETS-Table> | \ <Metadata-ETS-Table> [shards_partition]--><ETS-Table>
The returned value by shards:new/2
may be an atom if it is a named
table or a reference otherwise, and in the second case the returned
reference is the one of the metadata table, which is the main entry
point and it is owned by the main supervisor. See shards:new/2
for
more information.
> Tab = shards:new(tab, []). #Ref<0.1541908042.2337144842.31535> > shards:insert(Tab, [{a, 1}, {b, 2}, {c, 3}]). true > shards:lookup(Tab, a). [{a,1}] > shards:lookup(Tab, b). [{b,2}] > shards:lookup(Tab, c). [{c,3}] > shards:lookup_element(Tab, c, 2). 3 > shards:lookup(Tab, d). [] > shards:delete(Tab, c). true > shards:lookup(Tab, c). []
As you can see, the usage is exactly the same if you were using ets
,
you can try the rest of the ETS API but with shards
module.
shards
aims to keep 100% compatibility with current ETS API,
the semantic for some of the functions may be a bit different due to
the nature of sharding; it is not the same having all the entries in
a single table than distributed across multiple ones. For example,
for query-based functions like select
, match
, etc., the returned
entries are the same but not necessary the same order than ets
.
For first
, next
, and last
they behave the similar in the sense
by means of them a partitioned table is traversed, so the final result
is the same, but the order in which the entries are traversed may be
different. Therefore, it is highly recommended to read the documentation
of the functions.
access() = public | protected | private
continuation() = {Tab::tab(), MatchSpec::ets:match_spec(), Limit::pos_integer(), Partition::non_neg_integer(), Continuation::ets_continuation()}
Defines the convention for the query functions:
Tab
: Table reference.MatchSpec
: The ets:match_spec()
.Limit
: Results limit.Partition
: Partition index.Continuation
: The ets:continuation()
.filename() = string() | binary() | atom()
info_item() = compressed | fixed | heir | keypos | memory | name | named_table | node | owner | protection | safe_fixed | size | stats | type | write_concurrency | read_concurrency | shards
info_tuple() = {compressed, boolean()} | {heir, pid() | none} | {keypos, pos_integer()} | {memory, non_neg_integer()} | {name, atom()} | {named_table, boolean()} | {node, node()} | {owner, pid()} | {protection, access()} | {size, non_neg_integer()} | {type, type()} | {write_concurrency, boolean()} | {read_concurrency, boolean()} | {shards, [atom()]}
option() = type() | access() | named_table | {keypos, pos_integer()} | {heir, pid(), HeirData::term()} | {heir, none} | tweaks() | shards_opt()
Create table options – used by new/2
.
shards_opt() = {partitions, pos_integer()} | {keyslot_fun, shards_meta:keyslot_fun()} | {restore, term(), term()}
Shards extended options.
tab() = atom() | ets:tid()
Table parameter
tabinfo_item() = {name, atom()} | {type, type()} | {protection, access()} | {named_table, boolean()} | {keypos, non_neg_integer()} | {size, non_neg_integer()} | {extended_info, [md5sum | object_count]} | {version, {Major::non_neg_integer(), Minor::non_neg_integer()}} | {shards, [atom()]}
tweaks() = {write_concurrency, boolean()} | {read_concurrency, boolean()} | compressed
ETS tweaks option
type() = set | ordered_set | bag | duplicate_bag
all/0 | Equivalent to ets:all(). |
delete/1 |
Equivalent to ets:delete/1 . |
delete/2 | Equivalent to delete(Tab, Key, shards_meta:get(Tab)). |
delete/3 |
Equivalent to ets:delete/2 . |
delete_all_objects/1 | Equivalent to delete_all_objects(Tab, shards_meta:get(Tab)). |
delete_all_objects/2 |
Equivalent to ets:delete_all_objects/1 . |
delete_object/2 | Equivalent to delete_object(Tab, Object, shards_meta:get(Tab)). |
delete_object/3 |
Equivalent to ets:delete_object/2 . |
file2tab/1 | Equivalent to file2tab(Filename, []). |
file2tab/2 |
Equivalent to shards:file2tab/2 . |
first/1 | Equivalent to first(Tab, shards_meta:get(Tab)). |
first/2 |
Equivalent to ets:first/1 . |
foldl/3 | Equivalent to foldl(Fun, Acc, Tab, shards_meta:get(Tab)). |
foldl/4 |
Equivalent to ets:foldl/3 . |
foldr/3 | Equivalent to foldr(Fun, Acc, Tab, shards_meta:get(Tab)). |
foldr/4 |
Equivalent to ets:foldr/3 . |
i/0 | Equivalent to ets:i(). |
info/1 | Similar to ets:info/1' but extra information about the partitioned table is added. |
info/2 |
Equivalent to ets:info/2 . |
insert/2 | Equivalent to insert(Tab, ObjOrObjs, shards_meta:get(Tab)). |
insert/3 |
Equivalent to ets:insert/2 . |
insert_new/2 | Equivalent to insert_new(Tab, ObjOrObjs, shards_meta:get(Tab)). |
insert_new/3 |
Equivalent to ets:insert_new/2 . |
is_compiled_ms/1 | Equivalent to ets:is_compiled_ms(Term). |
last/1 |
Equivalent to ets:last/1 . |
lookup/2 | Equivalent to lookup(Tab, Key, shards_meta:get(Tab)). |
lookup/3 |
Equivalent to ets:lookup/2 . |
lookup_element/3 | Equivalent to lookup_element(Tab, Key, Pos, shards_meta:get(Tab)). |
lookup_element/4 |
Equivalent to ets:lookup_element/3 . |
match/1 |
Equivalent to ets:match/1 . |
match/2 | Equivalent to match(Tab, Pattern, shards_meta:get(Tab)). |
match/3 |
If 3rd argument is pos_integer() this function behaves
like ets:match/3 , otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:match/2 . |
match/4 |
Equivalent to ets:match/3 . |
match_delete/2 | Equivalent to match_delete(Tab, Pattern, shards_meta:get(Tab)). |
match_delete/3 |
Equivalent to ets:match_delete/2 . |
match_object/1 |
Equivalent to ets:match_object/1 . |
match_object/2 | Equivalent to match_object(Tab, Pattern, shards_meta:get(Tab)). |
match_object/3 |
If 3rd argument is pos_integer() this function behaves
like ets:match_object/3 , otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:match_object/2 . |
match_object/4 |
Equivalent to ets:match_object/3 . |
match_spec_compile/1 | Equivalent to ets:match_spec_compile(MatchSpec). |
match_spec_run/2 | Equivalent to ets:match_spec_run(List, CompiledMatchSpec). |
member/2 | Equivalent to member(Tab, Key, shards_meta:get(Tab)). |
member/3 |
Equivalent to ets:member/2 . |
meta/1 | Returns the metadata associated with the given table Tab . |
new/2 |
This operation is equivalent to ets:new/2 , but when is called,
instead of create a single ETS table, it creates a new supervision
tree for the partitioned table. |
next/2 | Equivalent to next(Tab, Key1, shards_meta:get(Tab)). |
next/3 |
Equivalent to ets:next/2 . |
partition_owners/1 | Returns the partition PIDs associated with the given table TabOrPid . |
prev/2 |
Equivalent to ets:next/2 . |
rename/2 | Equivalent to rename(Tab, Name, shards_meta:get(Tab)). |
rename/3 |
Equivalent to ets:rename/2 . |
safe_fixtable/2 | Equivalent to safe_fixtable(Tab, Fix, shards_meta:get(Tab)). |
select/1 |
Equivalent to ets:select/1 . |
select/2 | Equivalent to select(Tab, MatchSpec, shards_meta:get(Tab)). |
select/3 |
If 3rd argument is pos_integer() this function behaves
like ets:select/3 , otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:select/2 . |
select/4 |
Equivalent to ets:select/3 . |
select_count/2 | Equivalent to select_count(Tab, MatchSpec, shards_meta:get(Tab)). |
select_count/3 |
Equivalent to ets:select_count/2 . |
select_delete/2 | Equivalent to select_delete(Tab, MatchSpec, shards_meta:get(Tab)). |
select_delete/3 |
Equivalent to ets:select_delete/2 . |
select_replace/2 | Equivalent to select_replace(Tab, MatchSpec, shards_meta:get(Tab)). |
select_replace/3 |
Equivalent to ets:select_replace/2 . |
select_reverse/1 |
Equivalent to ets:select_reverse/1 . |
select_reverse/2 | Equivalent to select_reverse(Tab, MatchSpec, shards_meta:get(Tab)). |
select_reverse/3 |
If 3rd argument is pos_integer() this function behaves
like ets:select_reverse/3 , otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:select_reverse/2 . |
select_reverse/4 |
Equivalent to ets:select_reverse/3 . |
setopts/2 | Equivalent to setopts(Tab, Opts, shards_meta:get(Tab)). |
setopts/3 |
Equivalent to ets:setopts/2 . |
tab2file/2 | Equivalent to tab2file(Tab, Filename, []). |
tab2file/3 |
Equivalent to ets:tab2file/3 . |
tab2list/1 | Equivalent to tab2list(Tab, shards_meta:get(Tab)). |
tab2list/2 |
Equivalent to ets:tab2list/1 . |
tabfile_info/1 |
Equivalent to ets:tabfile_info/1 . |
table/1 | Equivalent to table(Tab, []). |
table/2 | Equivalent to table(Tab, Options, shards_meta:get(Tab)). |
table/3 |
Similar to ets:table/2 , but it returns a list of qlc:query_handle() ;
one per partition. |
take/2 | Equivalent to take(Tab, Key, shards_meta:get(Tab)). |
take/3 |
Equivalent to ets:take/2 . |
test_ms/2 | Equivalent to ets:test_ms(Tuple, MatchSpec). |
update_counter/3 | Equivalent to update_counter(Tab, Key, UpdateOp, shards_meta:get(Tab)). |
update_counter/4 |
Equivalent to ets:update_counter/4 . |
update_counter/5 |
Equivalent to ets:update_counter/4 . |
update_element/3 | Equivalent to update_element(Tab, Key, ElementSpec, shards_meta:get(Tab)). |
update_element/4 |
Equivalent to ets:update_element/3 . |
all() -> any()
Equivalent to ets:all().
delete(Tab::tab()) -> true
Equivalent to ets:delete/1
.
See also: ets:delete/1.
delete(Tab, Key) -> any()
Equivalent to delete(Tab, Key, shards_meta:get(Tab)).
Equivalent to ets:delete/2
.
See also: ets:delete/2.
delete_all_objects(Tab) -> any()
Equivalent to delete_all_objects(Tab, shards_meta:get(Tab)).
Equivalent to ets:delete_all_objects/1
.
See also: ets:delete_all_objects/1.
delete_object(Tab, Object) -> any()
Equivalent to delete_object(Tab, Object, shards_meta:get(Tab)).
Equivalent to ets:delete_object/2
.
See also: ets:delete_object/2.
file2tab(Filename) -> any()
Equivalent to file2tab(Filename, []).
file2tab(Filename, Options) -> {ok, Tab} | {error, Reason}
Equivalent to shards:file2tab/2
. Moreover, it restores the
supervision tree for the shards
corresponding to the given
file, such as if they had been created using shards:new/2
.
See also: ets:file2tab/2.
first(Tab) -> any()
Equivalent to first(Tab, shards_meta:get(Tab)).
Equivalent to ets:first/1
.
See also: ets:first/1.
foldl(Fun, Acc, Tab) -> any()
Equivalent to foldl(Fun, Acc, Tab, shards_meta:get(Tab)).
foldl(Fun, Acc0, Tab, Meta) -> Acc1
Equivalent to ets:foldl/3
.
See also: ets:foldl/3.
foldr(Fun, Acc, Tab) -> any()
Equivalent to foldr(Fun, Acc, Tab, shards_meta:get(Tab)).
foldr(Fun, Acc0, Tab, Meta) -> Acc1
Equivalent to ets:foldr/3
.
See also: ets:foldr/3.
i() -> any()
Equivalent to ets:i().
Similar to ets:info/1' but extra information about the partitioned table is added.
{partitions, post_integer()}
- Number of partitions.
{keyslot_fun, shards_meta:keyslot_fun()}
- Functions used for compute
the keyslot.
{parallel, boolean()}
- Whether the parallel mode is enabled or not.
See also: ets:info/1.
Equivalent to ets:info/2
.
shards:info/1
.
See also: ets:info/2.
insert(Tab, ObjOrObjs) -> any()
Equivalent to insert(Tab, ObjOrObjs, shards_meta:get(Tab)).
insert(Tab, ObjOrObjs, Meta) -> true | no_return()
Equivalent to ets:insert/2
.
ets:insert/2
and produces the same result, there is a big difference due to the
nature of the sharding distribution model, IT IS NOT ATOMIC.
Therefore, if it fails by inserting an object at some partition,
previous inserts execution on other partitions are not rolled back,
but an error is raised instead.
See also: ets:insert/2.
insert_new(Tab, ObjOrObjs) -> any()
Equivalent to insert_new(Tab, ObjOrObjs, shards_meta:get(Tab)).
insert_new(Tab, ObjOrObjs, Meta) -> boolean()
Equivalent to ets:insert_new/2
.
Despite this functions behaves exactly the same as ets:insert_new/2
and produces the same result, there is a big difference due to the
nature of the sharding distribution model, IT IS NOT ATOMIC.
Opposite to shards:insert/2
, this function tries to roll-back
previous inserts execution on other partitions if it fails by
inserting an object at some partition, but there might be race
conditions during roll-back execution.
Example:
> shards:insert_new(mytab, {k1, 1}). true > shards:insert_new(mytab, {k1, 1}). false > shards:insert_new(mytab, [{k1, 1}, {k2, 2}]). false
See also: ets:insert_new/2.
is_compiled_ms(Term) -> any()
Equivalent to ets:is_compiled_ms(Term).
Equivalent to ets:last/1
.
See also: ets:last/1.
lookup(Tab, Key) -> any()
Equivalent to lookup(Tab, Key, shards_meta:get(Tab)).
Equivalent to ets:lookup/2
.
See also: ets:lookup/2.
lookup_element(Tab, Key, Pos) -> any()
Equivalent to lookup_element(Tab, Key, Pos, shards_meta:get(Tab)).
lookup_element(Tab, Key, Pos, Meta) -> Elem
Equivalent to ets:lookup_element/3
.
See also: ets:lookup_element/3.
match(Continuation) -> {[Match], continuation()} | '$end_of_table'
Equivalent to ets:match/1
.
See also: ets:match/1.
match(Tab, Pattern) -> any()
Equivalent to match(Tab, Pattern, shards_meta:get(Tab)).
match(Tab, Pattern, LimitOrMeta) -> {[Match], Cont} | '$end_of_table' | [Match]
If 3rd argument is pos_integer()
this function behaves
like ets:match/3
, otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:match/2
.
See also: ets:match/2, ets:match/3.
match(Tab, Pattern, Limit, Meta) -> {[Match], Cont} | '$end_of_table'
Equivalent to ets:match/3
.
See also: ets:match/3.
match_delete(Tab, Pattern) -> any()
Equivalent to match_delete(Tab, Pattern, shards_meta:get(Tab)).
match_delete(Tab, Pattern, Meta) -> true
Equivalent to ets:match_delete/2
.
See also: ets:match_delete/2.
Equivalent to ets:match_object/1
.
See also: ets:match_object/1.
match_object(Tab, Pattern) -> any()
Equivalent to match_object(Tab, Pattern, shards_meta:get(Tab)).
match_object(Tab, Pattern, LimitOrMeta) -> {[Object], Cont} | '$end_of_table' | [Object]
If 3rd argument is pos_integer()
this function behaves
like ets:match_object/3
, otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:match_object/2
.
See also: ets:match_object/3.
match_object(Tab, Pattern, Limit, Meta) -> {[Object], Cont} | '$end_of_table'
Equivalent to ets:match_object/3
.
See also: ets:match_object/3.
match_spec_compile(MatchSpec) -> any()
Equivalent to ets:match_spec_compile(MatchSpec).
match_spec_run(List, CompiledMatchSpec) -> any()
Equivalent to ets:match_spec_run(List, CompiledMatchSpec).
member(Tab, Key) -> any()
Equivalent to member(Tab, Key, shards_meta:get(Tab)).
Equivalent to ets:member/2
.
See also: ets:member/2.
meta(Tab::tab()) -> shards_meta:t()
Returns the metadata associated with the given table Tab
.
This operation is equivalent to ets:new/2
, but when is called,
instead of create a single ETS table, it creates a new supervision
tree for the partitioned table.
The supervision tree is composed by a main supervisor shards_partition_sup
and N
number of workers or partitions handled by shards_partition
(partition owner). Each worker creates an ETS table to handle the partition.
Also, the main supervisor shards_partition_sup
creates an ETS table to
keep the metadata for the partitioned table.
Returns an atom if the created table is a named table, otherwise,
a reference is returned. In the last case, the returned reference
is the one of the metadata table, which is the main entry-point
and it is owned by the main supervisor shards_partition_sup
.
ets:new/2
, this functions provides
the next options:
{partitions, N}
- Specifies the number of partitions for the sharded
table. By default, N = erlang:system_info(schedulers_online)
.
{keyslot_fun, F}
- Specifies the function used to compute the partition
where the action will be evaluated. Defaults to erlang:phash2/2
.
{parallel, P}
- Specifies whether shards
should work in parallel mode
or not, for the applicable functions, e.g.: select
, match
, etc. By
default is set to false
.
{parallel_timeout, T}
- When parallel
is set to true
, it specifies
the max timeout for a parallel execution. Defaults to infinity
.
Currently, only public
access is supported by shards:new/2
. Since a
partitioned table is started with its own supervision tree when created,
it is very tricky to provide private
or protected
access since there
are multiple partitions (or ETS tables) and they are owned by the
supervisor's children, and the supervisor along with their children
(or partitions) are managed by shards
under-the-hood; it is completely
transparent for the client.
> Tab = shards:new(tab1, []). #Ref<0.1541908042.2337144842.31535> > shards:new(tab2, [named_table]). tab2See also the "Partitioned Table" section at the module documentation for more information.
See also: ets:new/2.
next(Tab, Key1) -> any()
Equivalent to next(Tab, Key1, shards_meta:get(Tab)).
next(Tab, Key1, Meta) -> Key2 | '$end_of_table'
Equivalent to ets:next/2
.
See also: ets:next/2.
Returns the partition PIDs associated with the given table TabOrPid
.
Equivalent to ets:next/2
.
See also: ets:prev/2.
rename(Tab, Name) -> any()
Equivalent to rename(Tab, Name, shards_meta:get(Tab)).
Equivalent to ets:rename/2
.
See also: ets:rename/2.
safe_fixtable(Tab, Fix) -> any()
Equivalent to safe_fixtable(Tab, Fix, shards_meta:get(Tab)).
Equivalent to ets:select/1
.
See also: ets:select/1.
select(Tab, MatchSpec) -> any()
Equivalent to select(Tab, MatchSpec, shards_meta:get(Tab)).
select(Tab, MatchSpec, LimitOrMeta) -> {[Match], Cont} | '$end_of_table' | [Match]
If 3rd argument is pos_integer()
this function behaves
like ets:select/3
, otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:select/2
.
See also: ets:select/3.
select(Tab, MatchSpec, Limit, Meta) -> {[Match], Cont} | '$end_of_table'
Equivalent to ets:select/3
.
See also: ets:select/3.
select_count(Tab, MatchSpec) -> any()
Equivalent to select_count(Tab, MatchSpec, shards_meta:get(Tab)).
select_count(Tab, MatchSpec, Meta) -> NumMatched
Equivalent to ets:select_count/2
.
See also: ets:select_count/2.
select_delete(Tab, MatchSpec) -> any()
Equivalent to select_delete(Tab, MatchSpec, shards_meta:get(Tab)).
select_delete(Tab, MatchSpec, Meta) -> NumDeleted
Equivalent to ets:select_delete/2
.
See also: ets:select_delete/2.
select_replace(Tab, MatchSpec) -> any()
Equivalent to select_replace(Tab, MatchSpec, shards_meta:get(Tab)).
select_replace(Tab, MatchSpec, Meta) -> NumReplaced
Equivalent to ets:select_replace/2
.
See also: ets:select_replace/2.
Equivalent to ets:select_reverse/1
.
See also: ets:select_reverse/1.
select_reverse(Tab, MatchSpec) -> any()
Equivalent to select_reverse(Tab, MatchSpec, shards_meta:get(Tab)).
select_reverse(Tab, MatchSpec, LimitOrMeta) -> {[Match], Cont} | '$end_of_table' | [Match]
If 3rd argument is pos_integer()
this function behaves
like ets:select_reverse/3
, otherwise, the 3rd argument is
assumed as shards_meta:t()` and it behaves like
`ets:select_reverse/2
.
See also: ets:select_reverse/3.
select_reverse(Tab, MatchSpec, Limit, Meta) -> {[Match], Cont} | '$end_of_table'
Equivalent to ets:select_reverse/3
.
See also: ets:select_reverse/3.
setopts(Tab, Opts) -> any()
Equivalent to setopts(Tab, Opts, shards_meta:get(Tab)).
setopts(Tab, Opts, Meta) -> true
Equivalent to ets:setopts/2
.
true
if the function was applied successfully on each partition,
otherwise, false
is returned.
See also: ets:setopts/2.
tab2file(Tab, Filename) -> any()
Equivalent to tab2file(Tab, Filename, []).
tab2file(Tab, Filename, Options) -> ok | {error, Reason}
Equivalent to ets:tab2file/3
.
ets:tab2file/3
,
and also generates a master file with the given Filename
that holds
the information of the created partition files so that they can be
recovered by calling ets:file2tab/1,2
.
See also: ets:tab2file/3.
tab2list(Tab) -> any()
Equivalent to tab2list(Tab, shards_meta:get(Tab)).
Equivalent to ets:tab2list/1
.
See also: ets:tab2list/1.
tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason}
Equivalent to ets:tabfile_info/1
.
See also: ets:tabfile_info/1.
table(Tab) -> any()
Equivalent to table(Tab, []).
table(Tab, Options) -> any()
Equivalent to table(Tab, Options, shards_meta:get(Tab)).
table(Tab, Options, Meta) -> QueryHandle
Similar to ets:table/2
, but it returns a list of qlc:query_handle()
;
one per partition.
See also: ets:table/2.
take(Tab, Key) -> any()
Equivalent to take(Tab, Key, shards_meta:get(Tab)).
Equivalent to ets:take/2
.
See also: ets:take/2.
test_ms(Tuple, MatchSpec) -> any()
Equivalent to ets:test_ms(Tuple, MatchSpec).
update_counter(Tab, Key, UpdateOp) -> any()
Equivalent to update_counter(Tab, Key, UpdateOp, shards_meta:get(Tab)).
update_counter(Tab, Key, UpdateOp, DefaultOrMeta) -> Result | [Result]
Equivalent to ets:update_counter/4
.
shards_meta:t()
, it behaves like
ets:update_counter/3
.
See also: ets:update_counter/4.
update_counter(Tab, Key, UpdateOp, Default, Meta) -> Result | [Result]
Equivalent to ets:update_counter/4
.
See also: ets:update_counter/4.
update_element(Tab, Key, ElementSpec) -> any()
Equivalent to update_element(Tab, Key, ElementSpec, shards_meta:get(Tab)).
update_element(Tab, Key, ElementSpec, Meta) -> boolean()
Equivalent to ets:update_element/3
.
See also: ets:update_element/3.
Generated by EDoc