diff options
Diffstat (limited to 'MLEB/Translate/src/Synchronization/GroupSynchronizationCache.php')
-rw-r--r-- | MLEB/Translate/src/Synchronization/GroupSynchronizationCache.php | 100 |
1 files changed, 90 insertions, 10 deletions
diff --git a/MLEB/Translate/src/Synchronization/GroupSynchronizationCache.php b/MLEB/Translate/src/Synchronization/GroupSynchronizationCache.php index 64ea5ad0..d581ff69 100644 --- a/MLEB/Translate/src/Synchronization/GroupSynchronizationCache.php +++ b/MLEB/Translate/src/Synchronization/GroupSynchronizationCache.php @@ -35,18 +35,28 @@ class GroupSynchronizationCache { /** @var PersistentCache */ private $cache; /** @var int */ - private $timeoutSeconds; + private $initialTimeoutSeconds; + /** @var int */ + private $incrementalTimeoutSeconds; /** @var string Cache tag used for groups */ private const GROUP_LIST_TAG = 'gsc_%group_in_sync%'; /** @var string Cache tag used for tracking groups that have errors */ private const GROUP_ERROR_TAG = 'gsc_%group_with_error%'; + /** @var string Cache tag used for tracking groups that are in review */ + private const GROUP_IN_REVIEW_TAG = 'gsc_%group_in_review%'; + + // The timeout is set to 40 minutes initially, and then incremented by 10 minutes + // each time a message is marked as processed if group is about to expire. + public function __construct( + PersistentCache $cache, + int $initialTimeoutSeconds = 2400, + int $incrementalTimeoutSeconds = 600 - // TODO: Decide timeout based on monitoring. Also check if it needs to be configurable - // based on the number of messages in the group. - public function __construct( PersistentCache $cache, int $timeoutSeconds = 2400 ) { + ) { $this->cache = $cache; - $this->timeoutSeconds = $timeoutSeconds; + $this->initialTimeoutSeconds = $initialTimeoutSeconds; + $this->incrementalTimeoutSeconds = $incrementalTimeoutSeconds; } /** @@ -66,7 +76,7 @@ class GroupSynchronizationCache { /** Start synchronization process for a group and starts the expiry time */ public function markGroupForSync( string $groupId ): void { - $expTime = $this->getExpireTime(); + $expTime = $this->getExpireTime( $this->initialTimeoutSeconds ); $this->cache->set( new PersistentCacheEntry( $this->getGroupKey( $groupId ), @@ -327,6 +337,12 @@ class GroupSynchronizationCache { $this->cache->delete( $messageErrorKey ); } + /** Checks if the group has errors */ + public function groupHasErrors( string $groupId ): bool { + $groupErrorKey = $this->getGroupErrorKey( $groupId ); + return $this->cache->has( $groupErrorKey ); + } + /** Checks if group has unresolved error messages. If not clears the group from error list */ public function syncGroupErrors( string $groupId ): GroupSynchronizationResponse { $groupSyncResponse = $this->getGroupErrorInfo( $groupId ); @@ -341,14 +357,75 @@ class GroupSynchronizationCache { return $groupSyncResponse; } + public function markGroupAsInReview( string $groupId ): void { + $groupReviewKey = $this->getGroupReviewKey( $groupId ); + $this->cache->set( + new PersistentCacheEntry( + $groupReviewKey, + $groupId, + null, + self::GROUP_IN_REVIEW_TAG + ) + ); + } + + public function markGroupAsReviewed( string $groupId ): void { + $groupReviewKey = $this->getGroupReviewKey( $groupId ); + $this->cache->delete( $groupReviewKey ); + } + + public function isGroupInReview( string $groupId ): bool { + return $this->cache->has( $this->getGroupReviewKey( $groupId ) ); + } + + public function extendGroupExpiryTime( string $groupId ): void { + $groupKey = $this->getGroupKey( $groupId ); + $groupEntry = $this->cache->get( $groupKey ); + + if ( $groupEntry === [] ) { + // Group is currently not being processed. + throw new LogicException( + 'Requested extension of expiry time for a group that is not being processed. ' . + 'Check if group is being processed by calling isGroupBeingProcessed() first' + ); + } + + if ( $groupEntry[0]->hasExpired() ) { + throw new InvalidArgumentException( + 'Cannot extend expiry time for a group that has already expired.' + ); + } + + $newExpiryTime = $this->getExpireTime( $this->incrementalTimeoutSeconds ); + + // We start with the initial timeout minutes, we only change the timeout if the group + // is actually about to expire. + if ( $newExpiryTime < $groupEntry[0]->exptime() ) { + return; + } + + $this->cache->setExpiry( $groupKey, $newExpiryTime ); + } + + /** @internal - Internal; For testing use only */ + public function getGroupExpiryTime( $groupId ): int { + $groupKey = $this->getGroupKey( $groupId ); + $groupEntry = $this->cache->get( $groupKey ); + if ( $groupEntry === [] ) { + throw new InvalidArgumentException( "$groupId currently not in processing!" ); + } + + return $groupEntry[0]->exptime(); + } + private function hasGroupTimedOut( int $syncExpTime ): bool { return ( new DateTime() )->getTimestamp() > $syncExpTime; } - private function getExpireTime(): int { + private function getExpireTime( int $timeoutSeconds ): int { $currentTime = ( new DateTime() )->getTimestamp(); $expTime = ( new DateTime() ) - ->setTimestamp( $currentTime + $this->timeoutSeconds ) + ->setTimestamp( $currentTime + $timeoutSeconds ) ->getTimestamp(); return $expTime; @@ -404,6 +481,9 @@ class GroupSynchronizationCache { private function getGroupMessageErrorTag( string $groupId ): string { return "gsc_%error%_$groupId"; } -} -class_alias( GroupSynchronizationCache::class, '\MediaWiki\Extensions\Translate\GroupSynchronizationCache' ); + private function getGroupReviewKey( string $groupId ): string { + $hash = substr( hash( 'sha256', $groupId ), 0, 40 ); + return substr( "{$hash}_gsc_%review%_$groupId", 0, 255 ); + } +} |