Skip to content

Commit 41c2980

Browse files
authored
Merge pull request #17 from michaelfeinbier/cli-performance
CLI performance on batch encryption
2 parents ab77996 + 14850a2 commit 41c2980

File tree

1 file changed

+63
-30
lines changed

1 file changed

+63
-30
lines changed

Command/DoctrineEncryptDatabaseCommand.php

Lines changed: 63 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
namespace Ambta\DoctrineEncryptBundle\Command;
44

55
use Ambta\DoctrineEncryptBundle\DependencyInjection\DoctrineEncryptExtension;
6-
use Doctrine\Common\Annotations\AnnotationReader;
6+
use Doctrine\ORM\EntityManager;
77
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
8+
use Symfony\Component\Console\Helper\ProgressBar;
89
use Symfony\Component\Console\Input\InputArgument;
910
use Symfony\Component\Console\Input\InputInterface;
1011
use Symfony\Component\Console\Output\OutputInterface;
1112
use Symfony\Component\Console\Question\ConfirmationQuestion;
1213

1314
/**
14-
* Hello World command for demo purposes.
15-
*
15+
* Batch encryption for the database
1616
*
1717
* @author Marcel van Nuil <marcel@ambta.com>
18+
* @author Michael Feinbier <michael@feinbier.net>
1819
*/
1920
class DoctrineEncryptDatabaseCommand extends ContainerAwareCommand
2021
{
@@ -26,8 +27,10 @@ protected function configure()
2627
{
2728
$this
2829
->setName('doctrine:encrypt:database')
29-
->setDescription('Decrypt whole database on tables which are encrypted')
30-
->addArgument("encryptor", InputArgument::OPTIONAL, "The encryptor u want to decrypt the database with");
30+
->setDescription('Encrypt whole database on tables which are not encrypted yet')
31+
->addArgument('encryptor', InputArgument::OPTIONAL, 'The encryptor you want to decrypt the database with')
32+
->addArgument('batchSize', InputArgument::OPTIONAL, 'The update/flush batch size', 20);
33+
3134
}
3235

3336
/**
@@ -39,7 +42,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
3942
$entityManager = $this->getContainer()->get('doctrine.orm.entity_manager');
4043
$question = $this->getHelper('question');
4144
$subscriber = $this->getContainer()->get('ambta_doctrine_encrypt.subscriber');
42-
$annotationReader = new AnnotationReader();
45+
$annotationReader = $this->getContainer()->get('annotation_reader');
46+
$batchSize = $input->getArgument('batchSize');
4347

4448
//Get list of supported encryptors
4549
$supportedExtensions = DoctrineEncryptExtension::$supportedEncryptorClasses;
@@ -53,9 +57,9 @@ protected function execute(InputInterface $input, OutputInterface $output)
5357
{
5458
$subscriber->setEncryptor($input->getArgument('encryptor'));
5559
} else {
56-
$output->writeln("\nGiven encryptor does not exists");
57-
$output->writeln("Supported encryptors: " . implode(", ", array_keys($supportedExtensions)));
58-
$output->writeln("You can also define your own class. (example: Ambta\DoctrineEncryptBundle\Encryptors\Rijndael128Encryptor)");
60+
$output->writeln('\nGiven encryptor does not exists');
61+
$output->writeln('Supported encryptors: ' . implode(', ', array_keys($supportedExtensions)));
62+
$output->writeln('You can also define your own class. (example: Ambta\DoctrineEncryptBundle\Encryptors\Rijndael128Encryptor)');
5963
return;
6064
}
6165
}
@@ -77,20 +81,20 @@ protected function execute(InputInterface $input, OutputInterface $output)
7781

7882
//Count propperties in metadata
7983
foreach($propertyArray as $property) {
80-
if($annotationReader->getPropertyAnnotation($property, "Ambta\DoctrineEncryptBundle\Configuration\Encrypted")) {
84+
if($annotationReader->getPropertyAnnotation($property, 'Ambta\DoctrineEncryptBundle\Configuration\Encrypted')) {
8185
$propertyCount++;
8286
}
8387
}
8488
}
8589

86-
$confirmationQuestion = new ConfirmationQuestion("<question>\n" . count($metaDataArray) . " entitys found which are containing " . $propertyCount . " properties with the encryption tag. \n\nWhich are going to be encrypted with [" . $subscriber->getEncryptor() . "]. \n\nWrong settings can mess up your data and it will be unrecoverable. \nI advise you to make <bg=yellow;options=bold>a backup</bg=yellow;options=bold>. \n\nContinu with this action? (y/yes)</question>", false);
90+
$confirmationQuestion = new ConfirmationQuestion("<question>\n" . count($metaDataArray) . " entities found which are containing " . $propertyCount . " properties with the encryption tag. \n\nWhich are going to be encrypted with [" . $subscriber->getEncryptor() . "]. \n\nWrong settings can mess up your data and it will be unrecoverable. \nI advise you to make <bg=yellow;options=bold>a backup</bg=yellow;options=bold>. \n\nContinue with this action? (y/yes)</question>", false);
8791

8892
if (!$question->ask($input, $output, $confirmationQuestion)) {
8993
return;
9094
}
9195

9296
//Start decrypting database
93-
$output->writeln("\nEncrypting all fields this can take up to several minutes depending on the database size.");
97+
$output->writeln("\nEncrypting all fields can take up to several minutes depending on the database size.");
9498

9599
//Loop through entity manager meta data
96100
foreach($metaDataArray as $metaData) {
@@ -105,7 +109,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
105109

106110
//Count propperties in metadata
107111
foreach ($propertyArray as $property) {
108-
if ($annotationReader->getPropertyAnnotation($property, "Ambta\DoctrineEncryptBundle\Configuration\Encrypted")) {
112+
if ($annotationReader->getPropertyAnnotation($property, 'Ambta\DoctrineEncryptBundle\Configuration\Encrypted')) {
109113
$propertyCount++;
110114
}
111115
}
@@ -115,27 +119,56 @@ protected function execute(InputInterface $input, OutputInterface $output)
115119
}
116120

117121
//If class is not an superclass
118-
if (!$annotationReader->getClassAnnotation($reflectionClass, "Doctrine\ORM\Mapping\MappedSuperclass")) {
119-
120-
/**
121-
* Get repository and entity Array
122-
* @var \Doctrine\ORM\EntityRepository $repository
123-
*/
124-
$repository = $entityManager->getRepository($metaData->name);
125-
$entityArray = $repository->findAll();
126-
127-
foreach($entityArray as $entity) {
128-
129-
$entity = $subscriber->processFields($entity);
130-
131-
//Persist and flush entity
132-
$entityManager->persist($entity);
133-
$entityManager->flush($entity);
122+
$i = 0;
123+
if (!$annotationReader->getClassAnnotation($reflectionClass, 'Doctrine\ORM\Mapping\MappedSuperclass')) {
124+
$iterator = $this->getEntityIterator($entityManager, $metaData->name);
125+
$totalCount = $this->getTableCount($entityManager, $metaData->name);
126+
127+
$output->writeln(sprintf('Processing <comment>%s</comment>', $metaData->name));
128+
$progressBar = new ProgressBar($output, $totalCount);
129+
foreach ($iterator as $row) {
130+
$subscriber->processFields($row[0]);
131+
132+
if (($i % $batchSize) === 0) {
133+
$entityManager->flush();
134+
$entityManager->clear();
135+
$progressBar->advance($batchSize);
136+
}
137+
$i++;
134138
}
139+
140+
$progressBar->finish();
141+
$output->writeln('');
142+
$entityManager->flush();
135143
}
136144
}
137145

138146
//Say it is finished
139-
$output->writeln("\nEncryption finished values encrypted: " . $subscriber->encryptCounter . " values.\nAll values are now encrypted.");
147+
$output->writeln("\nEncryption finished. Values encrypted: <info>" . $subscriber->encryptCounter . " values</info>.\nAll values are now encrypted.");
148+
}
149+
150+
/**
151+
* @param EntityManager $em
152+
* @param $name
153+
*
154+
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
155+
*/
156+
protected function getEntityIterator(EntityManager $em, $name)
157+
{
158+
$query = $em->createQuery(sprintf('SELECT o FROM %s o', $name));
159+
return $query->iterate();
160+
}
161+
162+
/**
163+
* @param EntityManager $manager
164+
* @param $name
165+
*
166+
* @return integer
167+
*/
168+
protected function getTableCount(EntityManager $manager, $name)
169+
{
170+
$query = $manager->createQuery(sprintf('SELECT COUNT(o) FROM %s o', $name));
171+
172+
return (int) $query->getSingleScalarResult();
140173
}
141174
}

0 commit comments

Comments
 (0)