diff --git a/apigee_edge.module b/apigee_edge.module index 5bbe8620d..5d9e9fa84 100644 --- a/apigee_edge.module +++ b/apigee_edge.module @@ -1365,6 +1365,7 @@ function apigee_edge_user_presave(UserInterface $account) { } $developer = $result->getDeveloper(); $developer->save(); + \Drupal::getContainer()->get('apigee_edge.dev_sync.last_update_tracker')->set($developer->getEmail(), \Drupal::time()->getCurrentTime()); } catch (\Exception $exception) { $previous = $exception->getPrevious(); @@ -1386,6 +1387,7 @@ function apigee_edge_user_presave(UserInterface $account) { // "originalEmail" property's value on the entity. $developer->enforceIsNew(TRUE); $developer->save(); + \Drupal::getContainer()->get('apigee_edge.dev_sync.last_update_tracker')->set($developer->getEmail(), \Drupal::time()->getCurrentTime()); } catch (\Exception $exception) { $context = [ @@ -1410,6 +1412,7 @@ function apigee_edge_user_presave(UserInterface $account) { $developer->enforceIsNew(FALSE); try { $developer->save(); + \Drupal::getContainer()->get('apigee_edge.dev_sync.last_update_tracker')->set($result->getDeveloper()->getEmail(), \Drupal::time()->getCurrentTime()); } catch (ApiException $exception) { $logger->error("Unable to update existing @developer developer's data after registered on the portal.", $context); diff --git a/apigee_edge.services.yml b/apigee_edge.services.yml index 82334a68b..599a5868a 100644 --- a/apigee_edge.services.yml +++ b/apigee_edge.services.yml @@ -217,3 +217,8 @@ services: apigee_edge.post_user_delete_action_performer: class: Drupal\apigee_edge\User\RemoveRelatedDeveloperAccountSynchronousPostUserDeleteActionPerformer arguments: ['@entity_type.manager', '@logger.channel.apigee_edge'] + + apigee_edge.dev_sync.last_update_tracker: + class: Drupal\Core\KeyValueStore\KeyValueStoreInterface + factory: [ '@keyvalue', 'get' ] + arguments: [ 'apigee_edge.dev_sync.last_update_tracker' ] diff --git a/src/Job/DeveloperCreateUpdate.php b/src/Job/DeveloperCreateUpdate.php index 191ff6df8..804b16695 100644 --- a/src/Job/DeveloperCreateUpdate.php +++ b/src/Job/DeveloperCreateUpdate.php @@ -70,6 +70,11 @@ protected function executeRequest() { if ($result->getSuccessfullyAppliedChanges() > 0) { $result->getDeveloper()->save(); } + // Record the sync attempt timestamp for the developer's email, + // regardless of whether an entity update occurred, to prevent redundant + // sync operations. This ensures that the developer will only be re-synced + // if a new relevant change is detected after this timestamp. + \Drupal::getContainer()->get('apigee_edge.dev_sync.last_update_tracker')->set($result->getDeveloper()->getEmail(), \Drupal::time()->getCurrentTime()); } catch (\Exception $exception) { $message = '@operation: Skipping %mail developer. @message %function (line %line of %file).
@backtrace_string'; diff --git a/src/Job/DeveloperSync.php b/src/Job/DeveloperSync.php index 5ce6eb7ab..390a6cbe5 100644 --- a/src/Job/DeveloperSync.php +++ b/src/Job/DeveloperSync.php @@ -20,7 +20,10 @@ namespace Drupal\apigee_edge\Job; use Drupal\apigee_edge\Entity\Developer; +use Drupal\apigee_edge\Entity\DeveloperInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreInterface; use Drupal\user\Entity\User; +use Drupal\user\UserInterface; /** * A job that synchronizes Apigee Edge developers and Drupal users. @@ -58,6 +61,11 @@ class DeveloperSync extends EdgeJob { */ protected $filter = NULL; + /** + * KV store tracks last update attempts for each user/developer. + */ + protected KeyValueStoreInterface $lastUpdateTracker; + /** * DeveloperSync constructor. * @@ -67,6 +75,7 @@ class DeveloperSync extends EdgeJob { public function __construct(?string $filter) { parent::__construct(); $this->filter = $filter; + $this->lastUpdateTracker = \Drupal::service('apigee_edge.dev_sync.last_update_tracker'); } /** @@ -130,6 +139,30 @@ protected function executeRequest() { $this->edgeDevelopers = $this->loadDevelopers(); } + /** + * Schedules the update of a user. + * + * @param \Drupal\user\UserInterface $user + * The user. + */ + protected function scheduleUserUpdate(UserInterface $user): void { + $update_user_job = new UserUpdate($user->getEmail()); + $update_user_job->setTag($this->getTag()); + $this->scheduleJob($update_user_job); + } + + /** + * Schedules the update of a developer. + * + * @param \Drupal\apigee_edge\Entity\DeveloperInterface $developer + * The developer. + */ + protected function scheduleDeveloper(DeveloperInterface $developer): void { + $update_developer_job = new DeveloperUpdate($developer->getEmail()); + $update_developer_job->setTag($this->getTag()); + $this->scheduleJob($update_developer_job); + } + /** * {@inheritdoc} */ @@ -144,20 +177,31 @@ public function execute(): bool { /** @var \Drupal\user\UserInterface $user */ $user = $this->drupalUsers[$clean_email]; - $last_modified_delta = $developer->getLastModifiedAt()->getTimestamp() - $user->getChangedTime(); - // Update Drupal user because the Apigee Edge developer is the most - // recent. - if ($last_modified_delta >= 0) { - $update_user_job = new UserUpdate($user->getEmail()); - $update_user_job->setTag($this->getTag()); - $this->scheduleJob($update_user_job); + $last_synced = $this->lastUpdateTracker->get($developer->getEmail(), 0); + + if ($last_synced === 0) { + $last_modified_delta = $developer->getLastModifiedAt()->getTimestamp() - $user->getChangedTime(); + if ($last_modified_delta === 0) { + $this->lastUpdateTracker->set($developer->getEmail(), \Drupal::time()->getCurrentTime()); + continue; + } + + // Update Drupal user because the Apigee Edge developer is the most + // recent. + if ($last_modified_delta > 0) { + $this->scheduleUserUpdate($user); + } + // Update Apigee Edge developer because the Drupal user is the most + // recent. + else { + $this->scheduleDeveloper($developer); + } + } + elseif ($last_synced < $developer->getLastModifiedAt()->getTimestamp()) { + $this->scheduleUserUpdate($user); } - // Update Apigee Edge developer because the Drupal user is the most - // recent. - elseif ($last_modified_delta < 0) { - $update_developer_job = new DeveloperUpdate($developer->getEmail()); - $update_developer_job->setTag($this->getTag()); - $this->scheduleJob($update_developer_job); + elseif ($last_synced < $user->getChangedTime()) { + $this->scheduleDeveloper($developer); } } diff --git a/src/Job/UserCreateUpdate.php b/src/Job/UserCreateUpdate.php index df51748db..9a5a9c9da 100644 --- a/src/Job/UserCreateUpdate.php +++ b/src/Job/UserCreateUpdate.php @@ -77,6 +77,12 @@ protected function executeRequest() { _apigee_edge_set_sync_in_progress(TRUE); $result->getUser()->save(); } + + // Record the sync attempt timestamp for the developer's email, + // regardless of whether an entity update occurred, to prevent redundant + // sync operations. This ensures that the developer will only be re-synced + // if a new relevant change is detected after this timestamp. + \Drupal::getContainer()->get('apigee_edge.dev_sync.last_update_tracker')->set($developer->getEmail(), \Drupal::time()->getCurrentTime()); } catch (\Exception $exception) { $message = '@operation: Skipping %mail user. @message %function (line %line of %file).
@backtrace_string'; @@ -85,8 +91,8 @@ protected function executeRequest() { '@operation' => get_class($this), ]; $context += Error::decodeException($exception); - // Unset backtrace, exception, severity_level as they are not shown in the log message - // and throws php warning in logs. + // Unset backtrace, exception, severity_level as they are not shown in + // the log message and throws php warning in logs. unset($context['backtrace'], $context['exception'], $context['severity_level']); $this->logger()->error($message, $context);