Playing With Fire

Exploring the web one Elixir at a time

Using eWebmachine to create a link shortener (part 4)

In the previous posts, we have been using the temporary Erlang Term Storage or ETS to store the data. As this is an in-memory store, any data that is stored is lost whenever the application is stopped.

In this post, we will look to change this and make the data persist between application restarts. To do this we will be using Disc-backed Erlang Term Storage - DETS. This writes the data to a specified file on the disc. For this application using DETS is fine as the data requirements are very small. Should this site take off, then I would recommend using Mnesia.

All changes will be made in lib/shorten_url_srv.ex as this is where the data is stored.

The first thing that we’ll do is add a new Module attribute as a constant towards the top of the module:

  @dets_file "shorten_urls.dets"

The next thing that will need to happen is setting up the file, this is handled in the init/1 function.

  def init(_) do
    {:ok, ref} = :dets.open_file(@tab, [file: @dets_file, type: :bag])
    {:ok, %St{next: 0} }

Here we ensure that the file is present, if not then the DETS engine creates the file for us.

Next up, we need to change the handlers:

  def handle_call({:get_url, id}, _from, state) do
    {:ok, ref} = :dets.open_file(@tab)
    reply = case :dets.lookup(ref, "#{id}") do
      [] -> {:error, :not_found}
      [{_id, url}] -> {:ok, url}
    {:reply, reply, state}

  def handle_call({:put_url, url}, _from, %St{next: n} = state) do
    id = ShortenUrlSrv.b36_encode(n)
    {:ok, ref} = :dets.open_file(@tab)
    :dets.insert(ref, {id, url})
    {:reply, {:ok, id}, %St{next: n+1}}

Lastly, we need to fix the do_lookup/1 function:

  defp do_lookup(id) do
    {:ok, ref} = :dets.open_file(@tab)
    record = case :dets.lookup(ref, id) do
      [] -> {:error, :not_found}
      [record] -> record

To test, run the application:

  $ iex -S mix

and add in some test data for now:

  iex> ShortenUrlSrv.put_url('')
  [{"0", ''}]

Navigate to http://localhost:18080/latest and you should see the link displayed.

Now restart the application, and then reload the page. It should still display the stored link.

Job Done