'latest', 'region' => $config['s3']['region'], 'endpoint' => $config['s3']['endpoint'], 'use_path_style_endpoint' => true, 'credentials' => [ 'key' => $config['s3']['key'], 'secret' => $config['s3']['secret'], ], 'suppress_php_deprecation_warning' => true ]); // Анализируем удаления по времени echo "1. Анализ удалений по времени суток...\n"; $deletionsByHour = []; $deletionsByDay = []; $batchDeletions = []; // Массовые удаления (много файлов за короткое время) $totalChecked = 0; $maxToCheck = 10000; try { $isTruncated = true; $continuationToken = null; $pageCount = 0; $maxPages = 20; $currentBatch = []; $lastDeleteTime = null; while ($isTruncated && $pageCount < $maxPages && $totalChecked < $maxToCheck) { $params = [ 'Bucket' => $s3Bucket, 'Prefix' => 'crm2/CRM_Active_Files/Documents/Project/', 'MaxKeys' => 1000 ]; if ($continuationToken) { $params['ContinuationToken'] = $continuationToken; } $versions = $s3Client->listObjectVersions($params); $pageCount++; if (isset($versions['DeleteMarkers'])) { foreach ($versions['DeleteMarkers'] as $marker) { $totalChecked++; $deleteDate = isset($marker['LastModified']) ? $marker['LastModified'] : null; if ($deleteDate) { $dateTime = new DateTime($deleteDate); $hour = $dateTime->format('H'); $day = $dateTime->format('Y-m-d'); if (!isset($deletionsByHour[$hour])) { $deletionsByHour[$hour] = 0; } $deletionsByHour[$hour]++; if (!isset($deletionsByDay[$day])) { $deletionsByDay[$day] = 0; } $deletionsByDay[$day]++; // Определяем массовые удаления (более 10 файлов за минуту) $deleteTimestamp = strtotime($deleteDate); if ($lastDeleteTime && abs($deleteTimestamp - $lastDeleteTime) < 60) { $currentBatch[] = $marker; } else { if (count($currentBatch) > 10) { $batchDeletions[] = [ 'count' => count($currentBatch), 'time' => date('Y-m-d H:i:s', $lastDeleteTime), 'files' => array_slice($currentBatch, 0, 5) // Первые 5 для примера ]; } $currentBatch = [$marker]; } $lastDeleteTime = $deleteTimestamp; } } } $isTruncated = isset($versions['IsTruncated']) && $versions['IsTruncated']; $continuationToken = isset($versions['NextContinuationToken']) ? $versions['NextContinuationToken'] : null; if (!$isTruncated) { break; } } // Проверяем последний батч if (count($currentBatch) > 10) { $batchDeletions[] = [ 'count' => count($currentBatch), 'time' => $lastDeleteTime ? date('Y-m-d H:i:s', $lastDeleteTime) : 'неизвестно', 'files' => array_slice($currentBatch, 0, 5) ]; } echo " Проверено delete markers: $totalChecked\n\n"; echo " Удаления по часам суток:\n"; ksort($deletionsByHour); foreach ($deletionsByHour as $hour => $count) { if ($count > 0) { echo " {$hour}:00 - " . ($hour + 1) . ":00: $count удалений\n"; } } echo "\n"; echo " Удаления по дням (топ 15):\n"; arsort($deletionsByDay); $count = 0; foreach ($deletionsByDay as $day => $deleteCount) { echo " $day: $deleteCount удалений\n"; if (++$count >= 15) break; } echo "\n"; if (!empty($batchDeletions)) { echo " Массовые удаления (более 10 файлов за минуту): " . count($batchDeletions) . "\n"; foreach (array_slice($batchDeletions, 0, 5) as $batch) { echo " Время: {$batch['time']}, удалено файлов: {$batch['count']}\n"; } echo "\n"; } } catch (\Aws\Exception\AwsException $e) { echo " Ошибка: " . $e->getMessage() . "\n"; } // Проверяем, может быть это связано с удалением документов в CRM echo "2. Проверка связи с удалением документов в CRM...\n"; $pdo = new PDO( "mysql:host={$dbconfig['db_server']};port=3306;dbname={$dbconfig['db_name']};charset=utf8", $dbconfig['db_username'], $dbconfig['db_password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] ); // Проверяем, сколько документов было удалено в последние дни $stmt = $pdo->prepare(' SELECT DATE(e.modifiedtime) as delete_date, COUNT(*) as count FROM vtiger_crmentity e INNER JOIN vtiger_notes n ON n.notesid = e.crmid WHERE e.deleted = 1 AND n.filelocationtype = "E" AND e.modifiedtime >= DATE_SUB(NOW(), INTERVAL 60 DAY) GROUP BY DATE(e.modifiedtime) ORDER BY delete_date DESC LIMIT 20 '); $stmt->execute(); $deletedDocs = $stmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($deletedDocs)) { echo " Удаленные документы в CRM (за последние 60 дней):\n"; foreach ($deletedDocs as $doc) { echo " {$doc['delete_date']}: {$doc['count']} документов\n"; } echo "\n"; } else { echo " Удаленных документов не найдено\n\n"; } // Сравниваем даты удалений в S3 и CRM echo "3. Сравнение дат удалений в S3 и CRM...\n"; if (!empty($deletionsByDay) && !empty($deletedDocs)) { echo " Сравнение:\n"; foreach ($deletedDocs as $doc) { $crmDate = $doc['delete_date']; $s3Count = $deletionsByDay[$crmDate] ?? 0; $crmCount = $doc['count']; if ($s3Count > 0) { echo " $crmDate:\n"; echo " Удалено в CRM: $crmCount документов\n"; echo " Delete markers в S3: $s3Count\n"; if ($s3Count > $crmCount * 2) { echo " ⚠️ В S3 удалено значительно больше файлов!\n"; } elseif (abs($s3Count - $crmCount) <= 10) { echo " ✅ Количество примерно совпадает\n"; } echo "\n"; } } } echo str_repeat("=", 80) . "\n"; echo "ВЫВОДЫ:\n\n"; // Определяем наиболее вероятную причину $maxHour = array_search(max($deletionsByHour), $deletionsByHour); $maxDay = array_search(max($deletionsByDay), $deletionsByDay); $maxDayCount = max($deletionsByDay); echo "1. Пик удалений:\n"; echo " - Время: {$maxHour}:00\n"; echo " - День: $maxDay ($maxDayCount удалений)\n\n"; if ($maxHour >= 6 && $maxHour <= 8) { echo "2. 💡 ВЕРОЯТНАЯ ПРИЧИНА: Автоматическая задача (cron job)\n"; echo " Удаления происходят рано утром (6-8 утра) - типичное время для cron\n\n"; } if (!empty($batchDeletions)) { echo "3. 💡 ВЕРОЯТНАЯ ПРИЧИНА: Массовое удаление (скрипт или автоматизация)\n"; echo " Найдено " . count($batchDeletions) . " случаев массового удаления\n\n"; } echo "4. 💡 РЕКОМЕНДАЦИЯ: Проверить:\n"; echo " - DeleteOrphanedItems в Nextcloud (запускается ежедневно)\n"; echo " - Cron задачи, которые могут удалять файлы\n"; echo " - Логи Nextcloud на предмет массовых удалений\n"; } catch (Exception $e) { echo "ОШИБКА: " . $e->getMessage() . "\n"; echo "Trace: " . $e->getTraceAsString() . "\n"; }