Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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) 
•...
Applicazioni multi 
tenant
Applicazioni multi 
tenant 
“Multi-tenant si riferisce ad una architettura 
software in cui una singola istanza del suddet...
Sharding
Sharding 
A database shard is a horizontal partition of data 
in a database. Each individual partition is referred 
to as ...
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 ooc...
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 
horizo...
Sharding con Doctrine 
At the moment there are no functionalities yet to 
dynamically pick a shard based on ID, query or 
...
ShardManager Interface 
$shardManager = new PoolingShardManager($conn); 
! 
$currentCustomerId = 1234; 
$shardManager->sel...
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 data...
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N data...
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N data...
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N data...
Il Piano 
Strategia di frazionamento 
Strategia di selezione del DB 
Switch della connessione 
Tool di gestione per N data...
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 ...
Strategia di frazionamento 
user_id istituto_id! username 
1 1 idiopathic 
2 2 bouffant 
3 1 skedaddle 
4 1 tweezers 
5 2 ...
Strategia di frazionamento 
user_id istituto_id! username 
1 1 idiopathic 
3 1 skedaddle 
4 1 tweezers 
6 1 foibles 
7 1 o...
Strategia di selezione 
del DB
Sottodominio?
Sottodominio
Chiedilo all’utente
Chiedilo all’utente 
• Login tramite database unico (default) ! 
• Selezione manuale dell’istituto 
• Switch della conness...
Una connessione “default” 
doctrine: 
dbal: 
default_connection: default 
connections: 
default: 
driver: "%database_drive...
500 Shards 
shards: 
mc12345678: 
id: 1 
host: '%database_host%' 
user: '%database_user%' 
password: '%database_password%'...
UUID 
user_id istituto_id! username uuid 
1 1 idiopathic 
e5f0b536- 
c4cd-47c4- 
a810- 
2 2 bouffant 
ea5d2eb4-851c 
-462d...
Sincronizzazione 
(onFlush, prePersist) 
$this 
->eventDispatcher 
->dispatch( 
MultiDbSyncEntityEvent::SYNC_UTENTE, 
new ...
Sincronizzazione 
public function onSyncUtente(MultiDbSyncEntityEvent 
$event) 
{ 
$user = $event->getEntity(); 
$shard = ...
500 Connessioni 
nuvolamc12345678: 
driver: '%database_driver%' 
host: '%database_host%' 
port: '%database_port%' 
dbname:...
Switch della 
connessione
Switch della connessione 
private function selectDbForIstituto( 
Istituto $istituto, 
SessionInterface $session 
) 
{ 
$sh...
Switch della connessione 
public function onKernelRequest(GetResponseEvent $event) 
{ 
if (!$event->isMasterRequest()) { 
...
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('Ag...
Configurare gli shards 
protected function configure() 
{ 
$this->setName('nuvola:shard:create-config') 
->setDescription(...
Ad ognuno il suo shard 
public function onConsoleCommand(ConsoleCommandEvent $event) 
{ 
$shardManager = new SafeShardMana...
Ciclare gli shards 
class ListShardsCommand extends AbstractShardCommand 
{ 
protected function configure() 
{ 
$this->set...
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 $in...
Parallelizzare FTW 
protected function doMigrate(GearmanJob $job) 
{ 
$shard = $job->workload(); 
$command = sprintf( 
'ap...
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
SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant
SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant
SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant
Upcoming SlideShare
Loading in …5
×

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

932 views

Published on

Sogni di sviluppare il tuo SaaS, di poterlo gestire, curare, evolvere. Speri di attrarre nuovi utenti con funzionalità innovative, di offrire un servizio veloce e puntuale. Finalmente ti puoi concentrare sulla qualità del tuo prodotto. Poi una mattina ti svegli, hai un database da 50GB, modificare una colonna richiede 8 ore e ti ritrovi in trappola. Gioie e dolori delle applicazioni multi-tenant. In questo talk analizzeremo perché e come abbiamo suddiviso il database di un SaaS da circa 1 milione di utenti.
> Vedremo come aggiungere un parametro di selezione del db a tutti i comandi della console, come eseguire comandi in parallelo per ridurre i tempi di manutenzione, come aggiungere info di debug utilizzando gli eventi del framework, come gestire il caricamento delle fixtures, quali idee si sono rivelate vincenti e quali no.

Published in: Technology
  • Hi Franceso, I'm developing an application with similar features, you could share with me some of your code to solve everything related to multitenancy? I'm having some problems and I can't progress. Thank you and sorry for my english
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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

  1. 1. SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant
  2. 2. @ftassi Francesco Tassi @matteomoretti85 Matteo Moretti
  3. 3. Nuvola e i suoi 50GB
  4. 4. Nuvola e i suoi 50GB • Difficoltà di manutenzione (backup/ripristino) • Difficoltà di evoluzione (alter dello schema) • Impossibile replicare il sistema (debug)
  5. 5. Applicazioni multi tenant
  6. 6. 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
  7. 7. Sharding
  8. 8. 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
  9. 9. Sharding user_id username 1 idiopathic 2 bouffant 3 skedaddle 4 tweezers 5 igloo 6 foibles 7 oocephalus
  10. 10. Sharding user_id username 1 idiopathic 2 bouffant 3 skedaddle 4 tweezers 5 igloo 6 foibles 7 oocephalus
  11. 11. 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
  12. 12. Vantaggi • Suddivide anche il carico di scrittura • Indici più piccoli • distribuzione dei dati migliore
  13. 13. Svantaggi • Difficile o impossibile effettuare query su shard differenti • Consistenza dei dati • Complessità extra
  14. 14. Supporto nativo http://en.wikipedia.org/wiki/ Shard_(database_architecture)#Support_for_sh ards
  15. 15. Sharding con Doctrine
  16. 16. 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
  17. 17. 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
  18. 18. 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.
  19. 19. https://www.flickr.com/photos/reallyboring/3234624436 Il Piano
  20. 20. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  21. 21. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  22. 22. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  23. 23. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  24. 24. Il Piano Strategia di frazionamento Strategia di selezione del DB Switch della connessione Tool di gestione per N databases
  25. 25. Strategia di frazionamento
  26. 26. KEEP CALM AND SPLIT YOUR DATA
  27. 27. 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
  28. 28. 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
  29. 29. 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
  30. 30. Strategia di selezione del DB
  31. 31. Sottodominio?
  32. 32. Sottodominio
  33. 33. Chiedilo all’utente
  34. 34. Chiedilo all’utente • Login tramite database unico (default) ! • Selezione manuale dell’istituto • Switch della connessione • Sincronizzazione dei dati duplicati
  35. 35. 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
  36. 36. 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
  37. 37. 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
  38. 38. Sincronizzazione (onFlush, prePersist) $this ->eventDispatcher ->dispatch( MultiDbSyncEntityEvent::SYNC_UTENTE, new MultiDbSyncEntityEvent($utente) );
  39. 39. 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); }
  40. 40. 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
  41. 41. Switch della connessione
  42. 42. Switch della connessione private function selectDbForIstituto( Istituto $istituto, SessionInterface $session ) { $shardManager = $this->get('shard_manager'); $shardManager->selectShard($istituto); ! $session->set('shard', $istituto->getShardId()); }
  43. 43. Switch della connessione public function onKernelRequest(GetResponseEvent $event) { if (!$event->isMasterRequest()) { return; } ! $this->shardManager->selectShard( $this->session->get(‘shard') ); }
  44. 44. https://www.flickr.com/photos/jdhancock/8671399450/ Gestire 500 DB Help needed
  45. 45. 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'); }
  46. 46. 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 }
  47. 47. 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); } ! }
  48. 48. 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)' ); } }
  49. 49. Ciclare gli shards app/console nu:sha:li | while read shard; do app/console doctrine:mig:mig - i $shard -n;done;
  50. 50. 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(); } }
  51. 51. 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; }
  52. 52. Parallelizzare FTW app/console gearman:job:execute NuvolaMultiDbBundleWorkerShardWorker~mig rate0 -n —env=prod
  53. 53. Conclusioni
  54. 54. Fa al caso tuo?
  55. 55. Si, lo rifarei
  56. 56. Domande?
  57. 57. https://joind.in/12212
  58. 58. Thanks

×