SlideShare une entreprise Scribd logo
1  sur  43
Télécharger pour lire hors ligne
Designing scalable application:
from umbrella project to
distributed system
Elixir Club 8, Kyiv, October 21, 2017
HELLO!
I am Anton Mishchuk
- Ruby/Elixir developer at Matic Insurance Services
we are hiring!
- A big fan of Elixir programming language
- Author and maintainer of: ESpec and Flowex libraries
github: antonmi
2
BEAM Applications
BEAM VM
Application
GS S GS
GS GS
Application
GS S GS
GS GS
Application
GS S GS
GS GS
3
Intro
○ Monolith or micro-services
○ BEAM approach
○ Umbrella project
○ Inter-service communication
4
Contents
○ “ML Tools” demo project
○ Three levels of code organization
○ Interfaces modules
○ Scaling to distributed system
○ Limiting concurrency
5
ML Tools
○ Stands for “Machine Learning Tools”
○ One can find it here:
□ https://github.com/antonmi/ml_tools
○ Umbrella project with 4 applications:
□ Datasets
□ Models
□ Utils
□ Main
6
Umbrella project
Monolith and microservices at the same time:
○ All applications are in one place
○ Easy to develop and test
○ Service-oriented architecture
○ Ready to scale
7
Code organization
○ Service level
○ Context level
○ Implementation level
8
Service level
Main
Datasets Models Utils
9
Context and
implementation
(datasets)
datasets/
lib/
datasets/
fetchers/
fetchers.ex
aws.ex
kaggle.ex
collections/
○ Datasets.Fetchers
○ Datasets.Fetchers.Aws
○ Datasets.Fetchers.Kaggle
10
Context and
implementation
(models)
models/
lib/
models/
lm/
lm.ex
engine.ex
rf/
rf.ex
engine.ex
○ Models.Lm
○ Models.Lm.Engine
○ Models.Rf
○ Models.Rf.Engine
11
Context and
implementation
(utils)
utils/
lib/
utils/
pre_processing/
pre_processing.ex
filter.ex
normalizer.ex
visualization/
...
○ Utils.PreProcessing
○ Utils.PreProcessing.Filter
○ Utils.PreProcessing.Normalizer
12
Main application
defp deps do
[
{:datasets, in_umbrella: true},
{:models, in_umbrella: true},
{:utils, in_umbrella: true}
]
end
13
Main application
defmodule Main.Zillow do
def rf_fit do
Datasets.Fetchers.zillow_data
|> Utils.PrePorcessing.normalize_data
|> Models.Rf.fit_model
end
end
14
What’s wrong with
this code?
defmodule Main.Zillow do
def rf_fit do
Datasets.Fetchers.zillow_data
|> Utils.PreProcessing.normalize_data
|> Models.Rf.fit_model
end
end
15
Encapsulation
problem
○ Modules, functions and that’s it!
○ Main application has access to all public
functions in all the applications
○ Micro-services -> tightly coupled monolith
16
Interfaces Modules
○ Specific modules in the application that
implements public interface
○ Grouped by context
○ Other application must use only functions
from this modules
17
Just delegate
defmodule Datasets.Interfaces.Fetchers do
alias Datasets.Fetchers
defdelegate zillow_data, to: Fetchers
defdelegate landsat_data, to: Fetchers
end
18
Use Interfaces only!
def rf_fit do
Datasets.Interfaces.Fetchers.zillow_data
|> Utils.Interfaces.PreProcessing.normalize_data
|> Models.Interfaces.Rf.fit_model
end
19
Interfaces Modules
○ It’s just a convention
○ But very important convention
○ It creates a good basis for future scaling
20
Let’s scale!
○ We decide to run each of the applications
on a different node
○ Modules from one application will be not
accessible in another one
○ But we still want keep all the things in one
place
21
Interface applications
Main
Datasets Models Utils
DatasetsInterface
ModelsInterface
UtilsInterface
22
Interface application
models_interface/
config/
lib/
models_interface/
models_interface.ex
lm.ex
rf.ex
mix.ex
23
Inter-Application
Communication
○ RPC (for ‘models’ app)
○ Distributed tasks (for ‘datasets’ app)
○ HTTP (for ‘utils’ app)
24
RPC
○ Remote Procedure Call
○ :rpc.call(node, module, fun, args)
○ BEAM handles all the serialization and
deserialization stuff
25
ModelsInterface.Rf
defmodule ModelsInterface.Rf do
def fit_model(data) do
ModelsInterface.remote_call(
Models.Interfaces.Rf,
:fit_model,
[data]
)
end
end
26
ModelsInterface
defmodule ModelsInterface do
def remote_call(module, fun, args, env  Mix.env) do
do_remote_call({module, fun, args}, env)
end
defp do_remote_call({module, fun, args}, :test) do
apply(module, fun, args)
end
defp do_remote_call({module, fun, args}, _) do
:rpc.call(remote_node(), module, fun, args)
end
end 27
Quick refactoring
○ Replace Models.Interfaces with
ModelsInterface and that’s it
○ You continue develop with the same speed
○ Nothing to change in tests
28
What about tests
defp do_remote_call({module, fun, args}, :test) do
apply(module, fun, args)
end
defp do_remote_call({module, fun, args}, _) do
:rpc.call(remote_node(), module, fun, args)
end
29
What about tests
defp deps do
[
...
{:models, in_umbrella: true, only: [:test]},
{:models_interface, in_umbrella: true},
...
]
end
30
RPC cons
○ Synchronous calls from “local” process
○ Needs additional logic for
asynchronous execution
31
Distributed tasks
○ Build on top of Elixir Task
○ Task are spawned on remote node
○ Supervisor controls the tasks
32
Start Supervisor
defmodule Datasets.Application do
...
def start(_type, _args) do
children = [
supervisor(Task.Supervisor,
[[name: Datasets.Task.Supervisor]],
[restart: :temporary, shutdown: 10000])
]
...
end
end 33
DatasetsInterface
defmodule DatasetsInterface do
...
defp do_spawn_task({module, fun, args}, _) do
Task.Supervisor.async(remote_supervisor(),
module, fun, args)
|> Task.await
end
defp remote_supervisor, do: #config data
end
34
Erlang Distribution
Protocol
Pros:
○ Battle-tested over decades
○ Free serialization / deserialization
Cons:
○ Not very secure
○ No very fast
○ BEAM specific
35
HTTP
UtilsInterface
defmodule UtilsInterface do
...
defp do_remote_call({module, fun, args}, _) do
{:ok, resp} = HTTPoison.post(remote_url(),
serialize({module, fun, args}))
deserialize(resp.body)
end
...
end
36
Plug in Utils app
defmodule Utils.Interfaces.Plug do
use Plug.Router
...
post "/remote" do
{:ok, body, conn} = Plug.Conn.read_body(conn)
{module, fun, args} = deserialize(body)
result = apply(module, fun, args)
send_resp(conn, 200, serialize(result))
end
end
37
Limiting
concurrency
When do you need this?
○ Third-party API rate limits
○ Heavy calculations per request
38
def start(_type, _args) do
pool_opts = [
name: {:local, Models.Interface},
worker_module: Models.Interfaces.Worker,
size: 5, max_overflow: 5]
children = [
:poolboy.child_spec(Models.Interface, pool_opts, []),
]
end
poolboy in
Models.Application
39
defmodule Models.Interfaces.Worker do
use GenServer
...
def handle_call({module, fun, args}, _from, state) do
result = apply(module, fun, args)
{:reply, result, state}
end
end
Models.Interfaces.Worker
40
defmodule Models.Interfaces.Rf do
def fit_model(data) do
with_poolboy({Models.Rf, :fit_model, [data]})
end
def with_poolboy(args) do
worker = :poolboy.checkout(Models.Interface)
result = GenServer.call(worker, args, :infinity)
:poolboy.checkin(Models.Interface, worker)
result
end
end
Models.Interfaces.Rf
41
Conclusion
○ Start with micro-services from
the very beginning
○ Think carefully about app interface
○ Use EDP for communication when scale
○ Keep “communication” code in separate app
42
THANKS!
Ask me questions!
43

