feat: always sync full 24 months horizon of contingents

This commit is contained in:
Björn Fromme
2026-08-20 12:03:21 +02:00
parent 351fbab498
commit c1f0cda479
6 changed files with 41 additions and 42 deletions
+8 -6
View File
@@ -26,7 +26,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Console entrypoint for the scheduled contingent snapshot sync.
*
* Runs at two cadences: a short horizon frequently, the full horizon a couple of times a day.
* Scheduled over the full horizon throughout: every 15 minutes during peak hours, hourly
* outside them. The options exist for ad-hoc runs — a narrower --horizon-months, or --hotel
* to re-sync a single accommodation while debugging.
*/
class BpnSyncContingentsCommand extends Command
{
@@ -48,7 +50,7 @@ class BpnSyncContingentsCommand extends Command
protected function configure(): void
{
$this
->addOption('horizon-months', null, InputOption::VALUE_REQUIRED, 'How many months ahead to sync', '24')
->addOption('horizon-months', null, InputOption::VALUE_REQUIRED, 'How many months ahead to sync (defaults to the full horizon)', '24')
->addOption('hotel', null, InputOption::VALUE_REQUIRED, 'Restrict the run to a single calendarCode')
->addOption('stale-after', null, InputOption::VALUE_REQUIRED, 'Minimum minutes since the last sync before an accommodation is eligible (ignored with --force)', '0')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Sync even if the snapshot was refreshed recently')
@@ -148,10 +150,10 @@ class BpnSyncContingentsCommand extends Command
// Only a *total* failure is worth failing the task over: that means upstream is down or
// the API key is rejected, and the scheduler's failure mail is genuinely actionable.
// A single hotel breaking must stay quiet — this task runs every 15 minutes, so failing
// on it would mail dozens of times a day until someone silenced the task. That hotel is
// not lost track of: its own calendar serves a 502, and contingent_sync_state records
// failure_count and last_error.
// A single hotel breaking must stay quiet — this task runs every 15 minutes during the
// day, so failing on it would mail dozens of times a day until someone silenced the task.
// That hotel is not lost track of: its own calendar serves a 502, and
// contingent_sync_state records failure_count and last_error.
return $processed > 0 && $failed === $processed ? Command::FAILURE : Command::SUCCESS;
}
+3 -2
View File
@@ -159,8 +159,9 @@ class ContingentSyncState
/**
* 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.
* The horizon only ever grows, so an ad-hoc run over a narrow window — say
* `--horizon-months=1` while debugging — cannot discard the reach the scheduled full sync
* established.
*/
public function recordSuccess(\DateTimeImmutable $syncedAt, \DateTimeImmutable $horizonTo): self
{
+2 -2
View File
@@ -37,8 +37,8 @@ class ContingentSnapshotManager
* Refreshes [dateFrom, dateTo] for a single accommodation.
*
* Days are fetched, diffed against what is stored and then persisted; the snapshot outside
* the requested window is left untouched, which is what lets the near-term and full-horizon
* schedules run at different cadences without fighting each other.
* the requested window is left untouched, so a narrow ad-hoc run cannot truncate what a
* full-horizon run already stored.
*/
public function sync(
Accommodation $accommodation,
+3 -2
View File
@@ -24,8 +24,9 @@ class ContingentSnapshotReader
/**
* How old the snapshot may get before we stop presenting it as current availability.
*
* The near-term sync runs every 15 minutes during the day and hourly overnight, so this only
* trips when the scheduler is genuinely broken, never on the normal cadence.
* The sync runs every 15 minutes during the day and hourly overnight, so even the sparsest
* cadence leaves five missed runs of slack: this only trips when the scheduler is genuinely
* broken, never on the normal cadence.
*/
private const string MAX_SNAPSHOT_AGE = '-6 hours';