4
votes

I use elang ets table as a simple cache. I want to use a process to scan the table and remove expired elements (multiple).

with ets:foldl

expire_table_example() ->
Tab = ets:new(ets_tab, [named_table, set]),
ets:insert(Tab, [{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5},{f,7}]),
Tab1 = ets:foldl(fun({Key, Val}, Acc) ->
if
   (Val > 3) -> [{Key, Val} | Acc];
   true -> Acc
 end
end, Tab, Tab),
io:format("end ~p ~n", [Tab1]).

I got

[{f,7},{e,5},{d,4}|ets_tab] %% the ets_tab is NOT expected.

How Can I fix This?

Any other API's would do this better?

3
The reason you're getting the atom ets_tab in your list is that you're putting it there by using Tab as the second argument to ets:foldl/3, which is the initial value of the fold accumulator. You should be using [] for the initial value instead. - Steve Vinoski
Actually, I want to return an empty ets table there. if I use [], it could be an empty list. right? - user3644708
If you want to empty the whole table, just use ets:delete_all_objects/1. - Steve Vinoski
@SteveVinoski. Thanks. it make sense. - user3644708

3 Answers

6
votes

You can't use ets table as accumulator.

For your purpose you can use ets:select_delete/2:

1> Tab = ets:new(ets_tab, [named_table, set]).
ets_tab
2> ets:insert(Tab, [{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5},{f,7}]).
true
3> ets:select_delete(Tab, [{{'$1','$2'},[{'=<','$2',3}],[true]}]).
3
4> ets:tab2list(Tab).
[{f,7},{e,5},{d,4}]

Or you can use ets:tab2list/1 to get list of all values, filter them and after that re-insert to table:

1> Tab = ets:new(ets_tab, [named_table, set]).
ets_tab
2> ets:insert(Tab, [{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5},{f,7}]).
true
3> L = ets:tab2list(Tab).
[{f,7},{e,5},{d,4},{c,3},{b,2},{a,1}]
4> L2 = lists:filter(fun({Key,Val}) -> Val > 3 end, L).
[{f,7},{e,5},{d,4}]
5> ets:delete_all_objects(Tab).
true
6> ets:insert(Tab, L2).
true
7> ets:tab2list(Tab).
[{f,7},{e,5},{d,4}]
0
votes

Another way it can be done is by using list comprehensions.

1> Tab = ets:new(ets_tab, [named_table, set]).
ets_tab   
2> ets:insert(Tab, [{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5},{f,7}]).    
true    
3> [{X,Y} || {X,Y} <- ets:tab2list(Tab), Y>3].    
[{f,7},{e,5},{d,4}]
0
votes

You may find that deleting huge numbers of objects periodically may result in undesirable latency spikes. There is interesting project which have cache segments as separate ets tables and deletes outdated objects by dropping whole ets, maybe you will find it interesting as well

https://github.com/fogfish/cache