Contenu connexe

Similaire à Designing scalable application: from umbrella project to distributed system - Anton Mishchuk

Flowex: Flow-Based Programming with Elixir GenStage - Anton Mishchuk
Flowex: Flow-Based Programming with Elixir GenStage - Anton MishchukFlowex: Flow-Based Programming with Elixir GenStage - Anton Mishchuk
Flowex: Flow-Based Programming with Elixir GenStage - Anton Mishchuk
Elixir Club
 

Similaire à Designing scalable application: from umbrella project to distributed system - Anton Mishchuk (20)

Hexagonal Architecture.pdf
Hexagonal Architecture.pdfHexagonal Architecture.pdf
Hexagonal Architecture.pdf
 
Webinar: Começando seus trabalhos com Machine Learning utilizando ferramentas...
Webinar: Começando seus trabalhos com Machine Learning utilizando ferramentas...Webinar: Começando seus trabalhos com Machine Learning utilizando ferramentas...
Webinar: Começando seus trabalhos com Machine Learning utilizando ferramentas...
 
cf.Objective() 2017 - Design patterns - Brad Wood
cf.Objective() 2017 - Design patterns - Brad Woodcf.Objective() 2017 - Design patterns - Brad Wood
cf.Objective() 2017 - Design patterns - Brad Wood
 
