Add service to clean expired locks and related tests

- Introduced `CleanOldLock` service to remove expired locks older than 24 hours.
- Added `removeExpiredBefore` method in `StoredObjectLockRepository` for efficient deletion of expired locks.
- Created `CleanOldLockTest` to verify the cleaning service functionality using `MockClock`.
This commit is contained in:
2026-04-08 22:26:27 +02:00
parent 4afdc9a7cc
commit 678ec844e2
3 changed files with 130 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocStoreBundle\Repository;
use Chill\DocStoreBundle\Entity\StoredObjectLock;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<StoredObjectLock>
*/
class StoredObjectLockRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $manager)
{
parent::__construct($manager, StoredObjectLock::class);
}
public function removeExpiredBefore(\DateTimeImmutable $dateTime): void
{
$qb = $this->createQueryBuilder('sol');
$qb->delete()
->where($qb->expr()->lt('sol.expireAt', ':at'))
->andWhere($qb->expr()->isNotNull('sol.expireAt'))
->setParameter('at', $dateTime, Types::DATETIMETZ_IMMUTABLE)
->getQuery()
->execute();
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocStoreBundle\Service\Lock;
use Chill\DocStoreBundle\Repository\StoredObjectLockRepository;
use Symfony\Component\Clock\ClockInterface;
class CleanOldLock
{
private readonly \DateInterval $cleanBefore;
public function __construct(
private readonly StoredObjectLockRepository $storedObjectLockRepository,
private readonly ClockInterface $clock,
) {
$this->cleanBefore = new \DateInterval('PT24H');
}
public function __invoke(): void
{
$before = $this->clock->now()->sub($this->cleanBefore);
$this->storedObjectLockRepository->removeExpiredBefore($before);
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocStoreBundle\Tests\Service\Lock;
use Chill\DocStoreBundle\Repository\StoredObjectLockRepository;
use Chill\DocStoreBundle\Service\Lock\CleanOldLock;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\Clock\MockClock;
/**
* @covers \Chill\DocStoreBundle\Service\Lock\CleanOldLock
*
* @internal
*/
class CleanOldLockTest extends TestCase
{
use ProphecyTrait;
private MockClock $clock;
/**
* @var ObjectProphecy<StoredObjectLockRepository>
*/
private ObjectProphecy $repository;
private CleanOldLock $service;
protected function setUp(): void
{
$this->clock = new MockClock('2024-01-02 12:00:00');
$this->repository = $this->prophesize(StoredObjectLockRepository::class);
$this->service = new CleanOldLock(
$this->repository->reveal(),
$this->clock
);
}
public function testInvoke(): void
{
$expectedBefore = $this->clock->now()->sub(new \DateInterval('PT24H'));
$this->repository->removeExpiredBefore($expectedBefore)->shouldBeCalled();
($this->service)();
}
}