diff --git a/participantes/duke-bank/README.md b/participantes/duke-bank/README.md index 0f2402054..e4b625cc8 100644 --- a/participantes/duke-bank/README.md +++ b/participantes/duke-bank/README.md @@ -1,15 +1,148 @@ -# Submissão para Rinha de Backend, Segunda Edição: 2024/Q1 - Controle de Concorrência +# Rinha de Backend 2024 Q1: Duke Bank - Explorando paradigmas e I/O não bloqueante com Java 21 e Quarkus! ☕ -## Duke Bank by Fernando Comunello +# Stack +**Plataformas**: Java 21, Quarkus 3 & Vert.x
+**Banco de dados**: PostgreSQL
+**Load Balancer**: NGINX
+**Runtime:** GraalVM Mandrel ([micro-imagem nativa](https://hub.docker.com/layers/fercomunello/rinha-backend-duke-bank-2024q1/latest/images/sha256-d3ab6ecb3a80db5b803ff66b5777869c0c580d1bfe0f0099691b6e262c04451e?context=explore) ~ 32 MB)
-Submissão feita com: -- `Java 21` como linguagem de programação. -- `Quarkus` como framework/plataforma. -- `Vert.x & GraalVM` como runtime. -- `PostgreSQL` como banco de dados relacional. -- `NGINX` como Load Balancer. +# Objetivo +Meu principal objetivo nesta edição da rinha foi explorar conceitos de programação reativa e paradigmas de programação introduzidos na última versão do Java como DOP e paradigmas já consolidados como OOP & FP, assim construí um projeto com mais itens além dos que foram pedidos pela Rinha de Backend, pensei um pouco fora da caixa para deixar o projeto o mais legal e intuitivo possível. E claro, não pode faltar os shell scripts interativos e várias linhas de testes com PL/pgSQL como de praxe! :) -- [repositório da api](https://github.com/fercomunello/duke-bank-rb-2024) +# Aprendizados +"Reactive Programming is kinda bad, like it's bad but it's tolerable." - Gavin King, criador do Hibernate. -[@fercomunello](https://twitter.com/fercomunello) @ twitter +Não gastei muito tempo dividindo os recursos da melhor forma possível, mas um fato curioso que notei é que com I/O não bloqueante acabou valendo mais a pena liberar apenas **2 conexões** com o PostgreSQL, uma para cada event-loop. Talvez o objetivo maior da programação reativa seja atender o maior número de requisições sem "abrir o bico", como muitos dizem, e com um consumo baixo de recursos, diferentemente de estratégias tradicionais como pool de worker threads e threads virtuais que costumam usar muito mais memória e/ou CPU mas que podem entregar uma performance melhor, aqui me refiro a tempo de execução em ms e não ao throughput. +Então uma lição importante é a seguinte: Async/NIO não significa performance mas sim **eficiência**, entregar o resultado na medida do possível mas com muito **menos consumo de recursos**, um exemplo disso é na parte de consulta do extrato, em vez de rodar a consulta do saldo + a consulta das últimas 10 transações de forma sequencial, podemos combinar ambas operações em uma só usando [pipelining](https://quarkus.io/guides/reactive-sql-clients#pipelining) a nível de conexão e em paralelo sem alocar outra thread. + +E aqui vai mais uma lição importante para o debate de Imperative Programming vs Reactive Programming: **"Measure don't guess"**, é isso! xD + +Uma boa ideia para fins de comparação seria replicar o schema do banco de dados deste projeto usando um pool de worker threads e implementando uma camada de comunicação direta com o driver JDBC do Postgres, eu explico essa abordagem em detalhes em um [vídeo](https://www.youtube.com/watch?v=3eZeS31dnCg) no meu canal: + + + + +# Redes +- Youtube: [@ferjava](https://www.youtube.com/@ferjava) +- Github: [@fercomunello](https://github.com/fercomunello) +- Twitter: [@fercomunello](https://twitter.com/fercomunello) +- Site: [fercomunello.github.io](https://fercomunello.github.io/br) + +**Repositório**: [fercomunello/duke-bank-rb-2024](https://github.com/fercomunello/duke-bank-rb-2024/) + +# Overview +Eu tinha iniciado com uma stored function (procedure), mas optei por substituir ela por uma query que realiza todas as operações no mesmo statement, ou seja, lock pessimista, UPDATE e INSERT em uma roundtrip (aka "Go Horse" 🤔 haha). + +![Screenshot from 2024-03-11 01-33-32](https://github.com/fercomunello/rinha-de-backend-2024-q1/assets/66500627/c027e042-5830-4714-b77b-2495ae096d1e) +![Screenshot from 2024-03-11 01-34-41](https://github.com/fercomunello/rinha-de-backend-2024-q1/assets/66500627/26d25690-c1f9-42b5-9f0b-7c429ce28be8) + +# Gatling +![photo_2024-03-11_00-51-37](https://github.com/fercomunello/rinha-de-backend-2024-q1/assets/66500627/2352caf8-f567-47cc-b5d1-1fdeabe05448) + +# Estrutura do projeto +``` + +$$$$$$$\ $$\ $$$$$$$\ $$\ +$$ __$$\ $$ | $$ __$$\ $$ | +$$ | $$ |$$\ $$\ $$ | $$\ $$$$$$\ $$ | $$ | $$$$$$\ $$$$$$$\ $$ | $$\ +$$ | $$ |$$ | $$ |$$ | $$ |$$ __$$\ $$$$$$$\ | \____$$\ $$ __$$\ $$ | $$ | +$$ | $$ |$$ | $$ |$$$$$$ / $$$$$$$$ | $$ __$$\ $$$$$$$ |$$ | $$ |$$$$$$ / +$$ | $$ |$$ | $$ |$$ _$$< $$ ____| $$ | $$ |$$ __$$ |$$ | $$ |$$ _$$< +$$$$$$$ |\$$$$$$ |$$ | \$$\ \$$$$$$$\ $$$$$$$ |\$$$$$$$ |$$ | $$ |$$ | \$$\ +\_______/ \______/ \__| \__| \_______| \_______/ \_______|\__| \__|\__| \__| + +duke-bank + - postgres-local.sh -> script "hit and run" para recriar o schema do postgres na hora +├── src +│ ├── main +│ │ ├── java +│ │ │ └── com +│ │ │ └── github +│ │ │ └── bank +│ │ │ └── duke +│ │ │ ├── Bank.java +│ │ │ ├── BankSchema.java +│ │ │ ├── BankStartup.java +│ │ │ ├── BankWarmup.java +│ │ │ ├── business -> Regras da Rinha no padrão BCE +│ │ │ │ ├── boundary +│ │ │ │ │ ├── BankStatementRoute.java +│ │ │ │ │ └── BankTransactionRoute.java +│ │ │ │ ├── control +│ │ │ │ │ ├── BankProtocol.java +│ │ │ │ │ ├── BankStatement.java +│ │ │ │ │ ├── BankTransaction.java +│ │ │ │ │ ├── BankTransactionProcedure.java +│ │ │ │ │ └── BankTransactionResult.java +│ │ │ │ └── entity +│ │ │ │ ├── BankAccount.java +│ │ │ │ ├── BankTransactionState.java +│ │ │ │ └── TransactionType.java +│ │ │ ├── Logger.java +│ │ │ └── vertx -> Classes "wrappers" acopladas com o Vert.x + Mutiny, +| | | para abstrair as implementações técnicas. +│ │ │ ├── sql -> Abstração bem simples para o PgPool, aos moldes da rinha (nada além do necessário). +│ │ │ │ ├── Database.java +│ │ │ │ ├── Dialect.java +│ │ │ │ ├── function +│ │ │ │ │ ├── RowMapper.java +│ │ │ │ │ └── UniTask.java +│ │ │ │ ├── SqlConnection.java +│ │ │ │ ├── StoredFunction.java +│ │ │ │ └── vendor +│ │ │ │ └── Postgres.java +│ │ │ └── web -> Uma api/lib OOP-driven para validações e i18n aos moldes da rinha +│ │ │ ├── CachedEntry.java +│ │ │ ├── HttpStatus.java +│ │ │ ├── i18n +│ │ │ │ ├── MessageBundle.java +│ │ │ │ ├── Messages.java +│ │ │ │ └── ValidationMessages.java +│ │ │ ├── Media.java +│ │ │ ├── Response.java +│ │ │ ├── RoutingExchange.java +│ │ │ └── validation +│ │ │ ├── constraints +│ │ │ │ ├── NotNull.java +│ │ │ │ ├── PositiveInt.java +│ │ │ │ └── TextLengthMax.java +│ │ │ ├── failure +│ │ │ │ ├── Constraint.java +│ │ │ │ ├── InvalidEntry.java +│ │ │ │ ├── ValidationError.java +│ │ │ │ └── ValidationException.java +│ │ │ ├── Validation.java +│ │ │ ├── Validations.java +│ │ │ ├── ValidationState.java +│ │ │ └── ValidationStatus.java +│ │ └── resources +│ │ ├── application.properties +│ │ ├── i18n +│ │ │ ├── messages_en_US.properties +│ │ │ ├── messages_pt_BR.properties +│ │ │ ├── validations_en_US.properties +│ │ │ └── validations_pt_BR.properties +│ │ └── postgresql +│ │ ├── bank-schema.sql -> schema da rinha +│ │ ├── scripts +│ │ │ ├── bank-schema-dev.sql +│ │ │ └── bank-statements-test.sql -> a primeira consulta que fiz para o extrato bancário +│ │ └── tests +│ │ └── bank-transaction-test.sql -> sim, adicionei testes integrados dentro do banco de dados O.o +│ └── test +│ ├── java +│ │ └── com +│ │ └── github +│ │ └── bank +│ │ └── duke +│ │ ├── business +│ │ │ ├── BankTest.java +│ │ │ ├── boundary +│ │ │ │ ├── BankStatementRouteTest.java -> teste de integração da rota de extratos bancários +│ │ │ │ └── BankTransactionRouteTest.java -> teste de integração da rota de transações +│ │ │ ├── control +│ │ │ │ └── BankTransactionTest.java -> teste de integração para o processamento das transações +│ │ │ └── entity +│ │ │ └── BankAccountTest.java -> teste unitário para verificar o estado da conta bancária +``` diff --git a/participantes/duke-bank/docker-compose.yaml b/participantes/duke-bank/docker-compose.yaml index 85f454029..3f0e61e31 100644 --- a/participantes/duke-bank/docker-compose.yaml +++ b/participantes/duke-bank/docker-compose.yaml @@ -1,3 +1,4 @@ +version: '3.9' services: duke-bank-1: image: fercomunello/rinha-backend-duke-bank-2024q1:latest @@ -20,8 +21,8 @@ services: deploy: resources: limits: - cpus: 0.35 - memory: 150M + cpus: '0.35' + memory: '150M' duke-bank-2: image: fercomunello/rinha-backend-duke-bank-2024q1:latest @@ -44,8 +45,8 @@ services: deploy: resources: limits: - cpus: 0.35 - memory: 150M + cpus: '0.35' + memory: '150M' duke-bank-postgres: image: postgres:latest @@ -66,8 +67,8 @@ services: deploy: resources: limits: - cpus: 0.6 - memory: 125M + cpus: '0.6' + memory: '125M' duke-bank-nginx: image: nginx:latest @@ -80,5 +81,5 @@ services: deploy: resources: limits: - cpus: 0.2 - memory: 125M + cpus: '0.2' + memory: '125M' diff --git a/participantes/duke-bank/testada b/participantes/duke-bank/testada deleted file mode 100644 index b2a2cb6c5..000000000 --- a/participantes/duke-bank/testada +++ /dev/null @@ -1,2 +0,0 @@ -testada em Mon Mar 11 03:27:38 UTC 2024 -abra um PR removendo esse arquivo caso queira que sua API seja testada novamente