Peer sim (p2p network)
Peer sim (p2p network)Peer sim (p2p network)
Peer sim (p2p network)
 
Machine Learning Infrastructure
Machine Learning InfrastructureMachine Learning Infrastructure
Machine Learning Infrastructure
 
Meetup 2020 - Back to the Basics part 101 : IaC
Meetup 2020 - Back to the Basics part 101 : IaCMeetup 2020 - Back to the Basics part 101 : IaC
Meetup 2020 - Back to the Basics part 101 : IaC
 
SigOpt at MLconf - Reducing Operational Barriers to Model Training
SigOpt at MLconf - Reducing Operational Barriers to Model TrainingSigOpt at MLconf - Reducing Operational Barriers to Model Training
SigOpt at MLconf - Reducing Operational Barriers to Model Training
 
Alexandra johnson reducing operational barriers to model training
Alexandra johnson   reducing operational barriers to model trainingAlexandra johnson   reducing operational barriers to model training
Alexandra johnson reducing operational barriers to model training
 
Persian MNIST in 5 Minutes
Persian MNIST in 5 MinutesPersian MNIST in 5 Minutes
Persian MNIST in 5 Minutes
 
JavaScript for Enterprise Applications
JavaScript for Enterprise ApplicationsJavaScript for Enterprise Applications
JavaScript for Enterprise Applications
 
Machine Learning Infrastructure
Machine Learning InfrastructureMachine Learning Infrastructure
Machine Learning Infrastructure
 
Rise of the machines: Continuous Delivery at SEEK - YOW! Night Summary Slides
Rise of the machines: Continuous Delivery at SEEK - YOW! Night Summary SlidesRise of the machines: Continuous Delivery at SEEK - YOW! Night Summary Slides
Rise of the machines: Continuous Delivery at SEEK - YOW! Night Summary Slides
 
Deep learning beyond the learning - Jörg Schad - Codemotion Rome 2018
Deep learning beyond the learning - Jörg Schad - Codemotion Rome 2018 Deep learning beyond the learning - Jörg Schad - Codemotion Rome 2018
Deep learning beyond the learning - Jörg Schad - Codemotion Rome 2018
 
Flow Base Programming with Node-RED and Functional Reactive Programming with ...
Flow Base Programming with Node-RED and Functional Reactive Programming with ...Flow Base Programming with Node-RED and Functional Reactive Programming with ...
Flow Base Programming with Node-RED and Functional Reactive Programming with ...
 
Leveraging Android's Linux Heritage at AnDevCon3
Leveraging Android's Linux Heritage at AnDevCon3Leveraging Android's Linux Heritage at AnDevCon3
Leveraging Android's Linux Heritage at AnDevCon3
 
TensorFlow 16: Building a Data Science Platform
TensorFlow 16: Building a Data Science Platform TensorFlow 16: Building a Data Science Platform
TensorFlow 16: Building a Data Science Platform
 
Flowex - Railway Flow-Based Programming with Elixir GenStage.
Flowex - Railway Flow-Based Programming with Elixir GenStage.Flowex - Railway Flow-Based Programming with Elixir GenStage.
Flowex - Railway Flow-Based Programming with Elixir GenStage.
 
