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

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

Embed Size (px)

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.

Citation preview

Page 1: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

SaaS con Symfony2un caso *molto* concreto di applicazione multitenant

Page 2: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

@ftassi Francesco Tassi

@matteomoretti85 Matteo Moretti

Page 3: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant
Page 4: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Nuvola e i suoi 50GB

Page 5: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

Applicazioni multi tenant

Page 7: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

– 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

Sharding

Page 9: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

– 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

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

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

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

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

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

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

Sharding con Doctrine

Page 17: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

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
Page 21: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Il Pianohttps://www.flickr.com/photos/reallyboring/3234624436

Page 22: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

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

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

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
Page 28: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Strategia di frazionamento

Page 29: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

KEEP CALMAND

SPLIT YOUR DATA

Page 30: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

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

Strategia di selezione del DB

Page 34: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Sottodominio?

Page 35: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Sottodominio

Page 36: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Chiedilo all’utente

Page 37: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

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

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

Sincronizzazione$this ->eventDispatcher ->dispatch( MultiDbSyncEntityEvent::SYNC_UTENTE, new MultiDbSyncEntityEvent($utente) );

(onFlush, prePersist)

Page 42: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

Switch della connessione

Page 45: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

Gestire 500 DBHelp needed

https://www.flickr.com/photos/jdhancock/8671399450/

Page 48: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

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

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

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

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

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

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

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

Parallelizzare FTW

app/console gearman:job:execute NuvolaMultiDbBundleWorkerShardWorker~migrate0 -n —env=prod

Page 56: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Conclusioni

Page 57: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Fa al caso tuo?

Page 58: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Si, lo rifarei

Page 59: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Domande?

Page 60: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

https://joind.in/12212

Page 61: SaaS con Symfony2 un caso *molto* concreto di applicazione multitenant

Thanks