Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
beardedeagle committed Jul 18, 2018
1 parent 379ac57 commit 55da0ed
Show file tree
Hide file tree
Showing 11 changed files with 491 additions and 10 deletions.
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
inputs: [".formatter.exs", "mix.exs", "config/.credo.exs", "{config,lib,test}/**/*.{ex,exs}"],
line_length: 98
]
22 changes: 14 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/_build
/cover
/deps
/doc
/.fetch
erl_crash.dump
*.ez
_build
.DS_Store
.elixir_ls
.fetch
.idea
.sonarlint
.vscode
*.beam
/config/*.secret.exs
*.ez
cover
deps
doc
erl_crash.dump
mnesiac-*.tar
mnesiac.iml
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
# mnesiac
mnesia autoclustering made easy!
# Mnesiac

Autoclustering for mnesia made easy!

Docs can be found at [https://hexdocs.pm/mnesiac](https://hexdocs.pm/mnesiac).

## Installation

Simply add `mnesiac` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:mnesiac, "~> 0.1.0"}
]
end
```
78 changes: 78 additions & 0 deletions config/.credo.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
%{
configs: [
%{
name: "default",
files: %{
included: ["lib/", "src/", "test/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
requires: [],
strict: true,
color: true,
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
{Credo.Check.Design.AliasUsage, priority: :low},
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
{Credo.Check.Design.TagTODO, exit_status: 2},
{Credo.Check.Design.TagFIXME},
{Credo.Check.Readability.AliasOrder},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 98},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.PipeChainStart,
excluded_argument_types: [:atom, :binary, :fn, :keyword], excluded_functions: []},
{Credo.Check.Refactor.UnlessWithElse},
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
{Credo.Check.Readability.Specs, false}
]
}
]
}
1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use Mix.Config
207 changes: 207 additions & 0 deletions lib/mnesiac.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
defmodule Mnesiac do
@moduledoc """
Mnesiac Manager
"""
require Logger

use GenServer

def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end

@impl true
def init(args) do
GenServer.cast(__MODULE__, {:init, args})

{:ok, []}
end

@impl true
def handle_cast({:init, nodes}, _state) do
init_mnesia(nodes)

{:noreply, []}
end

@doc """
Start Mnesia with/without a cluster
"""
def init_mnesia(nodes) do
nodes =
Enum.filter(List.delete(Node.list(), Node.self()), fn node ->
node in List.delete(List.flatten(nodes), Node.self())
end)

case nodes do
[h | _t] -> join_cluster(h)
[] -> start()
end
end

@doc """
Start Mnesia with/without a cluster. Test helper.
"""
def init_mnesia(nodes, :test) do
case List.delete(List.flatten(nodes), Node.self()) do
[h | _t] -> join_cluster(h)
[] -> start()
end
end

@doc """
Start Mnesia alone
"""
def start do
with :ok <- ensure_dir_exists(),
:ok <- start_server(),
:ok <- Store.copy_schema(Node.self()),
:ok <- Store.init_tables(),
:ok <- Store.ensure_tables_loaded() do
:ok
else
{:error, error} -> {:error, error}
end
end

@doc """
Join to a Mnesia cluster
"""
def join_cluster(cluster_node) do
with :ok <- ensure_stopped(),
:ok <- Store.delete_schema(),
:ok <- ensure_started(),
:ok <- connect(cluster_node),
:ok <- Store.copy_schema(Node.self()),
:ok <- Store.copy_tables(),
:ok <- Store.ensure_tables_loaded() do
:ok
else
{:error, reason} ->
Logger.log(:debug, fn -> inspect(reason) end)
{:error, reason}
end
end

@doc """
Cluster status
"""
def cluster_status do
running = :mnesia.system_info(:running_db_nodes)
stopped = :mnesia.system_info(:db_nodes) -- running

if stopped == [] do
[{:running_nodes, running}]
else
[{:running_nodes, running}, {:stopped_nodes, stopped}]
end
end

@doc """
Cluster with a node
"""
def connect(cluster_node) do
case :mnesia.change_config(:extra_db_nodes, [cluster_node]) do
{:ok, [_cluster_node]} -> :ok
{:ok, []} -> {:error, {:failed_to_connect_node, cluster_node}}
reason -> {:error, reason}
end
end

@doc """
Running Mnesia nodes
"""
def running_nodes do
:mnesia.system_info(:running_db_nodes)
end

@doc """
Is node in Mnesia cluster?
"""
def node_in_cluster?(cluster_node) do
Enum.member?(:mnesia.system_info(:db_nodes), cluster_node)
end

@doc """
Is running Mnesia node?
"""
def running_db_node?(cluster_node) do
Enum.member?(running_nodes(), cluster_node)
end

defp ensure_started do
with :ok <- start_server(),
:ok <- wait_for(:start) do
:ok
else
{:error, reason} -> {:error, reason}
end
end

defp ensure_stopped do
with :stopped <- stop_server(),
:ok <- wait_for(:stop) do
:ok
else
{:error, reason} -> {:error, reason}
end
end

defp ensure_dir_exists do
mnesia_dir = :mnesia.system_info(:directory)

with false <- File.exists?(mnesia_dir),
:ok <- File.mkdir(mnesia_dir) do
:ok
else
true ->
:ok

{:error, reason} ->
Logger.log(:debug, fn -> inspect(reason) end)
{:error, reason}
end
end

defp start_server do
:mnesia.start()
end

defp stop_server do
:mnesia.stop()
end

defp wait_for(:start) do
case :mnesia.system_info(:is_running) do
:yes ->
:ok

:no ->
{:error, :mnesia_unexpectedly_stopped}

:stopping ->
{:error, :mnesia_unexpectedly_stopping}

:starting ->
Process.sleep(1_000)
wait_for(:start)
end
end

defp wait_for(:stop) do
case :mnesia.system_info(:is_running) do
:no ->
:ok

:yes ->
{:error, :mnesia_unexpectedly_running}

:starting ->
{:error, :mnesia_unexpectedly_starting}

:stopping ->
Process.sleep(1_000)
wait_for(:stop)
end
end
end
Loading

0 comments on commit 55da0ed

Please sign in to comment.