Updata ets with fun

I wonder why there is no such function in the ETS interface:

Blockquote
-spec update_with(Table, Key, Fun) → term() when
Table :: table(),
Key :: term(),
Fun :: fun((Key :: term() , Value :: term() | [term()]) → ignore | {ok, NewValue, Return} | {error, Return}).
update_with(Table, Key, Fun) →
erlang:nif_error(undef).

When multiple processes operate on the same ETS table, I usually need to start one or more write processes to perform ETS modifications to ensure data consistency. I guess if I have a function like the one above, I don’t need to start the write process anymore。

1 Like

It is not technically possible in erts to run Erlang code and hold ETS locks that would guarantee atomicity. It is the same reason why you cannot write an Erlang fun as a match specification.

The functionality you describe could probably be written using some clever use of ets:lookup and ets:select_replace to create a compare and exchange. Something like:

update_with(Table, Key, Fun) ->
   [Object] = ets:lookup(Table, Key),
   {ok, NewObject} = Fun(Object),
   %% Create a match spec that first checks that object is the same, and then replaces it
   %% with the new object.
   case ets:select_replace(Table, FancyMatchSpec) of
      0 -> update_with(Table, Key, Fun);
      N -> N
   end.
1 Like

this is a good way to do, But this way code is not universal, different logic, different FancyMatchSpec.
how about if use global locks, such as:

Blockquote
global:set_lock(xxx)
[Object] = ets:lookup(Table, Key),
{ok, NewObject} = Fun(Object),
ets:insert(Table, NewObject)
global:del_lock(xxx)

1 Like

It should be possible to write a matchspec that would work for any logic in the update_with fun.

If you are going to use locks, then you may as well use mnesia IMO.

1 Like

The documentation of ets:select_replace actually contains an example of a generic compare-and-swap.

[Old] = ets:lookup(T, Key),
New = update_object(Old),
Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),

which would make

FancyMatchSpec = [{Object, [], [{const, NewObject}]}]
2 Likes

yes,it’s write some to write a matchspec that would work for any logic in the update_with fun. thanks for you.

1 Like

I initially thought that different Fun updates had different FancyMatchSpec matching different field values, because I thought matching the entire OldObject would be performance unfriendly. I’ll test that out later

1 Like