mirror of
https://github.com/UzixLS/migresia.git
synced 2025-07-19 07:11:28 +03:00
Clean up and reorganize the code + store timestamps as integers + extract the function to create new migrations to a separate script
This commit is contained in:
163
src/migresia.erl
163
src/migresia.erl
@ -1,4 +1,4 @@
|
||||
%% Copyright (c) 2013, Grzegorz Junka
|
||||
%% Copyright (c) 2015, Grzegorz Junka
|
||||
%% All rights reserved.
|
||||
%%
|
||||
%% Redistribution and use in source and binary forms, with or without
|
||||
@ -24,102 +24,91 @@
|
||||
|
||||
-module(migresia).
|
||||
|
||||
-export([create_new_migration/2, check/1, migrate/1, rollback/2, list_nodes/0, ensure_started/1]).
|
||||
-export([start_all_mnesia/0,
|
||||
ensure_started/1,
|
||||
check/1,
|
||||
migrate/1,
|
||||
rollback/2,
|
||||
list_nodes/0]).
|
||||
|
||||
-define(TABLE, schema_migrations).
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
-spec create_new_migration(atom(), string()) -> any().
|
||||
create_new_migration(App, Description) ->
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(),
|
||||
Filename = lists:flatten(io_lib:format("~w~2.2.0w~2.2.0w~2.2.0w~2.2.0w~2.2.0w_", [Year, Month, Day, Hour, Minute, Second]) ++ Description),
|
||||
Path = migresia_migrations:get_priv_dir(App),
|
||||
FullPathAndExtension = filename:join(Path, Filename ++ ".erl"),
|
||||
io:format("Creating new migration: ~p~n", [FullPathAndExtension]),
|
||||
ok = filelib:ensure_dir(<<Path/binary, <<"/">>/binary>>),
|
||||
ok = file:write_file(FullPathAndExtension, io_lib:fwrite("-module(~p).~n-behavior(db_migration).~n-export([up/0, down/0]). ~n~nup() -> ok.~n~ndown() -> throw(<<\"Downgraders not implemented.\">>).", [list_to_atom(Filename)])),
|
||||
io:format("Migration written.~n~n").
|
||||
|
||||
-spec check(atom()) -> ok | {error, any()}.
|
||||
check(App) ->
|
||||
case start_mnesia(false) of
|
||||
ok ->
|
||||
Loaded = migresia_migrations:list_unapplied_ups(App),
|
||||
if Loaded == [] ->
|
||||
[];
|
||||
true ->
|
||||
[ [X||{X,_} <- Loaded] ]
|
||||
end;
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
-spec migrate(atom()) -> ok | {error, any()}.
|
||||
migrate(App) ->
|
||||
case start_mnesia(true) of
|
||||
ok ->
|
||||
case migresia_migrations:ensure_schema_table_exists() of
|
||||
ok ->
|
||||
io:format("Waiting for tables (max timeout 2 minutes)...", []),
|
||||
ok = mnesia:wait_for_tables(mnesia:system_info(tables), 120000),
|
||||
rpc:multicall(migresia_migrations, list_unapplied_ups, [App]), %Basically just to ensure everybody has loaded all the migrations, which is necessary in distributed Mnesia transforms.
|
||||
Loaded = migresia_migrations:list_unapplied_ups(App),
|
||||
lists:foreach(fun execute_up/1, Loaded);
|
||||
{error, Error} -> Error
|
||||
end;
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
-spec rollback(atom(), integer()) -> ok | {error, any()}.
|
||||
rollback(App, Time) ->
|
||||
case start_mnesia(true) of
|
||||
ok ->
|
||||
case migresia_migrations:ensure_schema_table_exists() of
|
||||
ok ->
|
||||
io:format("Waiting for tables (max timeout 2 minutes)...", []),
|
||||
ok = mnesia:wait_for_tables(mnesia:system_info(tables), 120000),
|
||||
rpc:multicall(migresia_migrations, list_all_ups, [App]), %Basically just to ensure everybody has loaded all the migrations, which is necessary in distributed Mnesia transforms.
|
||||
ToRollBack = lists:reverse([X || X<- migresia_migrations:list_all_ups(App), binary_to_integer(element(2, X)) > Time]),
|
||||
lists:foreach(fun execute_down/1, ToRollBack);
|
||||
{error, Error} -> Error
|
||||
end;
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
execute_up({Module, Short}) ->
|
||||
io:format("Executing up in ~s...~n", [Module]),
|
||||
Module:up(),
|
||||
mnesia:dirty_write(schema_migrations, {schema_migrations, Short, true}),
|
||||
io:format(" => done~n", []).
|
||||
|
||||
execute_down({Module, Time}) ->
|
||||
io:format("Executing down in ~s...~n", [Module]),
|
||||
Module:down(),
|
||||
mnesia:dirty_delete(schema_migrations, Time),
|
||||
io:format(" => done~n", []).
|
||||
|
||||
-spec start_mnesia(boolean()) -> ok | {error, any()}.
|
||||
start_mnesia(RemoteToo) ->
|
||||
-spec start_all_mnesia() -> ok | {error, any()}.
|
||||
start_all_mnesia() ->
|
||||
io:format("Starting Mnesia...~n", []),
|
||||
case ensure_started(mnesia) of
|
||||
Other when Other /= ok -> io:format(" => Error:~p~n", [Other]), Other;
|
||||
ok ->
|
||||
case RemoteToo of
|
||||
false -> ok;
|
||||
true -> {ResultList, BadNodes} = rpc:multicall(list_nodes(), migresia, ensure_started, [mnesia]),
|
||||
BadStatuses = [X || X <- ResultList, X /= ok],
|
||||
if BadNodes /= [] -> io:format(" => Error:~p~n", [not_all_nodes_running]), {error, not_all_nodes_running};
|
||||
BadStatuses /= [] -> io:format(" => Error:~p~n", [BadStatuses]), {error, BadStatuses};
|
||||
true -> io:format(" => started~n", []), ok
|
||||
end
|
||||
end
|
||||
ok ->
|
||||
ensure_started_on_remotes(list_nodes());
|
||||
Err ->
|
||||
io:format(" => Error:~p~n", [Err]),
|
||||
Err
|
||||
end.
|
||||
|
||||
list_nodes() ->
|
||||
mnesia:table_info(schema, disc_copies).
|
||||
|
||||
ensure_started_on_remotes(Nodes) ->
|
||||
io:format("Ensuring Mnesia is running on nodes:~n~p~n", [Nodes]),
|
||||
{ResL, BadNodes} = rpc:multicall(Nodes, migresia, ensure_started, [mnesia]),
|
||||
handle_err([X || X <- ResL, X /= ok], BadNodes).
|
||||
|
||||
handle_err([], []) ->
|
||||
io:format(" => started~n", []),
|
||||
ok;
|
||||
handle_err(Results, Bad) ->
|
||||
if Results /= [] -> io:format(" => Error, received: ~p~n", [Results]) end,
|
||||
if Bad /= [] -> io:format(" => Error, bad nodes: ~p~n", [Bad]) end,
|
||||
{error, mnesia_not_started}.
|
||||
|
||||
-spec ensure_started(atom()) -> ok | {error, any()}.
|
||||
ensure_started(App) ->
|
||||
case application:start(App) of
|
||||
ok -> ok;
|
||||
{error,{already_started,App}} -> ok;
|
||||
A -> A
|
||||
end.
|
||||
{error, {already_started, App}} -> ok;
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
-spec check(atom()) -> ok | {error, any()}.
|
||||
check(App) ->
|
||||
case migresia_migrations:list_unapplied_ups(App) of
|
||||
[] -> [];
|
||||
{error, _} = Err -> Err;
|
||||
Loaded -> [X || {X, _} <- Loaded]
|
||||
end.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
-spec migrate(atom()) -> ok | {error, any()}.
|
||||
migrate(App) ->
|
||||
migrate(migresia_migrations:init_migrations(), App).
|
||||
|
||||
migrate(ok, App) ->
|
||||
io:format("Waiting for tables (max timeout 2 minutes)...", []),
|
||||
ok = mnesia:wait_for_tables(mnesia:system_info(tables), 120000),
|
||||
Loaded = migresia_migrations:list_unapplied_ups(App),
|
||||
%% Load the transform function on all nodes, see:
|
||||
%% http://toddhalfpenny.com/2012/05/21/possible-erlang-bad-transform-function-solution/
|
||||
rpc:multicall(nodes(), migresia_migrations, list_unapplied_ups, [App]),
|
||||
lists:foreach(fun migresia_migrations:execute_up/1, Loaded);
|
||||
migrate({error, _} = Err, _) ->
|
||||
Err.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
-spec rollback(atom(), integer()) -> ok | {error, any()}.
|
||||
rollback(App, Time) ->
|
||||
rollback(migresia_migrations:init_migrations(), App, Time).
|
||||
|
||||
rollback(ok, App, Time) ->
|
||||
io:format("Waiting for tables (max timeout 2 minutes)...", []),
|
||||
ok = mnesia:wait_for_tables(mnesia:system_info(tables), 120000),
|
||||
Ups = migresia_migrations:list_all_ups(App),
|
||||
%% Load the transform function on all nodes, see:
|
||||
%% http://toddhalfpenny.com/2012/05/21/possible-erlang-bad-transform-function-solution/
|
||||
rpc:multicall(nodes(), migresia_migrations, list_all_ups, [App]),
|
||||
ToRollBack = lists:reverse([X || {_, Ts, _} = X <- Ups, Ts > Time]),
|
||||
lists:foreach(fun migresia_migrations:execute_down/1, ToRollBack);
|
||||
rollback({error, _} = Err, _App, _Time) ->
|
||||
Err.
|
||||
|
Reference in New Issue
Block a user