Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tentando otimizar queries, mas falha no teste de concorrencia ainda #3

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions apps/db/src/db_data.erl
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
-module(db_data).

-export([obtem_cliente/1]).
-export([obtem_saldo/1]).
-export([salva_transacao/2]).
-export([obtem_transacoes/1]).
-export([salva_transacao/2]).

obtem_cliente(Id) ->
pgo:query("select * from clientes where id=$1", [Id]).

obtem_saldo(Id) ->
pgo:query("select valor from saldos where cliente_id=$1", [Id]).
pgo:query("select id, saldo, limite from clientes where id=$1", [Id]).

obtem_transacoes(Id) ->
pgo:query("select valor, tipo, descricao, realizada_em from transacoes where cliente_id=$1 order by id desc limit 10", [Id]).

salva_transacao(Id, {Valor, <<"c">>, Descricao}) ->
pgo:transaction(fun () ->
pgo:query("insert into transacoes (id, cliente_id, valor, tipo, descricao, realizada_em) values (default, $1, $2, 'c', $3, now())", [Id, Valor, Descricao]),
pgo:query("update saldos set valor = valor + $2 where cliente_id=$1 returning valor", [Id, Valor])
pgo:transaction(fun() ->
pgo:query("select transacao_credito($1, 'c', $2, $3)", [Id, Valor, Descricao])
end);

salva_transacao(Id, {Valor, <<"d">>, Descricao}) ->
pgo:transaction(fun () ->
pgo:query("insert into transacoes (id, cliente_id, valor, tipo, descricao, realizada_em) values (default, $1, $2, 'd', $3, now())", [Id, Valor, Descricao]),
pgo:query("update saldos set valor = valor + $2 where cliente_id=$1 returning valor", [Id, -1*Valor])
pgo:transaction(fun() ->
pgo:query("select transacao_debito($1, 'd', $2, $3)", [Id, Valor, Descricao])
end).
22 changes: 6 additions & 16 deletions apps/rinha/src/extrato.erl
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,15 @@ manda_extrato(Req, Saldo, Transacoes) ->
#{<<"content-type">> => <<"application/json">>},
formt_resp(Saldo, Transacoes), Req)}.

obtem_saldo(Id) ->
case db_data:obtem_saldo(Id) of
#{rows := [{Saldo}]} -> {saldo, Saldo};
_ -> banco
end.