Flowex: Flow-Based Programming with Elixir GenStage - Anton Mishchuk
Flowex: Flow-Based Programming with Elixir GenStage - Anton MishchukFlowex: Flow-Based Programming with Elixir GenStage - Anton Mishchuk
Flowex: Flow-Based Programming with Elixir GenStage - Anton Mishchuk
 
Async Web Frameworks in Python
Async Web Frameworks in PythonAsync Web Frameworks in Python
Async Web Frameworks in Python
 
From class to architecture
From class to architectureFrom class to architecture
From class to architecture
 

Plus de Elixir Club

Plus de Elixir Club (20)

Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club UkraineKubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
 
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
Integrating 3rd parties with Ecto -  Eduardo Aguilera | Elixir Club UkraineIntegrating 3rd parties with Ecto -  Eduardo Aguilera | Elixir Club Ukraine
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
 
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
— An async template  -  Oleksandr Khokhlov | Elixir Club Ukraine— An async template  -  Oleksandr Khokhlov | Elixir Club Ukraine
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
 
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
BEAM architecture handbook - Andrea Leopardi  | Elixir Club UkraineBEAM architecture handbook - Andrea Leopardi  | Elixir Club Ukraine
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
 
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
You ain't gonna need write a GenServer - Ulisses Almeida  | Elixir Club UkraineYou ain't gonna need write a GenServer - Ulisses Almeida  | Elixir Club Ukraine
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
 
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
 — Knock, knock — An async templates — Who’s there? - Alexander Khokhlov  |  ... — Knock, knock — An async templates — Who’s there? - Alexander Khokhlov  |  ...
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
 
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
 
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
Erlang cluster. How is it? Production experience. —  Valerii Vasylkov | Elixi...Erlang cluster. How is it? Production experience. —  Valerii Vasylkov | Elixi...
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
 
Promo Phx4RailsDevs - Volodya Sveredyuk
Promo Phx4RailsDevs - Volodya SveredyukPromo Phx4RailsDevs - Volodya Sveredyuk
Promo Phx4RailsDevs - Volodya Sveredyuk
 
Web of today — Alexander Khokhlov
Web of today —  Alexander KhokhlovWeb of today —  Alexander Khokhlov
Web of today — Alexander Khokhlov
 
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
ElixirConf Eu 2018, what was it like? – Eugene PirogovElixirConf Eu 2018, what was it like? – Eugene Pirogov
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
 
Implementing GraphQL API in Elixir – Victor Deryagin
Implementing GraphQL API in Elixir – Victor DeryaginImplementing GraphQL API in Elixir – Victor Deryagin
Implementing GraphQL API in Elixir – Victor Deryagin
 
WebPerformance: Why and How? – Stefan Wintermeyer
WebPerformance: Why and How? – Stefan WintermeyerWebPerformance: Why and How? – Stefan Wintermeyer
WebPerformance: Why and How? – Stefan Wintermeyer
 
GenServer in Action – Yurii Bodarev
GenServer in Action – Yurii Bodarev   GenServer in Action – Yurii Bodarev
GenServer in Action – Yurii Bodarev
 
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Russian Doll Paradox: Elixir Web without Phoenix - Alex RozumiiRussian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
 
Practical Fault Tolerance in Elixir - Alexei Sholik
Practical Fault Tolerance in Elixir - Alexei SholikPractical Fault Tolerance in Elixir - Alexei Sholik
Practical Fault Tolerance in Elixir - Alexei Sholik
 
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Phoenix and beyond: Things we do with Elixir - Alexander KhokhlovPhoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
 
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
Monads are just monoids in the category of endofunctors - Ike KurghinyanMonads are just monoids in the category of endofunctors - Ike Kurghinyan
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
 
Craft effective API with GraphQL and Absinthe - Ihor Katkov
Craft effective API with GraphQL and Absinthe - Ihor KatkovCraft effective API with GraphQL and Absinthe - Ihor Katkov
Craft effective API with GraphQL and Absinthe - Ihor Katkov
 
Elixir in a service of government - Alex Troush
Elixir in a service of government - Alex TroushElixir in a service of government - Alex Troush
Elixir in a service of government - Alex Troush
 

Dernier

Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Dernier (20)

Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdf
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 

Designing scalable application: from umbrella project to distributed system - Anton Mishchuk