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

  • Login to see the comments

SaaS con Symfony2

  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. Hiring! http://www.ideato.it/offerta-cercasi-sviluppatore- php/
  59. 59. Thanks

×