init(Req0=#{method := <<"GET">>}, State) ->
case db_data:obtem_cliente(cowboy_req:binding(id, Req0)) of
#{rows := []} -> {ok, cowboy_req:reply(404, Req0), State};
#{rows := [{Id, _, Limite}]} ->
case obtem_saldo(Id) of
{saldo, Saldo} ->
case db_data:obtem_transacoes(Id) of
#{rows := Transacoes} -> manda_extrato(Req0, {Limite, Saldo}, Transacoes);
_ -> {ok, cowboy_req:reply(500, Req0), State}
end;
banco -> {ok, cowboy_req:reply(500, Req0), State}
#{rows := [{Id, Saldo, Limite}]} ->
case db_data:obtem_transacoes(Id) of
#{rows := Transacoes} -> manda_extrato(Req0, {Limite, Saldo}, Transacoes);
_ -> {ok, cowboy_req:reply(500, Req0), State}
end;
_ -> {ok, cowboy_req:reply(404, Req0), State}
#{rows := []} -> {ok, cowboy_req:reply(404, Req0), State};
_ -> {ok, cowboy_req:reply(500, Req0), State}
end;


Expand Down
6 changes: 4 additions & 2 deletions apps/rinha/src/rinha_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
-export([start/2, stop/1]).

start(_Type, _Args) ->
application:ensure_all_started(pgo),
application:ensure_all_started(pgo, db),
{ok, Porta} = application:get_env(rinha, porta),
io:format("~p~n", [Porta]),
Dispatch = cowboy_router:compile([
%% {HostMatchm list({PatchMatch, Constraints, Handler, InitialState})}
{'_', [
Expand All @@ -22,7 +24,7 @@ start(_Type, _Args) ->
]),

persistent_term:put(erlang_rinher_dispatch, Dispatch),
{ok, _} = cowboy:start_clear(erlang_rinher, [{port, 6969}],
{ok, _} = cowboy:start_clear(erlang_rinher, [Porta],
#{env => #{dispatch => {persistent_term, erlang_rinher_dispatch}}}
),

Expand Down
55 changes: 16 additions & 39 deletions apps/rinha/src/transacoes.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,56 +43,33 @@ valida_transacao(Info) ->
Transacao -> valida_transacao(Transacao)
end.

salva_transacao(Id, Limite, {_, <<"c">>, _} = T) ->
case db_data:salva_transacao(Id, T) of
#{rows := [{Saldo}]} -> {salvo, {Saldo, Limite}};
_ -> banco
end;

salva_transacao(Id, Limite, {Valor, <<"d">>, _} = T) ->
case db_data:obtem_saldo(Id) of
#{rows := [{Saldo}]} ->
if (Saldo - Valor) >= (-1 * Limite) ->
case db_data:salva_transacao(Id, T) of
#{rows := [{NovoSaldo}]} -> {salvo, {NovoSaldo, Limite}};
_ -> banco
end;
true -> inconsistente
end;
_ -> banco
salva_transacao(Id, Transacao) ->
case catch db_data:salva_transacao(Id, Transacao) of
#{rows := [{{Id, <<"nao_encontrado">>}}]} -> nao_encontrou;
#{rows := [{{Id, <<"inconsistente">>}}]} -> inconsistente;
#{rows := [{{Saldo, Limite}}]} -> {salvo, {Saldo, Limite}};
{error, none_available} -> banco
end.

faz_transacao({Id, _, Limite}, [Body]) ->
faz_transacao(Id, [Body]) ->
case Body of
{Info, true} ->
case valida_transacao(Info) of
{transacao, T} -> salva_transacao(Id, Limite, T);
{transacao, T} -> salva_transacao(Id, T);
invalido -> invalido
end;
{_, false} -> vazio
end.

init(Req0=#{method := <<"POST">>, headers := #{<<"content-type">> := <<"application/json">>}}, State) ->
case db_data:obtem_cliente(cowboy_req:binding(id, Req0)) of
#{rows := [Cliente]} ->
{ok, Body, Req} = cowboy_req:read_urlencoded_body(Req0),
case faz_transacao(Cliente, Body) of
{salvo, Json} ->
{ok, cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>}, formt_resp(Json), Req)};
vazio ->
%%io:format("req vazio~n"),
{ok, cowboy_req:reply(422, Req), State};
invalido ->
io:format("transacao invalida~n"),
{ok, cowboy_req:reply(422, Req), State};
inconsistente ->
%%io:format("transacao inconsistente~n"),
{ok, cowboy_req:reply(422, Req), State};
banco ->
{ok, cowboy_req:reply(500, Req), State}
end;
#{rows := []} -> {ok, cowboy_req:reply(404, Req0), State};
_ -> {ok, cowboy_req:reply(404, Req0), State}
{ok, Body, Req} = cowboy_req:read_urlencoded_body(Req0),
case faz_transacao(cowboy_req:binding(id, Req), Body) of
{salvo, Json} -> {ok, cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>}, formt_resp(Json), Req)};
nao_encontrou -> {ok, cowboy_req:reply(404, Req0), State};
inconsistente -> {ok, cowboy_req:reply(422, Req0), State};
invalido -> {ok, cowboy_req:reply(422, Req0), State};
vazio -> {ok, cowboy_req:reply(422, Req0), State};
banco -> {ok, cowboy_req:reply(500, Req0), State}
end;

init(Req, State) ->
Expand Down
6 changes: 4 additions & 2 deletions config/sys.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[
{rinha, []},
{rinha, [
{porta, {port, 6969}}
]},
{db, [{pools, [{default, #{pool_size => 8,
host => "db",
host => "db",
database => "rinha",
user => "admin",
password => "123"}}]}]}
Expand Down
88 changes: 70 additions & 18 deletions init.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CREATE TABLE clientes (
id SERIAL PRIMARY KEY,
nome VARCHAR(50) NOT NULL,
saldo INTEGER NOT NULL,
limite INTEGER NOT NULL
);

Expand All @@ -15,24 +15,76 @@ CREATE TABLE transacoes (
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
);

CREATE TABLE saldos (
id SERIAL PRIMARY KEY,
cliente_id INTEGER NOT NULL,
valor INTEGER NOT NULL,
CONSTRAINT fk_clientes_saldos_id
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
);

DO $$
BEGIN
INSERT INTO clientes (nome, limite)
VALUES ('Cleiton Rasta', 1000 * 100),
('Alonzo Church', 800 * 100),
('Marcos Valle', 10000 * 100),
('Vinicius de Moraes', 100000 * 100),
('Jose Raul Capablanca', 5000 * 100);

INSERT INTO saldos (cliente_id, valor)
SELECT id, 0 FROM clientes;
INSERT INTO clientes (saldo, limite)
VALUES (0, 1000 * 100),
(0, 800 * 100),
(0, 10000 * 100),
(0, 100000 * 100),
(0, 5000 * 100);
END;
$$;

CREATE FUNCTION transacao_credito(
id_cliente integer,
tipo_salva char(1),
valor_salva integer,
descricao_salva varchar(10)
) RETURNS RECORD AS $$
DECLARE
cliente clientes%ROWTYPE;
retorno RECORD;
BEGIN
SELECT * INTO cliente FROM clientes WHERE id = id_cliente;

IF NOT FOUND THEN
RETORNO := (id_cliente, 'nao_encontrado');
RETURN retorno;
END IF;

UPDATE clientes
SET saldo = saldo + valor_salva
WHERE id = id_cliente
RETURNING saldo, limite INTO retorno;

INSERT INTO transacoes (id, cliente_id, valor, tipo, descricao, realizada_em)
VALUES (DEFAULT, id_cliente, valor_salva, tipo_salva, descricao_salva, NOW());

RETURN retorno;
END;
$$ LANGUAGE plpgsql;

CREATE FUNCTION transacao_debito(
id_cliente integer,
tipo_salva char(1),
valor_salva integer,
descricao_salva varchar(10)
) RETURNS RECORD AS $$
DECLARE
cliente clientes%ROWTYPE;
retorno RECORD;
BEGIN
SELECT * INTO cliente FROM clientes WHERE id = id_cliente;

IF NOT FOUND THEN
RETORNO := (id_cliente, 'nao_encontrado');
RETURN retorno;
END IF;

IF (cliente.saldo - valor_salva) >= (-1 * cliente.limite) THEN
UPDATE clientes
SET saldo = saldo + valor_salva
WHERE id = id_cliente
RETURNING saldo, limite INTO retorno;
ELSE
RETORNO := (id_cliente, 'inconsistente');
RETURN retorno;
END IF;

INSERT INTO transacoes (id, cliente_id, valor, tipo, descricao, realizada_em)
VALUES (DEFAULT, id_cliente, valor_salva, tipo_salva, descricao_salva, NOW());

RETURN retorno;
END;
$$ LANGUAGE plpgsql;