Adding Persistence to Slackir

So now we have a basic chat app but once our session is closed all of our messages are lost. If we were able to create, read, update and delete from a database we would be able to maintain a history of messages or have persistence across our application.

Creating our Database

First, we’ll create and run a migration. Migrations are pieces of code that create or change database columns. In Phoenix, we can invoke mix phoenix.gen.model from the terminal to generate our model, then run the migration:

$ mix phoenix.gen.model Message messages name:string message:string
$ mix ecto.migrate

Creating our messages in the Database

Next, we need to save the messages as they come in. If you are not familiar with programming this is create in the CRUD app methodology. In web/channels/random_channel.ex, we can add a call to do it, so the handle_in method looks like this:

def handle_in("shout", message, socket) do
  Slackir.Message.changeset(%Slackir.Message{}, message)
  |> Slackir.Repo.insert
  broadcast! socket, "shout", message
  {:noreply, socket}
end

Refactor

The above code is alright, but if we want to scale our application we will start to have a problem as all the saves to the database will slow down the request cycle and keep us from handling the traffic efficiently. Elixir has some built in tools for dealing with exactly this situation. Specifically, Kernel.spawn, which takes a module, a method, and arguments to pass to the method. So we’ll update our handler, and add a method:

def handle_in("shout", message, socket) do
  spawn(__MODULE__, :save_message, [message])
  broadcast! socket, "shout", message
  {:noreply, socket}
end

def save_message(message) do
  Slackir.Message.changeset(%Slackir.Message{}, message)
  |> Slackir.Repo.insert
end

What this does is spawn an elixir process to do the save, outside of the request cycle. Since we don’t need to see the results of the save to broadcast the message, there’s no reason to wait for it.