Depozit Application
Our aim was to provide web-banking app to serve customers' deposit accounts with the best possible functionality/codesize rate. We've created fast and robust responsive web application that can fit any devices and provides modern secure features over the fast WebSocket channel.
Minimal
When we are talking about enterprise solutions we always talk about giants, like CLR/JVM virtual machines and SAP/Oracle warehouses. However systems needn't to be complex and technologies like K langauge and Erlang provide enough declarative expressivness for business applications. Having some experience in building banking software we provide smallest possible processing infrastructure to be easily verified and managed while being robust at the same time.
The application is built using Erlang libraries from synrc
and spawnproc GitHub organizations. Consumer appliaction
hosts custom rules depending protocol or server module.
E.g. your business application defines a set of business
processes which are driven by BPE server, this application usually called ACT,
from "activity server".
The architecture is based on spawnproc processing applications and relies on synrc Erlang stack and N2O protocol. It is run using voxoz development tools. The application can be distributed as a single file, is able to run on Windows, Linux and Mac and fits to 2.88MB diskette. For processing opening application database consumes 2K per record.
Despite small size of application, it can operate hundred of millions records in KVS database. All banking "big-data" of transaction chains can be managed withing a single diskette. That is top-key feature of spawnproc application stack.
Responsive
Our aim was to provide smallest possible minimalistic application for PrivatBank customers, who are using deposit accounts. We've created fast and robust responsive web application that can fit any devices and provides modern secure features over the fast WebSocket channel.
Fact: SSL page load is about 380ms from AWS ELB. The uncompressed size of SPA static assests is also hyperoptmized:
$ cloc js css htm
Language files blank comment code
------------------------------------------------
Erlang 69 605 169 4800
HTML 4 60 23 394
Javascript 9 246 123 1884
CSS 1 32 11 176
Performant
Fact: the size is 2.1MB and boot time is 2s
$ ls -lh deposits
-rwxr--r-- 1 5HT staff 2.1M Feb 21 04:07 deposits
$ date && ./deposits
Sun Mar 1 01:53:42 EET 2015
Eshell V6.2 (abort with ^G)
1>
=INFO REPORT==== 1-Mar-2015::01:53:44 ===
deposits_sup:Deposits WebSocket Server is started.
Fact: it is easy to hold 20Mbps to the small AWS instance.
$ tcpkali -T10s -r 10000 -c 50 --first-message "N2O," \
-m PING --ws deposits.privat.bank/ws/static/app/open.htm
Ramped up to 50 connections.
Total data sent: 20.4 MiB (21390904 bytes)
Total data received: 3.4 MiB (3602222 bytes)
Bandwidth per channel: 0.399 Mbps, 49.9 kBps
Aggregate bandwidth: 2.878↓, 17.093↑ Mbps
Test duration: 10.0116 s.
Fact: 3M of records consume 6GB of storage. This is enought to put everything on ARM LING VM using 32GB SD-card for storing 2.1MB of application bundle and 5MB or LING image. The memory consumption could be limited to 8GB of RAM per 3M records.
> kvs:dump().
name storage_type memory size
account disc_copies 120442109 3205762
process disc_copies 20274 26
history disc_copies 4190 111
. . . .
. . . .
Snapshot taken: {{2015,3,3},{17,39,34}}
ok
$ du -hs [email protected]
5861M
Business Processes
In business process management the key feature of is a compact process definitions. Here is example of Deposit Opening process definition in simple and clean BPMN 2.0 aware language. The web application is an client to ACT business process server, which hosts business process context. All the processes can be executed in attached console without web application.
deposit_app() ->
#process { name = 'Create Deposit Account',
flows = [
#sequenceFlow{source='Init', target='Payment'},
#sequenceFlow{source='Payment', target='Signatory'},
#sequenceFlow{source='Payment', target='Process'},
#sequenceFlow{source='Process', target='Final'},
#sequenceFlow{source='Signatory', target='Process'},
#sequenceFlow{source='Signatory', target='Final'}
],
tasks = [
#userTask { name='Init', module = deposit },
#userTask { name='Signatory', module = deposit},
#serviceTask { name='Payment', module = deposit},
#serviceTask { name='Process', module = deposit},
#endEvent { name='Final'}
],
beginEvent = 'Init',
endEvent = 'Final',
events = [
#messageEvent{name="PaymentReceived"}
]
}.
Thanks to compact process context the 1 million records of process states consume only 1GB of storage.
Heart System Capacity
Having measured the capacity of WebSocket front-end, we also state that performance of BPE engine and all PrivatBank service stack is outstanding. Here is sample business process that involves most external heavy services.
EKBID = wf:qp(<<"ekbid">>,Req),
Phone = #phone { number = "+38000000000" },
Sid = #sid { sid = "15080" },
Client = #client { phone = "+38000000000",
ekb_id = EKBID,
names = <<"Тест"/utf8>>,
surnames = <<"Тест"/utf8>> },
Deposit = #deposit_app {
program = <<"DE00">>,
currency = <<"980">>,
rate = [{rate,24},{bonus,0},{renewal_bonus,0.5}],
duration = 12,
type = <<"TEST">>,
amount = 2,
chargeDeposit = card,
cardForCharge = #card{pan = <<"0000000000000000">>} },
{ok,Pid} = bpe:start(deposit:def(), [ {dep_atom,'DE00_12_uah'},
{notification, self()},
{lang, ru},
{channel,p24}]),
{complete,'CheckSession'} = bpe:amend(Pid, [Phone,Deposit,Client,Sid]),
{complete,'CheckClient'} = bpe:complete(Pid),
{complete,'AcquireApp'} = bpe:complete(Pid),
{complete,'PrepareAccount'} = bpe:complete(Pid),
{complete,'CreateAccount'} = bpe:complete(Pid),
{complete,'CardPayment'} = bpe:complete(Pid),
{complete,'Payment'} = bpe:complete(Pid),
Sid1 = bpe:doc(#sid{}, bpe:process(Pid)),
{complete,'PaymentComplete'} = bpe:amend(Pid, Sid1#sid{web_pid= self()}),
'AcquireApp' = bpe:complete(Pid),
For storm we are using wrk utility that can go far beyond siege, httperf, ab and jmeter abilities. BPE engine is settled as external REST service through our rest application. BPE along with PrivatBank services can pass all steps for 500 customers in 1 second on single Depository Application node.
$ wrk -c750 -d10s "http://processes.privat.bank/service/?ekbid=001"
Running 10s test @ http://processes.privat.bank/service/?ekbid=001
2 threads and 750 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.09s 391.52ms 2.00s 71.95%
Req/Sec 277.85 188.50 1.11k 70.69%
4925 requests in 10.08s, 860.91KB read
Socket errors: connect 0, read 0, write 0, timeout 102
Requests/sec: 588.78
Transfer/sec: 85.44KB
Table 1. Run test for each C for 1 minute
+---------------+------------+-------------+
| Connections | Finished | Timeouts |
+---------------+------------+-------------+
| 200 | 200 | 0 |
| 225 | 225 | 0 |
| 250 | 250 | 0 |
| 275 | 275 | 0 |
| 300 | 300 | 0 |
| 325 | 325 | 0 |
| 350 | 350 | 0 |
| 375 | 375 | 0 |
| 400 | 400 | 0 |
| 425 | 425 | 0 |
| 450 | 450 | 0 |
| 475 | 475 | 0 |
| 500 | 500 | 0 |
| 525 | 525 | 0 |
| 550 | 523 | 27 |
| 575 | 554 | 21 |
| 600 | 567 | 33 |
| 625 | 580 | 45 |
+---------------+------------+-------------+