Safe Insert , to avoid the 2GB table limit on mnesia.

main
Uriel Fanelli 2025-05-11 09:08:09 +02:00
parent 0cf55ca2ec
commit ea942d56f0
13 changed files with 101 additions and 34 deletions

2
.gitignore vendored
View File

@ -19,7 +19,7 @@ erl_crash.dump
*.d
# File temporanei di Mnesia (database locale)
Mnesia.*
mnesia.*
# File temporanei di editor
*~

View File

@ -0,0 +1,4 @@
%% db_safe_insert.hrl
-record(global_message, {id, activity, timestamp}).
-record(ap_users, {id, name, email, created_at}).

BIN
mnesia/DECISION_TAB.LOG Normal file

Binary file not shown.

BIN
mnesia/LATEST.LOG Normal file

Binary file not shown.

BIN
mnesia/ap_users.DAT Normal file

Binary file not shown.

BIN
mnesia/global_message.DAT Normal file

Binary file not shown.

BIN
mnesia/schema.DAT Normal file

Binary file not shown.

View File

@ -10,8 +10,8 @@
start(_Type, _Args) ->
%% creiamo un utente di test, solo per vedere se mnesia va
user_db:init(),
timeline_db:init(),
db_organization:setup(),
db_organization:init(),
users_local_check:check_ENV(), %% controlla le variabili d'ambiente
%% aggiungiamo un paio di utenti di esempio.

47
src/db_organization.erl Normal file
View File

@ -0,0 +1,47 @@
-module(db_organization).
-export([setup/0, init/0]).
%% 1. Imposta la directory di Mnesia leggendo la variabile di ambiente
setup() ->
case os:getenv("AP_DB_DIR") of
false ->
io:format("Errore: la variabile di ambiente AP_DB_DIR non è settata!~n"),
erlang:halt(1);
Dir ->
ok = application:set_env(mnesia, dir, Dir),
ok
end.
%% 2. Crea lo schema, avvia Mnesia e crea le tabelle (solo se non esistono già)
init() ->
%% Crea lo schema solo se non esiste già
case mnesia:create_schema([node()]) of
{error, {already_exists, _}} -> ok;
_ -> ok
end,
%% Avvia Mnesia
mnesia:start(),
%% Crea tutte le tabelle
create_tables().
%% 3. Crea tutte le tabelle che servono
create_tables() ->
create_table(ap_users, [id, name, email, created_at]),
create_table(global_message, [id, activity, timestamp]).
%% Funzione helper per creare una tabella solo se non esiste già
create_table(Name, Attributes) ->
case mnesia:create_table(Name, [
{disc_only_copies, [node()]},
{attributes, Attributes}
]) of
{atomic, ok} ->
io:format("Tabella ~p creata.~n", [Name]),
ok;
{aborted, {already_exists, _}} ->
io:format("Tabella ~p già esistente.~n", [Name]),
ok;
Other ->
io:format("Errore nella creazione della tabella ~p: ~p~n", [Name, Other]),
erlang:halt(1)
end.

32
src/db_safe_insert.erl Normal file
View File

@ -0,0 +1,32 @@
-module(db_safe_insert).
-include_lib("include/db_safe_insert.hrl").
-export([safe_insert/2]).
safe_insert(Tab, Record) ->
case mnesia:transaction(fun() -> mnesia:write(Record) end) of
{atomic, ok} ->
ok;
{aborted, {file_size, _}} ->
cleanup_and_retry(Tab, Record);
{aborted, {no_disk_space, _}} ->
cleanup_and_retry(Tab, Record);
Other ->
Other
end.
cleanup_and_retry(Tab, Record) ->
remove_oldest(Tab, 0.2),
mnesia:transaction(fun() -> mnesia:write(Record) end).
remove_oldest(Tab, Fraction) ->
Records = mnesia:dirty_match_object(make_pattern(Tab)),
N = trunc(length(Records) * Fraction),
Sorted = lists:sort(fun(A, B) -> get_timestamp(A) =< get_timestamp(B) end, Records),
ToDelete = lists:sublist(Sorted, N),
lists:foreach(fun(Rec) -> mnesia:dirty_delete_object(Rec) end, ToDelete).
make_pattern(global_message) -> #global_message{id='_', activity='_', timestamp='_'};
make_pattern(ap_users) -> #ap_users{id='_', name='_', email='_', created_at='_'}. %% <-- PUNTO QUI DIOCANE!
get_timestamp(#global_message{timestamp=T}) -> T;
get_timestamp(#ap_users{created_at=T}) -> T. %% <-- PUNTO SOLO ALLA FINE, DIOCANE!

View File

@ -1,24 +1,15 @@
%% timeline_db.erl
-module(timeline_db).
-export([init/0, add_message/1, all_messages/0]).
-export([add_message/1, all_messages/0]).
-record(global_message, {id, activity, timestamp}).
init() ->
%% Crea la tabella Mnesia se non esiste
mnesia:create_table(global_message, [
{attributes, record_info(fields, global_message)},
{disc_copies, [node()]}
]).
add_message(Activity) ->
%% Crea un ID unico e un timestamp per il messaggio
Id = erlang:unique_integer([monotonic, positive]),
Timestamp = erlang:system_time(microsecond),
F = fun() ->
mnesia:write(#global_message{id=Id, activity=Activity, timestamp=Timestamp})
end,
mnesia:transaction(F).
Record = #global_message{id=Id, activity=Activity, timestamp=Timestamp},
db_safe_insert:safe_insert(global_message, Record).
all_messages() ->
F = fun() ->

View File

@ -1,25 +1,16 @@
-module(user_db).
-export([init/0, add_user/2, get_user/1, all_users/0]).
-include("db_safe_insert.hrl").
-export([add_user/2, get_user/1, all_users/0]).
-record(user, {username, data}).
add_user(Name, Email) ->
Id = erlang:unique_integer([monotonic, positive]),
Timestamp = erlang:system_time(microsecond),
Record = #ap_users{id=Id, name=Name, email=Email, created_at=Timestamp},
db_safe_insert:safe_insert(ap_users, Record).
init() ->
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(user, [
{attributes, record_info(fields, user)},
{disc_copies, [node()]}
]).
add_user(Username, Data) ->
get_user(Id) ->
F = fun() ->
mnesia:write(#user{username=Username, data=Data})
end,
mnesia:transaction(F).
get_user(Username) ->
F = fun() ->
case mnesia:read({user, Username}) of
case mnesia:read({ap_users, Id}) of
[User] -> {ok, User};
[] -> not_found
end
@ -29,7 +20,8 @@ get_user(Username) ->
all_users() ->
F = fun() ->
mnesia:match_object(#user{username='_', data='_'})
mnesia:match_object(#ap_users{id='_', name='_', email='_', created_at='_'})
end,
{atomic, Users} = mnesia:transaction(F),
Users.

View File

@ -1,6 +1,7 @@
#!/bin/bash
export AP_FQDN="localhost"
export AP_DB_DIR="./mnesia"
rebar3 compile