At the end of last week’s Erlang Thursday, I said we would continue looking at the behavior of the select
functions in the ets
module.
So before we do any experimentation, we setup our test ETS tables, and this time we will also create a table of type ordered_set
.
Fun = fun() -> receive after infinity -> ok end end. % #Fun<erl_eval.20.54118792> SomeProcess = spawn(Fun). % <0.52.0> TestOrderedSetTable = ets:new(ordered_set_table, [public, ordered_set]). % 16402 TestTable = ets:new(ets_table, [public]). % 20499 ets:give_away(TestTable, SomeProcess, []). % true ets:give_away(TestOrderedSetTable, SomeProcess, []). % true
Next we will load our test ETS table with some dummy data, leaving some gaps in the sequence, allowing us to fill those gaps in later.
[[ets:insert(TestTable, {X, X}) || X <- lists:seq(1, 30, 2)]]. % [[true,true,true,true,true,true,true,true,true,true,true, % true,true,true,true]] [[ets:insert(TestOrderedSetTable, {X, X}) || X <- lists:seq(1, 30, 2)]]. % [[true,true,true,true,true,true,true,true,true,true,true, % true,true,true,true]]
We then do a select to get all of the records from the table so we can see how the results are ordered for the different table types.
ets:select(TestTable, [{{'$1', '$2'}, [], [{{'$1', '$2'}}]}]). % [{15,15}, % {25,25}, % {13,13}, % {21,21}, % {11,11}, % {1,1}, % {23,23}, % {7,7}, % {3,3}, % {9,9}, % {19,19}, % {29,29}, % {27,27}, % {17,17}, % {5,5}] ets:select(TestOrderedSetTable, [{{'$1', '$2'}, [], [{{'$1', '$2'}}]}]). % [{1,1}, % {3,3}, % {5,5}, % {7,7}, % {9,9}, % {11,11}, % {13,13}, % {15,15}, % {17,17}, % {19,19}, % {21,21}, % {23,23}, % {25,25}, % {27,27}, % {29,29}]
The ets
module also has a function ets:select_reverse, so let’s take a quick stop and see what that does for our ETS tables.
ets:select_reverse(TestTable, [{{'$1', '$2'}, [], [{{'$1', '$2'}}]}]). % [{15,15}, % {25,25}, % {13,13}, % {21,21}, % {11,11}, % {1,1}, % {23,23}, % {7,7}, % {3,3}, % {9,9}, % {19,19}, % {29,29}, % {27,27}, % {17,17}, % {5,5}] ets:select_reverse(TestOrderedSetTable, [{{'$1', '$2'}, [], [{{'$1', '$2'}}]}]). % [{29,29}, % {27,27}, % {25,25}, % {23,23}, % {21,21}, % {19,19}, % {17,17}, % {15,15}, % {13,13}, % {11,11}, % {9,9}, % {7,7}, % {5,5}, % {3,3}, % {1,1}]
If we look at the results of ets:select/2
and ets:select_reverse/2
, we see that for TestTable
we get the same result, and for TestOrderedSetTable
we get the results in a reverse order, which is what the documentation for ets:select_reverse/2
states. Which makes sense if you think about it,
With that brief diversion out of the way, lets run our same match_spec()
s from above, but limit the results to 5
records so we get a continuation back.
{Result, Continuation} = ets:select(TestTable, [{{'$1', '$2'}, [], [{{'$1', '$2'}}]}], 5). % {[{19,19},{29,29},{27,27},{17,17},{5,5}], % {20499,214,5,<<>>,[],0}} {OrdSetResult, OrdSetContinuation} = ets:select(TestOrderedSetTable, [{{'$1', '$2'}, [], [{{'$1', '$2'}}]}], 5). % {[{1,1},{3,3},{5,5},{7,7},{9,9}],{16402,9,[],5,<<>>,[],0,0}}
And with those continuations, we will see what the next results we would fetch would be.
ets:select(Continuation). % {[{1,1},{23,23},{7,7},{3,3},{9,9}],{20499,111,5,<<>>,[],0}} ets:select(OrdSetContinuation). % {[{11,11},{13,13},{15,15},{17,17},{19,19}], % {16402,19,[],5,<<>>,[],0,0}}
Remember those “gaps” we left in our sequence of numbers we used to create tuples?
Time to “fill in” those gaps of the sequence to see what happens if we fetch with our existing continuation as data gets populated concurrently.
[[ets:insert(TestOrderedSetTable, {X, X}) || X <- lists:seq(2, 30, 2)]]. % [[true,true,true,true,true,true,true,true,true,true,true, % true,true,true,true]] [[ets:insert(TestTable, {X, X}) || X <- lists:seq(2, 30, 2)]]. % [[true,true,true,true,true,true,true,true,true,true,true, % true,true,true,true]]
Now we re-run our ets:select/1
functions with the same continuations as before.
ets:select(Continuation). % {[{12,12},{7,7},{3,3},{10,10},{9,9}], % {20499,224,5,<<>>,[],0}} ets:select(OrdSetContinuation). % {[{10,10},{11,11},{12,12},{13,13},{14,14}], % {16402,14,[],5,<<>>,[],0,0}}
If we compare that to before we can see the we now have even number items in the list. For our TestTable
if we look above at the Continuation
value itself, we ahve the continuation point as 214
, since that is the only thing that has changed between that continuation and the resulting continuations from calling ets:select(Continuation).
. So with just a number it is hard to infer just how we might expect the continuation to change.
The OrdSetContinuation
on the other hand, has a 9
as its second element in the tuple, after the ETS table id of 16402
. This also happens to be the key of the last tuple in the result set, which matches up with the 19
and 14
in the other continuations. So in the case of the ordered set, we can infer that as part of the continuation for an ETS table of type ordered_set
, the continuation tells us the specific key of the last record that was returned, and we continue from that record regardless of any concurrent inserts that may have taken place.
Next time we will take a look at ets:is_compiled_ms/1 and how match specs might play in with continuations based off reading the documentation about ets:is_compiled_ms/1
.
–Proctor