SaaS con Symfony2
- 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
- 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
- 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.
- 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
- 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
- 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;
}