Leaderboard
Leaderboard (rank table) implementation using ETS tables.
Installation
If available in Hex, the package can be installed
by adding leaderboard
to your list of dependencies in mix.exs
:
def deps do
[{:leaderboard, "~> 0.2"}]
end
Usage
First off, the leaderboard GenServer
process must be started. Typically, it’s
started as a part of the supervision tree:
worker(Leaderboard, [Leaderboard.Test])
It requires table_name
argument which is the name of the leaderboard. It
must be an atom. The leaderboard tables shouldn’t be started dynamically
as the leaderboard names are atoms and they shouldn’t be generated
dynamically.
The leaderboard’s API has functions for inserting, updating and deleting records. These are write functions and are serialised. Also, there are functions for reading records in specific order and/or with limited number of returned values:
Leaderboard.insert(Leaderboard.Test, 30, "foo")
Leaderboard.insert(Leaderboard.Test, 5, "bar")
Leaderboard.insert(Leaderboard.Test, 19, "baz")
# update "bar" to 10
Leaderboard.insert(Leaderboard.Test, 10, "bar")
# lookup the score of "bar"
Leaderboard.lookup(Leaderboard.Test, "bar")
#=> 10
# select top 2 records in ascending order
Leaderboard.select(Leaderboard.Test, :ascend, 2)
#=> [{10, "bar"}, {19, "baz"}]
# match all records that have score > 10 in descending order and return
# their keys
match_spec = [{{{:"$1",:"$2"}}, [{:">", :"$1", 10}], [:"$2"]}]
Leaderboard.match(Leaderboard.Test, match_spec, :descend)
#=> ["foo", "baz"]
# delete "foo" record
Leaderboard.delete(Leaderboard.Test, "foo")
Implementation
The leaderboard is composed of a GenServer
process and two ETS tables. The
ETS key_value
is of type :set
:
key | value |
---|---|
key | score |
The second ETS table called score_table
is of type :ordered_set
.
It stores only keys without any values:
key | value |
---|---|
{score, key} | - |
When a new record is inserted into the leaderboard, the record is inserted
into both tables. All the writes are serialised via the GenServer
process.
The ETS tables are :protected
, so only the GenServer
process that owns
them can write. All the other processes are allowed just to read. Read
operations are not serialised so they can be done in concurrent manner.
Benchmark
TODO