feat: improved contingents api performance with db backed snapshots

This commit is contained in:
Björn Fromme committed 2026-08-20 11:46:03 +02:00
1 parent d5f9f4ef10
commit 351fbab498
20 files changed
+1521 -109

No files matched your search

+174
View File
@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace App\Entity\Groups;
use App\Repository\Groups\ContingentSyncStateRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
/**
* Bookkeeping for the scheduled contingent sync of a single accommodation.
*
* Holds the fingerprint of the stored contingent days, which is how the sync decides whether
* anything actually changed, and `changedAt` records when it last did.
*/
#[ORM\Entity(repositoryClass: ContingentSyncStateRepository::class)]
#[ORM\Table(name: 'contingent_sync_state')]
class ContingentSyncState
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\OneToOne]
#[ORM\JoinColumn(nullable: false, unique: true, onDelete: 'CASCADE')]
private ?Accommodation $accommodation = null;
/**
* sha256 over the stored contingent days, contingent status only.
*/
#[ORM\Column(length: 64)]
private string $contentHash = '';
/**
* Last time the fingerprint actually changed. Kept for operators, not exposed via the API.
*/
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $changedAt = null;
/**
* Last successful sync, regardless of whether anything changed.
*/
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $syncedAt = null;
#[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $horizonTo = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $lastError = null;
#[ORM\Column]
private int $failureCount = 0;
public function __construct(?Accommodation $accommodation = null)
{
$this->accommodation = $accommodation;
}
public function getId(): ?int
{
return $this->id;
}
public function getAccommodation(): ?Accommodation
{
return $this->accommodation;
}
public function setAccommodation(?Accommodation $accommodation): self
{
$this->accommodation = $accommodation;
return $this;
}
public function getContentHash(): string
{
return $this->contentHash;
}
public function setContentHash(string $contentHash): self
{
$this->contentHash = $contentHash;
return $this;
}
public function getChangedAt(): ?\DateTimeImmutable
{
return $this->changedAt;
}
public function setChangedAt(?\DateTimeImmutable $changedAt): self
{
$this->changedAt = $changedAt;
return $this;
}
public function getSyncedAt(): ?\DateTimeImmutable
{
return $this->syncedAt;
}
public function setSyncedAt(?\DateTimeImmutable $syncedAt): self
{
$this->syncedAt = $syncedAt;
return $this;
}
public function getHorizonTo(): ?\DateTimeImmutable
{
return $this->horizonTo;
}
public function setHorizonTo(?\DateTimeImmutable $horizonTo): self
{
$this->horizonTo = $horizonTo;
return $this;
}
public function getLastError(): ?string
{
return $this->lastError;
}
public function setLastError(?string $lastError): self
{
$this->lastError = $lastError;
return $this;
}
public function getFailureCount(): int
{
return $this->failureCount;
}
public function setFailureCount(int $failureCount): self
{
$this->failureCount = $failureCount;
return $this;
}
public function recordFailure(string $error): self
{
$this->lastError = $error;
++$this->failureCount;
return $this;
}
/**
* Records a successful sync of a window ending at $horizonTo.
*
* The horizon only ever grows: the near-term job runs far more often than the full-horizon
* one, and must not discard the reach the latter established.
*/
public function recordSuccess(\DateTimeImmutable $syncedAt, \DateTimeImmutable $horizonTo): self
{
$this->syncedAt = $syncedAt;
$this->horizonTo = null === $this->horizonTo || $horizonTo > $this->horizonTo ? $horizonTo : $this->horizonTo;
$this->lastError = null;
$this->failureCount = 0;
return $this;
}
}