saas con symfony2 un caso *molto* concreto di applicazione multitenant
DESCRIPTION
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.TRANSCRIPT
![Page 1: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/1.jpg)
SaaS con Symfony2un caso *molto* concreto di applicazione multitenant
![Page 2: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/2.jpg)
@ftassi Francesco Tassi
@matteomoretti85 Matteo Moretti
![Page 3: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/3.jpg)
![Page 4: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/4.jpg)
Nuvola e i suoi 50GB
![Page 5: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/5.jpg)
Nuvola e i suoi 50GB• Difficoltà di manutenzione (backup/ripristino)
• Difficoltà di evoluzione (alter dello schema)
• Impossibile replicare il sistema (debug)
![Page 6: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/6.jpg)
Applicazioni multi tenant
![Page 7: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/7.jpg)
– wikipedia
“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)”.
Applicazioni multi tenant
![Page 8: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/8.jpg)
Sharding
![Page 9: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/9.jpg)
– wikipedia
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.
Sharding
![Page 10: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/10.jpg)
Shardinguser_id username
1 idiopathic
2 bouffant
3 skedaddle
4 tweezers
5 igloo
6 foibles
7 oocephalus
![Page 11: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/11.jpg)
Shardinguser_id username
1 idiopathic
2 bouffant
3 skedaddle
4 tweezers
5 igloo
6 foibles
7 oocephalus
![Page 12: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/12.jpg)
Shardinguser_id username
1 idiopathic2 bouffant3 skedaddle
user_id username
4 tweezers
5 igloo
6 foibles
7 oocephalus
Shard 1
Shard 2
![Page 13: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/13.jpg)
Vantaggi• Suddivide anche il carico di scrittura
• Indici più piccoli
• distribuzione dei dati migliore
![Page 14: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/14.jpg)
Svantaggi• Difficile o impossibile effettuare query su shard
differenti
• Consistenza dei dati
• Complessità extra
![Page 15: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/15.jpg)
http://en.wikipedia.org/wiki/Shard_(database_architecture)#Support_for_sh
ards
Supporto nativo
![Page 16: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/16.jpg)
Sharding con Doctrine
![Page 17: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/17.jpg)
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.
Sharding con Doctrine
- http://doctrine-dbal.readthedocs.org/en/latest/reference/sharding.html
![Page 18: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/18.jpg)
At the moment there are no functionalities yet to dynamically pick a shard based on ID, query or
database row yet
Sharding con Doctrine
- http://doctrine-dbal.readthedocs.org/en/latest/reference/sharding.html
![Page 19: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/19.jpg)
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.
![Page 20: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/20.jpg)
![Page 21: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/21.jpg)
Il Pianohttps://www.flickr.com/photos/reallyboring/3234624436
![Page 22: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/22.jpg)
Il PianoStrategia di frazionamento
Strategia di selezione del DB
Switch della connessione
Tool di gestione per N databases
![Page 23: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/23.jpg)
Il PianoStrategia di frazionamento
Strategia di selezione del DB
Switch della connessione
Tool di gestione per N databases
![Page 24: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/24.jpg)
Il PianoStrategia di frazionamento
Strategia di selezione del DB
Switch della connessione
Tool di gestione per N databases
![Page 25: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/25.jpg)
Il PianoStrategia di frazionamento
Strategia di selezione del DB
Switch della connessione
Tool di gestione per N databases
![Page 26: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/26.jpg)
Il PianoStrategia di frazionamento
Strategia di selezione del DB
Switch della connessione
Tool di gestione per N databases
![Page 27: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/27.jpg)
![Page 28: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/28.jpg)
Strategia di frazionamento
![Page 29: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/29.jpg)
KEEP CALMAND
SPLIT YOUR DATA
![Page 30: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/30.jpg)
Strategia di frazionamentouser_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
![Page 31: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/31.jpg)
Strategia di frazionamentouser_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
![Page 32: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/32.jpg)
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
![Page 33: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/33.jpg)
Strategia di selezione del DB
![Page 34: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/34.jpg)
Sottodominio?
![Page 35: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/35.jpg)
Sottodominio
![Page 36: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/36.jpg)
Chiedilo all’utente
![Page 37: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/37.jpg)
Chiedilo all’utente• Login tramite database unico (default) !
• Selezione manuale dell’istituto
• Switch della connessione
• Sincronizzazione dei dati duplicati
![Page 38: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/38.jpg)
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
Una connessione “default”
![Page 39: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/39.jpg)
500 Shardsshards: 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
![Page 40: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/40.jpg)
UUIDuser_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-
cd4a3d449c464 1 tweezers cd5759ae-7a7e
-42d1-b4cf-0cd0701b
5 2 igloo 64976e7a-54d2-4230-a8ef-
d624dc320cee6 1 foibles 202528c0-7028
-4a6f-9c0b-e97c6544693c
7 1 oocephalus 30bd250c-0a9c-4cf2-
a54c-020804d1
![Page 41: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/41.jpg)
Sincronizzazione$this ->eventDispatcher ->dispatch( MultiDbSyncEntityEvent::SYNC_UTENTE, new MultiDbSyncEntityEvent($utente) );
(onFlush, prePersist)
![Page 42: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/42.jpg)
Sincronizzazionepublic function onSyncUtente(MultiDbSyncEntityEvent $event){ $user = $event->getEntity(); $shard = $this->getShardToSync($user); /** @var $connection \Doctrine\DBAL\Connection */ $connection = $this->doctrine->getConnection($this->getConnectionNameFromShard($shard)); $this->syncUser($user, $connection);}
![Page 43: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/43.jpg)
500 Connessioninuvolamc12345678: driver: '%database_driver%' host: '%database_host%' port: '%database_port%' dbname: nuvolamc12345678 user: '%database_user%' password: '%database_password%' charset: UTF8nuvolamcps015006: driver: '%database_driver%' host: '%database_host%' port: '%database_port%' dbname: nuvolamcps015006 user: '%database_user%' password: '%database_password%' charset: UTF8
![Page 44: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/44.jpg)
Switch della connessione
![Page 45: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/45.jpg)
Switch della connessioneprivate function selectDbForIstituto( Istituto $istituto, SessionInterface $session){ $shardManager = $this->get('shard_manager'); $shardManager->selectShard($istituto);! $session->set('shard', $istituto->getShardId());}
![Page 46: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/46.jpg)
Switch della connessionepublic function onKernelRequest(GetResponseEvent $event){ if (!$event->isMasterRequest()) { return; }! $this->shardManager->selectShard( $this->session->get(‘shard') );}
![Page 47: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/47.jpg)
Gestire 500 DBHelp needed
https://www.flickr.com/photos/jdhancock/8671399450/
![Page 48: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/48.jpg)
Configurare gli shardsprotected 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');}
![Page 49: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/49.jpg)
Configurare gli shardsprotected 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}
![Page 50: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/50.jpg)
Ad ognuno il suo shardpublic function onConsoleCommand(ConsoleCommandEvent $event){ $shardManager = new SafeShardManager($connection); $istituto = $input->getParameterOption(['--istituto', '-i']);! if ('global' === $istituto) { $shardManager->selectGlobal(); } else { $shardManager->selectShard($istituto); }!}
![Page 51: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/51.jpg)
Ciclare gli shardsclass 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)' ); } }
![Page 52: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/52.jpg)
Ciclare gli shards
app/console nu:sha:li | while read shard; do app/console doctrine:mig:mig -i $shard -n;done;
![Page 53: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/53.jpg)
Parallelizzare FTWclass 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(); }}
![Page 54: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/54.jpg)
Parallelizzare FTWprotected 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;}
![Page 55: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/55.jpg)
Parallelizzare FTW
app/console gearman:job:execute NuvolaMultiDbBundleWorkerShardWorker~migrate0 -n —env=prod
![Page 56: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/56.jpg)
Conclusioni
![Page 57: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/57.jpg)
Fa al caso tuo?
![Page 58: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/58.jpg)
Si, lo rifarei
![Page 59: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/59.jpg)
Domande?
![Page 61: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant](https://reader034.vdocumenti.com/reader034/viewer/2022042700/5589509ed8b42a1e638b4620/html5/thumbnails/61.jpg)
Thanks