From d3aecc718d9a3839d6dca5c9d6dcd3b60495867c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Jakes=CC=8C?= Date: Thu, 31 Oct 2019 14:26:50 +0100 Subject: [PATCH] Add temporary fix for race conditions [MAILPOET-2436] --- lib/Settings/SettingsController.php | 13 +------------ lib/Settings/SettingsRepository.php | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/Settings/SettingsController.php b/lib/Settings/SettingsController.php index c91456f8d3..7baf8604e4 100644 --- a/lib/Settings/SettingsController.php +++ b/lib/Settings/SettingsController.php @@ -109,7 +109,7 @@ class SettingsController { } } $setting[$last_key] = $value; - $this->saveValue($main_key, $this->settings[$main_key]); + $this->settings_repository->createOrUpdateByName($main_key, $this->settings[$main_key]); } function delete($key) { @@ -150,17 +150,6 @@ class SettingsController { return $setting ? $setting->getValue() : null; } - private function saveValue($key, $value) { - $setting = $this->settings_repository->findOneByName($key); - if (!$setting) { - $setting = new SettingEntity(); - $setting->setName($key); - $this->settings_repository->persist($setting); - } - $setting->setValue($value); - $this->settings_repository->flush(); - } - function resetCache() { $this->settings = []; $this->loaded = false; diff --git a/lib/Settings/SettingsRepository.php b/lib/Settings/SettingsRepository.php index 1c18a18c3b..c420ae60e3 100644 --- a/lib/Settings/SettingsRepository.php +++ b/lib/Settings/SettingsRepository.php @@ -2,8 +2,10 @@ namespace MailPoet\Settings; +use Carbon\Carbon; use MailPoet\Doctrine\Repository; use MailPoet\Entities\SettingEntity; +use MailPoet\WP\Functions as WPFunctions; /** * @method SettingEntity[] findBy(array $criteria, array $order_by = null, int $limit = null, int $offset = null) @@ -18,6 +20,24 @@ class SettingsRepository extends Repository { return $this->doctrine_repository->findOneBy(['name' => $name]); } + function createOrUpdateByName($name, $value) { + // Temporarily use low-level INSERT ... ON DUPLICATE KEY UPDATE query to avoid race conditions + // between entity fetch and creation with multiple concurrent requests. This will be replaced + // by a code solving atomicity of create-or-update on entity (ORM) level in a follow-up ticket. + $now = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); + $table_name = $this->entity_manager->getClassMetadata(SettingEntity::class)->getTableName(); + $this->entity_manager->getConnection()->executeUpdate(" + INSERT INTO $table_name (name, value, created_at, updated_at) + VALUES (:name, :value, :now, :now) + ON DUPLICATE KEY UPDATE value = :value, updated_at = :now + ", [ + 'name' => $name, + 'value' => is_array($value) ? serialize($value) : $value, + 'now' => $now, + ]); + $this->entity_manager->clear(SettingEntity::class); + } + protected function getEntityClassName() { return SettingEntity::class; }