SlideShare a Scribd company logo
1 of 61
Download to read offline
SaaS con Symfony2 
un caso *molto* concreto di applicazione multitenant
@ftassi 
Francesco Tassi 
@matteomoretti85 
Matteo Moretti
Nuvola e i suoi 50GB
Nuvola e i suoi 50GB 
• Difficoltà di manutenzione (backup/ripristino) 
• Difficoltà di evoluzione (alter dello schema) 
• Impossibile replicare il sistema (debug)
Applicazioni multi 
tenant
Applicazioni multi 
tenant 
“Multi-tenant si riferisce ad una architettura 
software in cui una singola istanza del suddetto 
software gira su un server ed è utilizzata da più di 
una client organization (tenant)”. 
– wikipedia
Sharding
Sharding 
A database shard is a horizontal partition of data 
in a database. Each individual partition is referred 
to as a shard or database shard. Each shard is 
held on a separate database server instance, to 
spread load. 
– wikipedia
Sharding 
user_id username 
1 idiopathic 
2 bouffant 
3 skedaddle 
4 tweezers 
5 igloo 
6 foibles 
7 oocephalus
Sharding 
user_id username 
1 idiopathic 
2 bouffant 
3 skedaddle 
4 tweezers 
5 igloo 
6 foibles 
7 oocephalus
Sharding 
user_id username 
1 idiopathic 
2 bouffant 
3 skedaddle 
user_id username 
4 tweezers 
5 igloo 
6 foibles 
7 oocephalus 
Shard 1 
Shard 2
Vantaggi 
• Suddivide anche il carico di scrittura 
• Indici più piccoli 
• distribuzione dei dati migliore
Svantaggi 
• Difficile o impossibile effettuare query su shard 
differenti 
• Consistenza dei dati 
• Complessità extra
Supporto nativo 
http://en.wikipedia.org/wiki/ 
Shard_(database_architecture)#Support_for_sh 
ards
Sharding con Doctrine
Sharding con Doctrine 
Starting with 2.3 Doctrine DBAL contains some 
functionality to simplify the development of 
horizontally sharded applications. 
! 
In this first release it contains a ShardManager 
interface. This interface allows to programatically 
select a shard to send queries to. 
- http://doctrine-dbal.readthedocs.org/en/latest/reference/sharding.html
Sharding con Doctrine 
At the moment there are no functionalities yet to 
dynamically pick a shard based on ID, query or 
database row yet 
- http://doctrine-dbal.readthedocs.org/en/latest/reference/sharding.html
ShardManager Interface 
$shardManager = new PoolingShardManager($conn); 
! 
$currentCustomerId = 1234; 
$shardManager->selectShard($currentCustomerId); 
// all queries after this call hit the shard 
// where customer with id 1234 is on. 
! 
$shardManager->selectGlobal(); 
// the global database is selected.
https://www.flickr.com/photos/reallyboring/3234624436 
Il Piano
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N databases
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N databases
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N databases
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N databases
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N databases
Strategia di 
frazionamento
KEEP CALM 
AND 
SPLIT YOUR DATA
Strategia di frazionamento 
user_id istituto_id! username 
1 1 idiopathic 
2 2 bouffant 
3 1 skedaddle 
4 1 tweezers 
5 2 igloo 
6 1 foibles 
7 1 oocephalus
Strategia di frazionamento 
user_id istituto_id! username 
1 1 idiopathic 
2 2 bouffant 
3 1 skedaddle 
4 1 tweezers 
5 2 igloo 
6 1 foibles 
7 1 oocephalus
Strategia di frazionamento 
user_id istituto_id! username 
1 1 idiopathic 
3 1 skedaddle 
4 1 tweezers 
6 1 foibles 
7 1 oocephalus 
user_id istituto_id! username 
2 2 bouffant 
5 2 igloo 
Shard 1 
Shard 2
Strategia di selezione 
del DB
Sottodominio?
Sottodominio
Chiedilo all’utente
Chiedilo all’utente 
• Login tramite database unico (default) ! 
• Selezione manuale dell’istituto 
• Switch della connessione 
• Sincronizzazione dei dati duplicati
Una connessione “default” 
doctrine: 
dbal: 
default_connection: default 
connections: 
default: 
driver: "%database_driver%" 
host: "%database_host%" 
port: "%database_port%" 
dbname: "%database_name%" 
user: "%database_user%" 
password: "%database_password%" 
charset: UTF8
500 Shards 
shards: 
mc12345678: 
id: 1 
host: '%database_host%' 
user: '%database_user%' 
password: '%database_password%' 
dbname: nuvolamc12345678 
charset: UTF8 
mcps015006: 
id: 2 
host: '%database_host%' 
user: '%database_user%' 
password: '%database_password%' 
dbname: mcps015006 
charset: UTF8
UUID 
user_id istituto_id! username uuid 
1 1 idiopathic 
e5f0b536- 
c4cd-47c4- 
a810- 
2 2 bouffant 
ea5d2eb4-851c 
-462d-a25e- 
1756bece 
3 1 skedaddle 
a5889369-61d8 
-4b3c-b93f-cd4a3d449c46 
4 1 tweezers 
cd5759ae-7a7e 
-42d1- 
b4cf-0cd0701b 
5 2 igloo 
64976e7a-54d2 
-4230-a8ef-d624dc320cee 
6 1 foibles 
202528c0-7028 
-4a6f-9c0b-e97c6544693c 
7 1 oocephalus 
30bd250c-0a9c 
-4cf2- 
a54c-020804d1
Sincronizzazione 
(onFlush, prePersist) 
$this 
->eventDispatcher 
->dispatch( 
MultiDbSyncEntityEvent::SYNC_UTENTE, 
new MultiDbSyncEntityEvent($utente) 
);
Sincronizzazione 
public function onSyncUtente(MultiDbSyncEntityEvent 
$event) 
{ 
$user = $event->getEntity(); 
$shard = $this->getShardToSync($user); 
/** @var $connection DoctrineDBALConnection */ 
$connection = $this->doctrine->getConnection($this- 
>getConnectionNameFromShard($shard)); 
$this->syncUser($user, $connection); 
}
500 Connessioni 
nuvolamc12345678: 
driver: '%database_driver%' 
host: '%database_host%' 
port: '%database_port%' 
dbname: nuvolamc12345678 
user: '%database_user%' 
password: '%database_password%' 
charset: UTF8 
nuvolamcps015006: 
driver: '%database_driver%' 
host: '%database_host%' 
port: '%database_port%' 
dbname: nuvolamcps015006 
user: '%database_user%' 
password: '%database_password%' 
charset: UTF8
Switch della 
connessione
Switch della connessione 
private function selectDbForIstituto( 
Istituto $istituto, 
SessionInterface $session 
) 
{ 
$shardManager = $this->get('shard_manager'); 
$shardManager->selectShard($istituto); 
! 
$session->set('shard', $istituto->getShardId()); 
}
Switch della connessione 
public function onKernelRequest(GetResponseEvent $event) 
{ 
if (!$event->isMasterRequest()) { 
return; 
} 
! 
$this->shardManager->selectShard( 
$this->session->get(‘shard') 
); 
}
https://www.flickr.com/photos/jdhancock/8671399450/ 
Gestire 500 DB 
Help needed
Configurare gli shards 
protected function configure() 
{ 
$this->setName('nuvola:shard:add-config') 
->setDescription('Aggiunge la configurazione 
necessaria ad uno shard') 
->addOption('host', null, InputOption::VALUE_OPTIONAL, 
'L'host della connessione al db') 
->addOption('codiceMeccanografico', null, 
InputOption::VALUE_OPTIONAL, 'Codice meccanografico per lo 
shard'); 
}
Configurare gli shards 
protected function configure() 
{ 
$this->setName('nuvola:shard:create-config') 
->setDescription('Crea il file di configurazione per gli 
shards') 
->addOption( 
'append', 
null, 
InputOption::VALUE_NONE, 
'Se impostato a false cancella la configurazione attuale, 
altrimenit la aggiunge. Default a true' 
) 
//CUT 
}
Ad ognuno il suo shard 
public function onConsoleCommand(ConsoleCommandEvent $event) 
{ 
$shardManager = new SafeShardManager($connection); 
$istituto = $input->getParameterOption(['--istituto', '-i']); 
! 
if ('global' === $istituto) { 
$shardManager->selectGlobal(); 
} else { 
$shardManager->selectShard($istituto); 
} 
! 
}
Ciclare gli shards 
class ListShardsCommand extends AbstractShardCommand 
{ 
protected function configure() 
{ 
$this->setName('nuvola:shard:list-shards') 
->setDescription('Restituisce l'elenco degli shard configurati') 
->addOption( 
'letteraInizioIntervallo', 
null, 
InputOption::VALUE_OPTIONAL, 
'Lettera di inizio intervallo per lo shard da esportare (estremo 
compreso)' 
) 
->addOption( 
'letteraFineIntervallo', 
null, 
InputOption::VALUE_OPTIONAL, 
'Lettera di fine intervallo per lo shard da esportare (estremo compreso)' 
); 
} 
}
Ciclare gli shards 
app/console nu:sha:li | while read 
shard; do app/console doctrine:mig:mig - 
i $shard -n;done;
Parallelizzare FTW 
class MigrateCommand extends AbstractParallelCommand 
{ 
protected function execute(InputInterface $input, OutputInterface 
$output) 
{ 
/** @var GearmanClient $gearman */ 
$gearman = $this->getContainer()->get('gearman'); 
//[CUT] 
foreach ($shards as $shard) { 
$job = 'NuvolaMultiDbBundleWorkerShardWorker~migrate' . 
$shard['queue']; 
$gearman->addTask($job, $shard['shard']); 
} 
! 
$gearman->runTasks(); 
} 
}
Parallelizzare FTW 
protected function doMigrate(GearmanJob $job) 
{ 
$shard = $job->workload(); 
$command = sprintf( 
'app/console doctrine:migrations:migrate -n -i %s --env=%s', 
$shard, 
$this->env 
); 
! 
$process = $this->runProcess($job, $command); 
! 
if (!$process->isSuccessful()) { 
$this->sendErrorsToJob($job, $process, $command, 'Errore migrando ' . $shard); 
return; 
} 
! 
$success = [sprintf('Migrazione per %s completata', $shard)]; 
$job->sendComplete(serialize($success)); 
! 
return; 
}
Parallelizzare FTW 
app/console gearman:job:execute 
NuvolaMultiDbBundleWorkerShardWorker~mig 
rate0 -n —env=prod
Conclusioni
Fa al caso tuo?
Si, lo rifarei
Domande?
https://joind.in/12212
Thanks

More Related Content

Similar to SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Come Drupal costruisce le tue pagine
Come Drupal costruisce le tue pagineCome Drupal costruisce le tue pagine
Come Drupal costruisce le tue paginesparkfabrik
 
EE Incremental Store
EE Incremental StoreEE Incremental Store
EE Incremental Storefirenze-gtug
 
(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...
(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...
(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...Team per la Trasformazione Digitale
 
September 2010 - Gatein
September 2010 - GateinSeptember 2010 - Gatein
September 2010 - GateinJBug Italy
 
Sviluppo web dall'antichità all'avanguardia e ritorno
Sviluppo web  dall'antichità all'avanguardia e ritornoSviluppo web  dall'antichità all'avanguardia e ritorno
Sviluppo web dall'antichità all'avanguardia e ritornolordarthas
 
Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.
Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.
Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.Stefano Bianchini
 
Levate l'ancora! Rotte senza problemi con ZF2
Levate l'ancora! Rotte senza problemi con ZF2Levate l'ancora! Rotte senza problemi con ZF2
Levate l'ancora! Rotte senza problemi con ZF2Diego Drigani
 
PostgrSQL 9.3&9.4 - DjangoVillage
PostgrSQL 9.3&9.4 - DjangoVillagePostgrSQL 9.3&9.4 - DjangoVillage
PostgrSQL 9.3&9.4 - DjangoVillageMiriade Spa
 
Spring, IBatis e Transazioni Aop Nel Jug Avis Web
Spring, IBatis e Transazioni Aop Nel Jug Avis WebSpring, IBatis e Transazioni Aop Nel Jug Avis Web
Spring, IBatis e Transazioni Aop Nel Jug Avis WebMassimiliano Dessì
 
Programmazione e gestione della sicurezza: Verbale elettronico
Programmazione e gestione della sicurezza: Verbale elettronicoProgrammazione e gestione della sicurezza: Verbale elettronico
Programmazione e gestione della sicurezza: Verbale elettronicoDavide Ciambelli
 
Implementazione di un ambiente in alta affidabilità
Implementazione di un ambiente in alta affidabilitàImplementazione di un ambiente in alta affidabilità
Implementazione di un ambiente in alta affidabilitàAlfredo Parisi
 
DDAY2014 - Performance in Drupal 8
DDAY2014 - Performance in Drupal 8DDAY2014 - Performance in Drupal 8
DDAY2014 - Performance in Drupal 8DrupalDay
 
Elasticsearch a quick introduction
Elasticsearch a quick introductionElasticsearch a quick introduction
Elasticsearch a quick introductionFederico Panini
 
Simple Cloud API: accesso semplificato al cloud computing
Simple Cloud API: accesso semplificato al cloud computingSimple Cloud API: accesso semplificato al cloud computing
Simple Cloud API: accesso semplificato al cloud computingFrancesca1980
 
Sviluppo web con Ruby on Rails
Sviluppo web con Ruby on RailsSviluppo web con Ruby on Rails
Sviluppo web con Ruby on Railsjekil
 
How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript Stefano Marchisio
 
Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...
Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...
Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...azuredayit
 

Similar to SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant (20)

Come Drupal costruisce le tue pagine
Come Drupal costruisce le tue pagineCome Drupal costruisce le tue pagine
Come Drupal costruisce le tue pagine
 
EE Incremental Store
EE Incremental StoreEE Incremental Store
EE Incremental Store
 
(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...
(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...
(in)Sicurezza nella PA - Gianluca Varisco, Cybersecurity del Team per la Tras...
 
September 2010 - Gatein
September 2010 - GateinSeptember 2010 - Gatein
September 2010 - Gatein
 
Sviluppo web dall'antichità all'avanguardia e ritorno
Sviluppo web  dall'antichità all'avanguardia e ritornoSviluppo web  dall'antichità all'avanguardia e ritorno
Sviluppo web dall'antichità all'avanguardia e ritorno
 
Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.
Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.
Sicurezza Php (giugno 2010) Stefano Bianchini presso Ce.Se.N.A.
 
SQL Server & GDPR
SQL Server & GDPRSQL Server & GDPR
SQL Server & GDPR
 
Idp, passo dopo passo!
Idp, passo dopo passo!Idp, passo dopo passo!
Idp, passo dopo passo!
 
Levate l'ancora! Rotte senza problemi con ZF2
Levate l'ancora! Rotte senza problemi con ZF2Levate l'ancora! Rotte senza problemi con ZF2
Levate l'ancora! Rotte senza problemi con ZF2
 
Perl Template Toolkit
Perl Template ToolkitPerl Template Toolkit
Perl Template Toolkit
 
PostgrSQL 9.3&9.4 - DjangoVillage
PostgrSQL 9.3&9.4 - DjangoVillagePostgrSQL 9.3&9.4 - DjangoVillage
PostgrSQL 9.3&9.4 - DjangoVillage
 
Spring, IBatis e Transazioni Aop Nel Jug Avis Web
Spring, IBatis e Transazioni Aop Nel Jug Avis WebSpring, IBatis e Transazioni Aop Nel Jug Avis Web
Spring, IBatis e Transazioni Aop Nel Jug Avis Web
 
Programmazione e gestione della sicurezza: Verbale elettronico
Programmazione e gestione della sicurezza: Verbale elettronicoProgrammazione e gestione della sicurezza: Verbale elettronico
Programmazione e gestione della sicurezza: Verbale elettronico
 
Implementazione di un ambiente in alta affidabilità
Implementazione di un ambiente in alta affidabilitàImplementazione di un ambiente in alta affidabilità
Implementazione di un ambiente in alta affidabilità
 
DDAY2014 - Performance in Drupal 8
DDAY2014 - Performance in Drupal 8DDAY2014 - Performance in Drupal 8
DDAY2014 - Performance in Drupal 8
 
Elasticsearch a quick introduction
Elasticsearch a quick introductionElasticsearch a quick introduction
Elasticsearch a quick introduction
 
Simple Cloud API: accesso semplificato al cloud computing
Simple Cloud API: accesso semplificato al cloud computingSimple Cloud API: accesso semplificato al cloud computing
Simple Cloud API: accesso semplificato al cloud computing
 
Sviluppo web con Ruby on Rails
Sviluppo web con Ruby on RailsSviluppo web con Ruby on Rails
Sviluppo web con Ruby on Rails
 
How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript
 
Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...
Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...
Azure Day Rome Reloaded 2019 - Ingestion nel datalake passando tramite API Ma...
 

Recently uploaded

Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...Associazione Digital Days
 
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...Associazione Digital Days
 
ScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AIScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AIinfogdgmi
 
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”Associazione Digital Days
 
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...Associazione Digital Days
 
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...Associazione Digital Days
 

Recently uploaded (6)

Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
 
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
 
ScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AIScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AI
 
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
 
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
 
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
 

SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

  • 1. SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant
  • 2. @ftassi Francesco Tassi @matteomoretti85 Matteo Moretti
  • 3.
  • 4. Nuvola e i suoi 50GB
  • 5. Nuvola e i suoi 50GB • Difficoltà di manutenzione (backup/ripristino) • Difficoltà di evoluzione (alter dello schema) • Impossibile replicare il sistema (debug)
  • 7. Applicazioni multi tenant “Multi-tenant si riferisce ad una architettura software in cui una singola istanza del suddetto software gira su un server ed è utilizzata da più di una client organization (tenant)”. – wikipedia
  • 9. Sharding A database shard is a horizontal partition of data in a database. Each individual partition is referred to as a shard or database shard. Each shard is held on a separate database server instance, to spread load. – wikipedia
  • 10. Sharding user_id username 1 idiopathic 2 bouffant 3 skedaddle 4 tweezers 5 igloo 6 foibles 7 oocephalus
  • 11. Sharding user_id username 1 idiopathic 2 bouffant 3 skedaddle 4 tweezers 5 igloo 6 foibles 7 oocephalus
  • 12. Sharding user_id username 1 idiopathic 2 bouffant 3 skedaddle user_id username 4 tweezers 5 igloo 6 foibles 7 oocephalus Shard 1 Shard 2
  • 13. Vantaggi • Suddivide anche il carico di scrittura • Indici più piccoli • distribuzione dei dati migliore
  • 14. Svantaggi • Difficile o impossibile effettuare query su shard differenti • Consistenza dei dati • Complessità extra
  • 15. Supporto nativo http://en.wikipedia.org/wiki/ Shard_(database_architecture)#Support_for_sh ards
  • 17. Sharding con Doctrine Starting with 2.3 Doctrine DBAL contains some functionality to simplify the development of horizontally sharded applications. ! In this first release it contains a ShardManager interface. This interface allows to programatically select a shard to send queries to. - http://doctrine-dbal.readthedocs.org/en/latest/reference/sharding.html
  • 18. Sharding con Doctrine At the moment there are no functionalities yet to dynamically pick a shard based on ID, query or database row yet - http://doctrine-dbal.readthedocs.org/en/latest/reference/sharding.html
  • 19. ShardManager Interface $shardManager = new PoolingShardManager($conn); ! $currentCustomerId = 1234; $shardManager->selectShard($currentCustomerId); // all queries after this call hit the shard // where customer with id 1234 is on. ! $shardManager->selectGlobal(); // the global database is selected.
  • 20.
  • 22. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  • 23. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  • 24. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  • 25. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  • 26. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  • 27.
  • 29. KEEP CALM AND SPLIT YOUR DATA
  • 30. Strategia di frazionamento user_id istituto_id! username 1 1 idiopathic 2 2 bouffant 3 1 skedaddle 4 1 tweezers 5 2 igloo 6 1 foibles 7 1 oocephalus
  • 31. Strategia di frazionamento user_id istituto_id! username 1 1 idiopathic 2 2 bouffant 3 1 skedaddle 4 1 tweezers 5 2 igloo 6 1 foibles 7 1 oocephalus
  • 32. Strategia di frazionamento user_id istituto_id! username 1 1 idiopathic 3 1 skedaddle 4 1 tweezers 6 1 foibles 7 1 oocephalus user_id istituto_id! username 2 2 bouffant 5 2 igloo Shard 1 Shard 2
  • 37. Chiedilo all’utente • Login tramite database unico (default) ! • Selezione manuale dell’istituto • Switch della connessione • Sincronizzazione dei dati duplicati
  • 38. Una connessione “default” doctrine: dbal: default_connection: default connections: default: driver: "%database_driver%" host: "%database_host%" port: "%database_port%" dbname: "%database_name%" user: "%database_user%" password: "%database_password%" charset: UTF8
  • 39. 500 Shards shards: mc12345678: id: 1 host: '%database_host%' user: '%database_user%' password: '%database_password%' dbname: nuvolamc12345678 charset: UTF8 mcps015006: id: 2 host: '%database_host%' user: '%database_user%' password: '%database_password%' dbname: mcps015006 charset: UTF8
  • 40. UUID user_id istituto_id! username uuid 1 1 idiopathic e5f0b536- c4cd-47c4- a810- 2 2 bouffant ea5d2eb4-851c -462d-a25e- 1756bece 3 1 skedaddle a5889369-61d8 -4b3c-b93f-cd4a3d449c46 4 1 tweezers cd5759ae-7a7e -42d1- b4cf-0cd0701b 5 2 igloo 64976e7a-54d2 -4230-a8ef-d624dc320cee 6 1 foibles 202528c0-7028 -4a6f-9c0b-e97c6544693c 7 1 oocephalus 30bd250c-0a9c -4cf2- a54c-020804d1
  • 41. Sincronizzazione (onFlush, prePersist) $this ->eventDispatcher ->dispatch( MultiDbSyncEntityEvent::SYNC_UTENTE, new MultiDbSyncEntityEvent($utente) );
  • 42. Sincronizzazione public function onSyncUtente(MultiDbSyncEntityEvent $event) { $user = $event->getEntity(); $shard = $this->getShardToSync($user); /** @var $connection DoctrineDBALConnection */ $connection = $this->doctrine->getConnection($this- >getConnectionNameFromShard($shard)); $this->syncUser($user, $connection); }
  • 43. 500 Connessioni nuvolamc12345678: driver: '%database_driver%' host: '%database_host%' port: '%database_port%' dbname: nuvolamc12345678 user: '%database_user%' password: '%database_password%' charset: UTF8 nuvolamcps015006: driver: '%database_driver%' host: '%database_host%' port: '%database_port%' dbname: nuvolamcps015006 user: '%database_user%' password: '%database_password%' charset: UTF8
  • 45. Switch della connessione private function selectDbForIstituto( Istituto $istituto, SessionInterface $session ) { $shardManager = $this->get('shard_manager'); $shardManager->selectShard($istituto); ! $session->set('shard', $istituto->getShardId()); }
  • 46. Switch della connessione public function onKernelRequest(GetResponseEvent $event) { if (!$event->isMasterRequest()) { return; } ! $this->shardManager->selectShard( $this->session->get(‘shard') ); }
  • 48. Configurare gli shards protected function configure() { $this->setName('nuvola:shard:add-config') ->setDescription('Aggiunge la configurazione necessaria ad uno shard') ->addOption('host', null, InputOption::VALUE_OPTIONAL, 'L'host della connessione al db') ->addOption('codiceMeccanografico', null, InputOption::VALUE_OPTIONAL, 'Codice meccanografico per lo shard'); }
  • 49. Configurare gli shards protected function configure() { $this->setName('nuvola:shard:create-config') ->setDescription('Crea il file di configurazione per gli shards') ->addOption( 'append', null, InputOption::VALUE_NONE, 'Se impostato a false cancella la configurazione attuale, altrimenit la aggiunge. Default a true' ) //CUT }
  • 50. Ad ognuno il suo shard public function onConsoleCommand(ConsoleCommandEvent $event) { $shardManager = new SafeShardManager($connection); $istituto = $input->getParameterOption(['--istituto', '-i']); ! if ('global' === $istituto) { $shardManager->selectGlobal(); } else { $shardManager->selectShard($istituto); } ! }
  • 51. Ciclare gli shards class ListShardsCommand extends AbstractShardCommand { protected function configure() { $this->setName('nuvola:shard:list-shards') ->setDescription('Restituisce l'elenco degli shard configurati') ->addOption( 'letteraInizioIntervallo', null, InputOption::VALUE_OPTIONAL, 'Lettera di inizio intervallo per lo shard da esportare (estremo compreso)' ) ->addOption( 'letteraFineIntervallo', null, InputOption::VALUE_OPTIONAL, 'Lettera di fine intervallo per lo shard da esportare (estremo compreso)' ); } }
  • 52. Ciclare gli shards app/console nu:sha:li | while read shard; do app/console doctrine:mig:mig - i $shard -n;done;
  • 53. Parallelizzare FTW class MigrateCommand extends AbstractParallelCommand { protected function execute(InputInterface $input, OutputInterface $output) { /** @var GearmanClient $gearman */ $gearman = $this->getContainer()->get('gearman'); //[CUT] foreach ($shards as $shard) { $job = 'NuvolaMultiDbBundleWorkerShardWorker~migrate' . $shard['queue']; $gearman->addTask($job, $shard['shard']); } ! $gearman->runTasks(); } }
  • 54. Parallelizzare FTW protected function doMigrate(GearmanJob $job) { $shard = $job->workload(); $command = sprintf( 'app/console doctrine:migrations:migrate -n -i %s --env=%s', $shard, $this->env ); ! $process = $this->runProcess($job, $command); ! if (!$process->isSuccessful()) { $this->sendErrorsToJob($job, $process, $command, 'Errore migrando ' . $shard); return; } ! $success = [sprintf('Migrazione per %s completata', $shard)]; $job->sendComplete(serialize($success)); ! return; }
  • 55. Parallelizzare FTW app/console gearman:job:execute NuvolaMultiDbBundleWorkerShardWorker~mig rate0 -n —env=prod
  • 57. Fa al caso tuo?