LFE Friday - ETS selects, continuations and inserts
This week's LFE Friday was translated with permission from the Erlang Thursday series by Steven Proctor. This week's translator: Robert Virding.
At the end of last week's LFE Friday, we 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
.
> (set fun (lambda () (receive (after 'infinity 'ok))))
#Fun<lfe_eval.23.76437303>
> (set some-process (spawn fun))
<0.36.0>
> (set set-table (ets:new 'set-table '(public set)))
8207
> (set ordered-set-table (ets:new 'ordered-set-table '(public ordered_set)))
12304
> (ets:give_away set-table some-process ())
true
> (ets:give_away ordered-set-table some-process ())
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.
> (list-comp ((<- x (lists:seq 1 30 2))) (ets:insert set-table (tuple x x)))
(true true true true true true true true true true true true true true true)
> (list-comp ((<- x (lists:seq 1 30 2))) (ets:insert ordered-set-table (tuple x x)))
(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 set-table '(#(#($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 ordered-set-table '(#(#($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 set-table '(#(#($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 ordered-set-table '(#(#($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 set-table
we get the same result, and for ordered-set-table
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.
> (set `#(,set-result ,set-continuation) (ets:select set-table '(#(#($1 $2) () (#(#($1 $2))))) 5))
#((#(19 19) #(29 29) #(27 27) #(17 17) #(5 5))
#(8207 214 5 #"" () 0))
> (set `#(,ord-set-result ,ord-set-continuation) (ets:select ordered-set-table '(#(#($1 $2) () (#(#($1 $2))))) 5))
#((#(1 1) #(3 3) #(5 5) #(7 7) #(9 9))
#(12304 9 () 5 #"" () 0 0))
And with those continuations, we will see what the next results we would fetch would be.
> (ets:select set-continuation)
#((#(1 1) #(23 23) #(7 7) #(3 3) #(9 9))
#(8207 111 5 #"" () 0))
> (ets:select ord-set-continuation)
#((#(11 11) #(13 13) #(15 15) #(17 17) #(19 19))
#(12304 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.
> (list-comp ((<- x (lists:seq 2 30 2))) (ets:insert set-table (tuple x x)))
(true true true true true true true true true true true true true true true)
> (list-comp ((<- x (lists:seq 2 30 2))) (ets:insert ordered-set-table (tuple x x)))
(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 set-continuation)
#((#(12 12) #(7 7) #(3 3) #(10 10) #(9 9))
#(8207 224 5 #"" () 0))
> (ets:select ord-set-continuation)
#((#(10 10) #(11 11) #(12 12) #(13 13) #(14 14))
#(12304 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 set-table
if we look above at the set-continuation
value itself, we have 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 set-continuation)
. So with just a number it is hard to infer just how we might expect the continuation to change.
The ord-set-continuation
on the other hand, has a 9
as its second element in the tuple, after the ETS table id of 12304
. 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
.