42 lines
1.8 KiB
PHP
42 lines
1.8 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Serp\Operations;
|
||
|
|
|
||
|
|
use App\Models\SerpBudgetEntry;
|
||
|
|
use App\Models\SerpOperationState;
|
||
|
|
|
||
|
|
final class RampUpPolicy
|
||
|
|
{
|
||
|
|
public function __construct(private ActivationManager $activation) {}
|
||
|
|
|
||
|
|
public function assertAllowsNewPaid(): void
|
||
|
|
{
|
||
|
|
if ($this->activation->state() !== ActivationState::Enabled) {
|
||
|
|
return;
|
||
|
|
}$state = SerpOperationState::query()->lockForUpdate()->find(1);
|
||
|
|
if (! $state) {
|
||
|
|
throw new OperationBlockedException('ramp_state_missing');
|
||
|
|
}
|
||
|
|
|
||
|
|
if (! $state->ramp_started_at) {
|
||
|
|
$state->forceFill(['ramp_started_at' => now('UTC')])->save();
|
||
|
|
}$age = $state->ramp_started_at->diffInMinutes(now('UTC'));
|
||
|
|
$limit = $age < 60 ? (int) config('serp.ramp.first_hour_limit') : ($age < 360 ? (int) config('serp.ramp.six_hour_limit') : (int) config('serp.ramp.first_day_limit'));
|
||
|
|
$count = SerpBudgetEntry::where('reserved_at', '>=', $state->ramp_started_at)->count();
|
||
|
|
if ($count >= $limit) {
|
||
|
|
throw new OperationBlockedException('ramp_up_limit');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function status(): array
|
||
|
|
{
|
||
|
|
$state = SerpOperationState::find(1);
|
||
|
|
if (! $state?->ramp_started_at) {
|
||
|
|
return ['started_at' => null, 'age_minutes' => null, 'used' => 0, 'limit' => (int) config('serp.ramp.first_hour_limit')];
|
||
|
|
}$age = $state->ramp_started_at->diffInMinutes(now('UTC'));
|
||
|
|
$limit = $age < 60 ? (int) config('serp.ramp.first_hour_limit') : ($age < 360 ? (int) config('serp.ramp.six_hour_limit') : (int) config('serp.ramp.first_day_limit'));
|
||
|
|
|
||
|
|
return ['started_at' => $state->ramp_started_at->toAtomString(), 'age_minutes' => (int) $age, 'used' => SerpBudgetEntry::where('reserved_at', '>=', $state->ramp_started_at)->count(), 'limit' => $limit];
|
||
|
|
}
|
||
|
|
}
|