I haven’t seen one. Now that you mention it, I do agree that using lists in Erlang/Elixir (I’ll use “erts” for shorthand) would be more ergonomic. As you say, the FDB Tuple layer encourages the practice of “building up” a fdb-tuple, which is awkward with an erts-tuple, since they’re fixed length data structures.
Also, pattern matching on erts-lists is even more powerful than erts-tuples, so it sounds like very good idea indeed!
{"user", user_id, _, _, _, _, _, _}
= :erlfdb_tuple.unpack(key) # ugh!
["user", user_id | _]
= :erlfdb_tuple_v2.unpack(key) # yay!
I have a bad track record of predicting reality in micro-benchmarks like this, but I wonder if using erts-lists would actually be faster in the best case than erts-tuples due to the conversion you mention. Either way, it’s likely to be negligible compared to I/O
.
I can see why this would be useful for your database server, likewise for Bedrock, since you’re more likely to want to do key comparisons with both the binary representation and the data structure. On the client, this has never been a pain point for me, though, since the server always returns keys in the correct sort order, and I can’t remember ever needing to compare them myself. That being said, I’m a big fan of design simplicity, so
from me!
On the question of whether or not an ERTS-friendly Tuple V2 would be a good idea to store in actual FDB – I’m a maybe on this. On the one hand, FDB is supposed to allow the client to be entirely in control of the Layer.
On the other hand, GetMappedRange exists. As you’re aware, FDB implements this feature with assumptions about the key and value encoding, specifically that they are Tuple encoded. If you were to only change the type codes, it would probably still work because FDB would have no reason to decode the types. AFAIK their only assumption is regarding the boundaries between fields. But GetMappedRange is so finicky that I worry there is some dragon lurking there.
Keeping in mind how important records were (and still are) in Erlang, the tuple ordering makes sense. Having fixed-length tuples together means that different versions of your ets and mnesia records would be nicely grouped. I don’t know if this is the reason, but it seems like a real benefit.
(This also illustrates why records can be tricky
)
iex(1)> :ets.new(:tab, [:named_table, {:keypos, 2}, :bag])
:tab
iex(2)> :ets.insert(:tab, {:user, "1"})
true
iex(3)> :ets.insert(:tab, {:user, "2"})
true
iex(4)> :ets.insert(:tab, {:user, "1", "Alice"})
true
iex(5)> :ets.insert(:tab, {:user, "2", "Bob"})
true
iex(6)> :ets.tab2list(:tab) |> Enum.sort()
[{:user, "1"}, {:user, "2"}, {:user, "1", "Alice"}, {:user, "2", "Bob"}]
Wdym? If you’re going for a stable sort order in both binary and erts, you must, no?