Based on Google RPC and Protocol Buffer - a multi threaded Asynchronous Application that Allows Deposit, Withdrawal and Get Balance for the users.Project consists of a Wallet server and Wallet client.The wallet server will keep track of a users monetary balance in the system. The client will emulate users depositing and withdrawing funds.
Function | Input | Output | Error |
---|---|---|---|
Deposit | UserID,Amount,Currency | Success/Failure | Unknown Currency , User Unknown |
Withdraw | UserID,Amount,Currency | Success/Failure | Unknown Currency , User Unknown |
Get Balance | User id | The balance of the users account for each currency | User Unknown |
Currency (Values are EUR, USD, GBP)
- Spring BOOT based Client Which makes Concurrent Transaction Request via GRPC Stub over HTTP2 to BPWS.
- The wallet client will emulate a number of users concurrently using the wallet.
- The wallet client must connect to the wallet server over gRPC.
- The client eliminating users doing rounds (a sequence of events).
- Whenever a round is needed it is picked at random from the following list of available rounds
- Deposit 100 USD
- Withdraw 200 USD
- Deposit 100 EUR
- Get Balance
- Withdraw 100 USD
- Get Balance
- Withdraw 100 USD
- Withdraw 100 GBP
- Deposit 300 GPB
- Withdraw 100 GBP
- Withdraw 100 GBP
- Withdraw 100 GBP
- Withdraw 100 GBP
- Withdraw 100 GBP
- Get Balance
- Deposit 100 USD
- Deposit 100 USD
- Withdraw 100 USD
- Deposit 100 USD
- Get Balance
- Withdraw 200 USD
- Get Balance
- users (number of concurrent users emulated)
- concurrent_threads_per_user (number of concurrent requests a user will make)
- rounds_per_thread (number of rounds each thread is executing)
K Users
Should Run Concurrently.- Each Kth User Can make
N Requests
. - Each Nth Request can Spawn
M Concurrent Operations(Rounds)
. - Each Round will have
DEPOSIT
,WITHDRAW
,BALANCE
. - Total Number of threads will K * M * N (* Average 7 Transactions per round).
Records balance in user Wallet.
Expose API for Depositing Money, Withdrawing and Getting Balance in different currencies.
Has proto file(.proto), Generated Stubs and Domains shared by BPWC and BPWS.
-
Server Handles Transaction on FCFS. Ideally in a solution like this the sequence of transactions must be maintained viz OOS.
-
About "Make sure the client exits when all rounds has been executed."
- BPWC exposes SWAGGER API for Testing.
- CLI Command: Not Implemented
-
Technologies
- Java 8.
- gRPC
- MySQL and H2
- Gradle
- JUnit and Test Containers
- SLF4J
- Docker
- Hibernate
- Spring and Spring Boot.
- Swagger
- Springs Transaction.
-
Junits Coverage of > 80% is OOS(Out of Scope).
-
The docker containers should run via Compose/Kubernetes. OOS.
-
The Client/Server Doesn't retry failed transactions. OOS
-
The user ID is Taken from Number of Users param (userID:1 for numberOfuser=1,userID:1,userID:2 for numberOfuser=2).
-
Database schema has been kept Simple with One table.
-
The actual applicable schema is included in
Future Aspiration Section
with SQL and Schema Diagram. -
The Service Response/Request has been kept same for
RAPID (Rapid Application Development in Pro-typing)
Otherwise it should be different for each transaction type example /docs/wallet.proto. -
Implements limited caching
spring-caching
although not bench-marked, it will play a crucial role with expanding user base. -
bp
is anapplication prefix
.
If local/remote instance of MY SQL is running nothing else to do.
Replace the IP/PORT/SCHEMA in `application.yaml` in BPWS.
Script: start-bp-wallet-mysql-docker.bat
Run above in root folder it takes 5-10 Minutes to come up.
Execute following command to get the IP of docker-machine bash#docker-machine -ip
Make Sure MYSQL or Any DB is UP and its properties are configured in `application.yaml`
Script: start-bp-wallet-server.bat
(Please read the comments in file for more info.)
Script: start-bp-wallet-docker.bat
Script: start-bp-wallet-client.bat
Script: start-bp-wallet-docker.bat
http://localhost:8080/swagger-ui.html#/wallet-client-controller/executeUsingPOST
http://<dockermachine -ip>:8080/swagger-ui.html#//wallet-client-controller/executeUsingPOST
{
"numberOfRequests": 1,
"numberOfRounds": 1,
"numberOfUsers": 100
}
{
"transactions": {
"DEPOSIT": {
"TRANSACTION_SUCCESS": 146,
"TRANSACTION_FAILED": 0,
"UNRECOGNIZED": 0
},
"WITHDRAW": {
"TRANSACTION_SUCCESS": 17,
"TRANSACTION_FAILED": 0,
"UNRECOGNIZED": 0
},
"BALANCE": {
"TRANSACTION_SUCCESS": 174,
"TRANSACTION_FAILED": 0,
"UNRECOGNIZED": 0
},
"UNRECOGNIZED": {
"TRANSACTION_SUCCESS": 0,
"TRANSACTION_FAILED": 395,
"UNRECOGNIZED": 0
}
},
"timeTaken": 10
}
timeTaken in Seconds.
Note:For quick start up please import the project in STS and run BPWS
and BPWC
as spring boot app
.
- The Whole Structure of the BP-wallet application is loosely coupled SOA.
- Each Client,Server,DB Instances are developed keeping Scalability,Elasticity and Fault tolerance in mind.
- Docker Instances make it possible to enable containerization and Helps in Deployments.
- The Performance Tuning variables are not yet externalized.
- Server Side - Connection Pooling Configurations(Depends on Given Deployment Platform).
- Client Side - Task Executor is Configurable with Concurrent Worker Threads.
- The
BPWP
is shared with Client and Server. - Synchronization or any code level locking on DB has been avoided as there can be multiple instances running.
Optimistic Locking
via@Version
annotation is implemented forConcurrency
(Which can also be configured for retry mechanism[Disabled for Now])- There are Still Some
Race Conditions
inBPWS
. - User Registration: N number User are registered with Zero Balance at application startup (This is done to avoid user not found exception and to support Integration tests. This is a bare-bone approach and only adopted due to RAPID).
- For Improved performance
Logging
has been minimized via debug or disabled inapplication.yaml
. - Some of the Decisions and choices are evident from TPS section.
Its Pandora's Box !! Can have Numerous Permutation & Combination with each variant, and requires performance tuning and monitoring to reach a common objective or to handle any future spikes
.
Application Variant : All below are 10 Concurrent Calls but they take different execution time because of their nature.
- Single user Making 10 Deposit: Corresponds to 20 DB Calls 10 for Get and 10 for Update.
- Single user Making 5 Withdraws 5 Deposit:Same.
- Single user Making 4 Withdraws 4 Deposit 2 Balance: 18 DB Calls 10 Get and 8 Update.
All of the above transactions have high chances of OptimisticLockException
due to versioning on WIP same row stale object.Can be mitigated with retry mechanism however in this kind of scenario the sequence need to be guaranteed possible with bidirectional streaming
.
Then there are other scenarios with multiple users with multiple transactions - testing is OOS.
- Embedded H2 DB:only used for RAPID.
- MYSQL DB: Although mysql can handle 150+1 Connections , for that the calling system should be of very high configurations, The current application configures the Connection Pool Max Size to be 10 based
Number of Cores * 2 + Max(tX Spindle time)
- Dockerized MYSQL DB: This makes a delta of 5-10 %.
- HIKARI:If the number of transaction grows > 400 HCP starts getting
Connection Not Available
, to avoid this the session state was properly synced with DB using flush in finally block.The connection time out was tweaked to 3 Minutes from default.This alone is not sufficient there need to be Data replication in MYSQL with Application Caching for outstanding performance which OOS.For better performance a dynamically resizing DB connection pooling mechanism is needed ..Flexy-pool
fits here but OOS. - Apache:OOS
- Wallet Server Deployed Local Machine -(4 GB RAM 4 Cores i5):At large 100+-.
- Wallet Server Deployed on other Machines:OOS
Having said that, About 120(+-) concurrent requests per-second was achieved, Although it may very on machines, For example About 1000+- Transactions happened in about 1 second for one user deposit [On H2 DB !!].
Apart from this - there is another POC written which executes for one second so to better understand the stages of optimization.
The over all goal was to run it wit time command and check the actual CPU utilization
Memory Utilization
.
1. Make a withdrawal of USD 200 for user with id 1. Must return "insufficient_funds".
2. Make a deposit of USD 100 to user with id 1.
3. Check that all balances are correct
4. Make a withdrawal of USD 200 for user with id 1. Must return "insufficient_funds".
5. Make a deposit of EUR 100 to user with id 1.
6. Check that all balances are correct
7. Make a withdrawal of USD 200 for user with id 1. Must return "insufficient_funds".
8. Make a deposit of USD 100 to user with id 1.
9. Check that all balances are correct
10. Make a withdrawal of USD 200 for user with id 1. Must return "ok".
11. Check that all balances are correct
12. Make a withdrawal of USD 200 for user with id 1. Must return "insufficient_funds".
- Cloud Ready.
- Load Ba-lancer.
- Service Discovery.
- Authentication.
- UI Client.
- Docker Images provisioning and Orchestration via compose.
- Caching enabled Entities - with Eviction and Put strategies.
- DB Schema.