feat(pargar): cap inbound mande/document logs after insert
Keep the newest 2000 success rows per inbound type so dumps stop growing from Pargar webhooks, and store compact document payloads instead of full JSON. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,7 @@ use App\Models\Tahesab;
|
||||
use App\Models\Zarin;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
@@ -20,6 +21,9 @@ class DatabaseLogPruneService
|
||||
/** همراستا با حذف قطعی در PriceSnapshotService::pruneHistory */
|
||||
public const RETENTION_DAYS = 31;
|
||||
|
||||
/** سقف ردیف موفق مانده/سند ورودی پرگار — بقیه (سفارش/کیف) را نمیبُرد */
|
||||
public const PARAGAR_INBOUND_KEEP = 2000;
|
||||
|
||||
private const CHUNK = 500;
|
||||
|
||||
public function pruneHistory(?Carbon $now = null): array
|
||||
@@ -29,6 +33,7 @@ class DatabaseLogPruneService
|
||||
return [
|
||||
'retention_days' => self::RETENTION_DAYS,
|
||||
'cutoff' => $cutoff->toDateTimeString(),
|
||||
'pargar_inbound' => $this->prunePargarInboundOverflow(),
|
||||
'price_sync_logs' => $this->prunePriceSyncLogs($cutoff),
|
||||
'logs' => $this->pruneGenericLogs($cutoff),
|
||||
'pargar_sync_logs' => $this->pruneSuccessfulRows(
|
||||
@@ -137,4 +142,101 @@ class DatabaseLogPruneService
|
||||
|
||||
return ['deleted' => $deleted, 'skipped' => false];
|
||||
}
|
||||
|
||||
/**
|
||||
* فقط لاگ ورودی پرگار (mande/document). سفارش و سند کیف دست نخورده میماند.
|
||||
*
|
||||
* @return array{mande: int, document: int}
|
||||
*/
|
||||
public function prunePargarInboundOverflow(int $maxDeletePerType = 0): array
|
||||
{
|
||||
if (! Schema::hasTable('pargar_sync_logs')) {
|
||||
return ['mande' => 0, 'document' => 0];
|
||||
}
|
||||
|
||||
ignore_user_abort(true);
|
||||
if (function_exists('set_time_limit')) {
|
||||
@set_time_limit(180);
|
||||
}
|
||||
|
||||
$this->ensureTypeStatusIndex();
|
||||
|
||||
$locked = $this->acquirePruneLock();
|
||||
if ($locked === false) {
|
||||
return ['mande' => 0, 'document' => 0];
|
||||
}
|
||||
|
||||
try {
|
||||
return [
|
||||
'mande' => $this->pruneOldestSuccessBeyond(PargarSyncService::TYPE_MANDE, $maxDeletePerType),
|
||||
'document' => $this->pruneOldestSuccessBeyond(PargarSyncService::TYPE_DOCUMENT, $maxDeletePerType),
|
||||
];
|
||||
} finally {
|
||||
if ($locked === true) {
|
||||
$this->releasePruneLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function pruneOldestSuccessBeyond(string $type, int $maxDelete): int
|
||||
{
|
||||
$cutoffId = PargarSyncLog::query()
|
||||
->where('type', $type)
|
||||
->where('sync_status', PargarSyncService::STATUS_SUCCESS)
|
||||
->orderByDesc('id')
|
||||
->skip(self::PARAGAR_INBOUND_KEEP)
|
||||
->value('id');
|
||||
|
||||
if (! $cutoffId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$query = PargarSyncLog::query()
|
||||
->where('type', $type)
|
||||
->where('sync_status', PargarSyncService::STATUS_SUCCESS)
|
||||
->where('id', '<=', $cutoffId)
|
||||
->orderBy('id');
|
||||
|
||||
if ($maxDelete > 0) {
|
||||
return (int) $query->limit($maxDelete)->delete();
|
||||
}
|
||||
|
||||
return (int) $query->delete();
|
||||
}
|
||||
|
||||
private function ensureTypeStatusIndex(): void
|
||||
{
|
||||
try {
|
||||
$names = collect(DB::select('SHOW INDEX FROM `pargar_sync_logs`'))
|
||||
->pluck('Key_name')
|
||||
->all();
|
||||
if (in_array('pargar_sync_logs_type_status_index', $names, true)) {
|
||||
return;
|
||||
}
|
||||
DB::statement(
|
||||
'ALTER TABLE `pargar_sync_logs` ADD INDEX `pargar_sync_logs_type_status_index` (`type`, `sync_status`)'
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return bool|null true=locked, false=busy, null=no lock support */
|
||||
private function acquirePruneLock(): ?bool
|
||||
{
|
||||
try {
|
||||
$row = DB::selectOne('SELECT GET_LOCK(?, 0) AS l', ['khanegi_pargar_inbound_prune']);
|
||||
|
||||
return (int) ($row->l ?? 0) === 1;
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function releasePruneLock(): void
|
||||
{
|
||||
try {
|
||||
DB::selectOne('SELECT RELEASE_LOCK(?) AS l', ['khanegi_pargar_inbound_prune']);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user