defmodule Que.Persistence.Mnesia do
use Que.Persistence
@moduledoc """
Mnesia adapter to persist `Que.Job`s
This module defines a Database and a Job Table in Mnesia to keep
track of all Jobs, along with Mnesia transaction methods that
provide an easy way to find, insert, update or destroy Jobs from
the Database.
It implements all callbacks defined in `Que.Persistence`, along
with some `Mnesia` specific ones. You should read the
`Que.Persistence` documentation if you just want to interact
with the Jobs in database.
## Persisting to Disk
`Que` works out of the box without any configuration needed, but
initially all Jobs are not persisted to disk, and are only in
memory. You'll need to create the Mnesia Schema on disk and create
the Job Database for this to work.
Que provides ways that automatically do this for you. First,
specify the location where you want your Mnesia database to be
created in your `config.exs` file. It's highly recommended that you
specify your `Mix.env` in the path to keep development, test and
production databases separate.
```
config :mnesia, dir: 'mnesia/\#{Mix.env}/\#{node()}'
# Notice the single quotes
```
You can now either run the `Mix.Tasks.Que.Setup` mix task or call
`Que.Persistence.Mnesia.setup!/0` to create the Schema, Database
and Tables.
"""
@config [db: DB, table: Jobs]
@db Module.concat(__MODULE__, @config[:db])
@store Module.concat(@db, @config[:table])
@doc """
Creates the Mnesia Database for `Que` on disk
This creates the Schema, Database and Tables for
Que Jobs on disk for the specified erlang nodes so
Jobs are persisted across application restarts.
Calling this momentarily stops the `:mnesia`
application so you should make sure it's not being
used when you do.
If no argument is provided, the database is created
for the current node.
## On Production
For a compiled release (`Distillery` or `Exrm`),
start the application in console mode or connect a
shell to the running release and simply call the
method:
```
$ bin/my_app remote_console
iex(my_app@127.0.0.1)1> Que.Persistence.Mnesia.setup!
:ok
```
You can alternatively provide a list of nodes for
which you would like to create the schema:
```
iex(my_app@host_x)1> nodes = [node() | Node.list]
[:my_app@host_x, :my_app@host_y, :my_app@host_z]
iex(my_app@node_x)2> Que.Persistence.Mnesia.setup!(nodes)
:ok
```
"""
@spec setup!(nodes :: list(node)) :: :ok
def setup!(nodes \\ [node()]) do
# Create the DB directory (if custom path given)
if path = Application.get_env(:mnesia, :dir) do
:ok = File.mkdir_p!(path)
end
# Create the Schema
Memento.stop
Memento.Schema.create(nodes)
Memento.start
# Create the DB with Disk Copies
# TODO:
# Use Memento.Table.wait when it gets implemented
# @db.create!(disk: nodes)
# @db.wait(15000)
Memento.Table.create!(@store, disc_copies: nodes)
end
@doc "Returns the Mnesia configuration for Que"
@spec __config__ :: Keyword.t
def __config__ do
[
database: @db,
table: @store,
path: Path.expand(Application.get_env(:mnesia, :dir))
]
end
# Callbacks in Table Definition
# -----------------------------
# Make sures that the DB exists (either
# in memory or on disk)
@doc false
def initialize do
Memento.Table.create(@store)
end
@doc false
defdelegate all, to: @store, as: :all_jobs
@doc false
defdelegate all(worker), to: @store, as: :all_jobs
@doc false
defdelegate completed, to: @store, as: :completed_jobs
@doc false
defdelegate completed(worker), to: @store, as: :completed_jobs
@doc false
defdelegate incomplete, to: @store, as: :incomplete_jobs
@doc false
defdelegate incomplete(worker), to: @store, as: :incomplete_jobs
@doc false
defdelegate failed, to: @store, as: :failed_jobs
@doc false
defdelegate failed(worker), to: @store, as: :failed_jobs
@doc false
defdelegate find(job), to: @store, as: :find_job
@doc false
defdelegate insert(job), to: @store, as: :create_job
@doc false
defdelegate update(job), to: @store, as: :update_job
@doc false
defdelegate destroy(job), to: @store, as: :delete_job
end