PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / Cron / Task / Reindex.php
backup / src / JetBackup / Cron / Task Last commit date
.htaccess 1 year ago Backup.php 1 year ago Download.php 1 year ago DownloadBackupLog.php 1 year ago Export.php 1 year ago Extract.php 1 year ago PreRestore.php 5 months ago Reindex.php 1 month ago Restore.php 5 months ago RetentionCleanup.php 4 months ago System.php 5 months ago Task.php 4 months ago index.html 1 year ago web.config 1 year ago
Reindex.php
641 lines
1 <?php
2
3 namespace JetBackup\Cron\Task;
4
5 use Exception;
6 use JetBackup\Alert\Alert;
7 use JetBackup\BackupJob\BackupJob;
8 use JetBackup\Data\Engine;
9 use JetBackup\Destination\Destination;
10 use JetBackup\Destination\DestinationFile;
11 use JetBackup\Destination\Integration\DestinationDirIterator;
12 use JetBackup\Destination\Integration\DestinationFile as DestinationFileAlias;
13 use JetBackup\Entities\Util;
14 use JetBackup\Exception\DBException;
15 use JetBackup\Exception\DestinationException;
16 use JetBackup\Exception\ExecutionTimeException;
17 use JetBackup\Exception\IOException;
18 use JetBackup\Exception\JBException;
19 use JetBackup\Exception\JetBackupLinuxException;
20 use JetBackup\Exception\ReindexException;
21 use JetBackup\Exception\SnapshotMetaException;
22 use JetBackup\Exception\TaskException;
23 use JetBackup\Exception\ValidationException;
24 use JetBackup\Factory;
25 use JetBackup\JetBackup;
26 use JetBackup\JetBackupLinux\JetBackupLinux;
27 use JetBackup\License\License;
28 use JetBackup\Queue\Queue;
29 use JetBackup\Queue\QueueItem;
30 use JetBackup\Queue\QueueItemReindex;
31 use JetBackup\ResumableTask\ResumableTask;
32 use JetBackup\Snapshot\Snapshot;
33 use JetBackup\Snapshot\SnapshotItem;
34 use SleekDB\Exceptions\InvalidArgumentException;
35
36 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
37
38 class Reindex extends Task {
39
40 const LOG_FILENAME = 'reindex';
41
42 private QueueItemReindex $_queue_item_reindex;
43 private ?Destination $_destination=null;
44
45 public function __construct() {
46 parent::__construct(self::LOG_FILENAME);
47 }
48
49 /**
50 * @return void
51 * @throws InvalidArgumentException
52 * @throws DBException
53 * @throws TaskException
54 * @throws \SleekDB\Exceptions\IOException
55 */
56 public function execute():void {
57 parent::execute();
58
59 $this->_queue_item_reindex = $this->getQueueItem()->getItemData();
60 $this->_destination = $this->_queue_item_reindex->getDestinationId() ? new Destination($this->_queue_item_reindex->getDestinationId()) : null;
61
62 if($this->_destination && !License::isValid() && !in_array($this->_destination->getType(), Destination::LICENSE_EXCLUDED)) {
63 $this->getLogController()->logError("You can't reindex from {$this->_destination->getType()} destination without a license");
64 $this->getQueueItem()->updateStatus(Queue::STATUS_ABORTED);
65 $this->getQueueItem()->updateProgress('Reindex Aborted!', QueueItem::PROGRESS_LAST_STEP);
66 return;
67 }
68
69 if($this->getQueueItem()->getStatus() == Queue::STATUS_PENDING) {
70 if($this->_destination) $this->getLogController()->logMessage("Starting reindex for destination \"{$this->_destination->getName()}\"");
71 else $this->getLogController()->logMessage("Starting reindex for JetBackup Linux");
72
73 $this->getQueueItem()->getProgress()->setTotalItems(count(Queue::STATUS_REINDEX_NAMES)+3);
74 $this->getQueueItem()->save();
75
76 $this->getQueueItem()->updateProgress('Starting reindex');
77 } else if($this->getQueueItem()->getStatus() > Queue::STATUS_PENDING) {
78 $this->getLogController()->logMessage('Resumed Reindex');
79 }
80
81 try {
82
83 $this->func([$this, '_checkRequirements']);
84 $this->func([$this, '_markSnapshots']);
85 $this->func([$this, '_reindexSnapshots']);
86 $this->func([$this, '_deleteSnapshots']);
87
88 if($this->getQueueItem()->getStatus() < Queue::STATUS_DONE && !$this->getQueueItem()->getErrors()) $this->getQueueItem()->updateStatus(Queue::STATUS_DONE);
89 else $this->getQueueItem()->updateStatus(Queue::STATUS_PARTIALLY);
90 $this->getLogController()->logMessage('Completed!');
91 } catch(ReindexException $e) {
92 $this->getQueueItem()->updateStatus(Queue::STATUS_FAILED);
93 $this->getLogController()->logError($e->getMessage());
94 $this->getLogController()->logMessage('Failed!');
95 }
96
97 $this->getQueueItem()->updateProgress(
98 $this->getQueueItem()->getStatus() == Queue::STATUS_DONE
99 ? 'Reindex Completed!'
100 : ($this->getQueueItem()->getStatus() == Queue::STATUS_PARTIALLY
101 ? 'Completed with errors (see logs)'
102 : 'Reindex Failed!'),
103 QueueItem::PROGRESS_LAST_STEP
104 );
105
106 $this->getLogController()->logMessage('Total time: ' . $this->getExecutionTimeLimit());
107 }
108
109 public function _checkRequirements() {
110 if($this->_destination) return;
111
112 if(!Factory::getSettingsGeneral()->isJBIntegrationEnabled()) throw new ReindexException("JetBackup Linux integration isn't enabled");
113 if(!JetBackupLinux::isInstalled()) throw new ReindexException("JetBackup Linux isn't installed on this server");
114
115 try {
116 JetBackupLinux::checkRequirements();
117 } catch (JetBackupLinuxException $e) {
118 throw new ReindexException($e->getMessage());
119 }
120 }
121
122 /**
123 * @return void
124 * @throws ReindexException
125 */
126 public function _deleteSnapshots() {
127
128 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
129 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
130
131 $this->getQueueItem()->updateStatus(Queue::STATUS_REINDEX_DELETE_SNAPSHOTS);
132 $this->getLogController()->logMessage("Removing all unneeded snapshots");
133
134 try {
135
136 // remove all snapshots with reindex flag
137 if($this->_destination) {
138 $list = Snapshot::query()
139 ->where([Snapshot::DESTINATION_ID, '=', $this->_destination->getId()])
140 ->where([Engine::ENGINE, '=', Engine::ENGINE_WP])
141 ->where([Snapshot::REINDEX, '=', true])
142 ->getQuery()
143 ->fetch();
144 } else {
145 $list = Snapshot::query()
146 ->where([Engine::ENGINE, '=', Engine::ENGINE_JB])
147 ->where([Snapshot::REINDEX, '=', true])
148 ->getQuery()
149 ->fetch();
150 }
151
152 if (empty($list)) return;
153
154 foreach ($list as $item) {
155 $snapshot = new Snapshot($item[JetBackup::ID_FIELD]);
156 if(!$snapshot->getId()) continue;
157 $snapshot->delete();
158 }
159
160 } catch( Exception $e) {
161 throw new ReindexException($e->getMessage());
162 }
163 }
164
165 /**
166 * @throws DBException
167 * @throws \SleekDB\Exceptions\IOException
168 * @throws InvalidArgumentException
169 */
170 private function _handleSGBSnapshot($filename) {
171
172 $this->getLogController()->logDebug("[_handleSnapshot] Filename: $filename");
173
174 try {
175 $stat = $this->_destination->getInstance()->getFileStat($filename);
176 $this->getLogController()->logDebug("[_handleSnapshot] getFileStat: " . print_r($stat, true));
177
178 } catch( Exception $e) {
179 $this->getLogController()->logError("[_handleSnapshot] Failed getting snapshot stats. Error: " . $e->getMessage());
180 return;
181 }
182
183 $name = substr($stat->getName(), 0, -5);
184 $created = $stat->getModifyTime() ?? 0;
185 $size = $stat->getSize() ?? 0;
186
187 $this->getLogController()->logDebug("[_handleSnapshot] Name: $name");
188 $this->getLogController()->logDebug("[_handleSnapshot] Created: $created");
189 $this->getLogController()->logDebug("[_handleSnapshot] Size: $size");
190
191 $this->getLogController()->logMessage("");
192 $this->getLogController()->logMessage("\tLegacy Snapshot found \"$name\"");
193
194
195 try {
196 $details = Snapshot::query()
197 ->where([Snapshot::NAME, '=', $name])
198 ->where([ Engine::ENGINE, '=', Engine::ENGINE_SGB])
199 ->where([Snapshot::DESTINATION_ID, '=', $this->_destination->getId()])
200 ->getQuery()
201 ->first();
202 } catch( Exception $e) {
203 $this->getLogController()->logError("[_handleSnapshot] Failed importing snapshot. Error: " . $e->getMessage());
204 return;
205 }
206
207 if($details) {
208 $snapshot = new Snapshot($details[JetBackup::ID_FIELD]);
209 $this->getLogController()->logMessage("\t[_handleSnapshot] Snapshot found on local database {$snapshot->getName()}");
210 $snapshot->setCreated($created);
211 $snapshot->setBackupType(BackupJob::TYPE_ACCOUNT);
212 $snapshot->setContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL);
213 $snapshot->setStructure(BackupJob::STRUCTURE_COMPRESSED);
214 $snapshot->setReindex(false);
215 $snapshot->save();
216 return;
217 }
218
219 $this->getLogController()->logMessage("\t[_handleSnapshot] Snapshot not exist on local database");
220 $snapshot = new Snapshot();
221 $snapshot->setBackupType(BackupJob::TYPE_ACCOUNT);
222 $snapshot->setContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL);
223 $snapshot->setStructure(BackupJob::STRUCTURE_COMPRESSED);
224 $snapshot->setDestinationId($this->_destination->getId());
225 $snapshot->setName($name);
226 $snapshot->setCreated($created);
227 $snapshot->setSize($size);
228 $snapshot->setEngine(Engine::ENGINE_SGB);
229 $snapshot->save();
230
231 $item = new SnapshotItem();
232 $item->setParentId($snapshot->getId());
233 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
234 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL);
235 $item->setName($name);
236 $item->setCreated($created);
237 $item->setSize($size);
238 $item->setPath($filename);
239 $item->setEngine(Engine::ENGINE_SGB);
240 $item->save();
241 }
242
243 /**
244 * @param $identifier
245 * @param $name
246 *
247 * @return void
248 * @throws DBException
249 * @throws IOException
250 * @throws InvalidArgumentException
251 * @throws \SleekDB\Exceptions\IOException
252 */
253 private function _handleSnapshot($identifier, $name) {
254 $this->getLogController()->logMessage("");
255 $this->getLogController()->logMessage("\tSnapshot found \"$name\"");
256
257 $meta_filepath = sprintf(Snapshot::META_FILEPATH, JetBackup::SEP . $identifier . JetBackup::SEP . $name);
258 $this->getLogController()->logDebug("[_handleSnapshot] Identifier: $identifier");
259 $this->getLogController()->logDebug("[_handleSnapshot] Meta path: $meta_filepath");
260 try {
261 if(!$this->_destination->fileExists($meta_filepath)) throw new DestinationException("can't find meta file $meta_filepath");
262 } catch(DestinationException $e) {
263 $this->getLogController()->logError("[_handleSnapshot] Failed importing snapshot. Error: " . $e->getMessage());
264 $this->getQueueItem()->addError();
265 return;
266 }
267
268 $meta_file = Factory::getLocations()->getTempDir() . JetBackup::SEP . 'dest_reindex_' . Util::generateUniqueId() . '.tmp';
269
270 try {
271 $this->_destination->copyFileToLocal($meta_filepath, $meta_file, $this->getQueueItem(), $this);
272 } catch(IOException|DestinationException $e) {
273 $this->getLogController()->logError("[_handleSnapshot] Failed importing snapshot. Error: failed downloading meta file ({$e->getMessage()})");
274 $this->getQueueItem()->addError();
275 return;
276 }
277
278 try {
279
280 $details = Snapshot::query()
281 ->where([Snapshot::NAME, '=', $name])
282 ->where([Engine::ENGINE, '=', Engine::ENGINE_WP])
283 ->where([Snapshot::DESTINATION_ID, '=', $this->_destination->getId()])
284 ->where([Snapshot::JOB_IDENTIFIER, '=', $identifier])
285 ->getQuery()
286 ->first();
287 } catch( Exception $e) {
288 $this->getLogController()->logError("[_handleSnapshot] Failed importing snapshot. Error: " . $e->getMessage());
289 $this->getQueueItem()->addError();
290 return;
291 }
292
293 if($details) {
294 $snapshot = new Snapshot($details[JetBackup::ID_FIELD]);
295 $this->getLogController()->logMessage("\t[_handleSnapshot] Snapshot found on local database {$snapshot->getName()}");
296 $snapshot->setReindex(false);
297 try {
298 $snapshot->removeItems();
299 } catch( Exception $e) {
300 $this->getLogController()->logError("[_handleSnapshot] Failed removing snapshot items. Error: " . $e->getMessage());
301 $this->getQueueItem()->addError();
302 return;
303 }
304 } else {
305 $this->getLogController()->logMessage("\t[_handleSnapshot] Snapshot not exist on local database");
306 $snapshot = new Snapshot();
307 $snapshot->setDestinationId($this->_destination->getId());
308 $snapshot->setJobIdentifier($identifier);
309 }
310
311 try {
312 $snapshot->importMeta($meta_file, $this->_queue_item_reindex->isCrossDomain());
313 } catch(SnapshotMetaException $e) {
314 // Can't import snapshot, there is a missing data in meta file
315 $this->getLogController()->logError("Failed importing snapshot. Error: " . $e->getMessage());
316 $this->getQueueItem()->addError();
317 unlink($meta_file);
318 return;
319 }
320
321 unlink($meta_file);
322
323 $snapshot->save();
324
325 $this->getLogController()->logMessage("\tSnapshot imported successfully");
326 $this->getLogController()->logDebug("Snapshot data: " . print_r($snapshot->getDisplay(), true));
327 }
328
329 /**
330 * @param $identifier
331 *
332 * @return array
333 * @throws ReindexException
334 */
335 public function _fetchSnapshots($identifier): array {
336
337 $this->getLogController()->logDebug("[_fetchSnapshots] Identifier: /$identifier/");
338
339 try {
340 $list = $this->_destination->listDir("/$identifier/");
341 } catch(DestinationException $e) {
342 throw new ReindexException($e->getMessage());
343 }
344
345 $snapshots = [];
346
347 while($list->hasNext()) {
348 $name = $list->getNext()->getName();
349 $this->getLogController()->logDebug("[_fetchSnapshots] Name: $name");
350 if(!preg_match(Snapshot::SNAPSHOT_NAME_REGEX, $name)) continue;
351 $snapshots[] = $name;
352 }
353
354 return $snapshots;
355 }
356
357 /**
358 * @param int $pageSize
359 * @param int $skip
360 *
361 * @return array
362 * @throws JetBackupLinuxException
363 */
364 public function _fetchJBBackupsPage(int $pageSize, int $skip):array {
365 $this->getLogController()->logDebug("[_fetchJBBackupsPage] Fetching backups (limit=$pageSize, skip=$skip)");
366 $this->getLogController()->logDebug("[_fetchJBBackupsPage] Memory before: " . Util::bytesToHumanReadable(memory_get_usage(true)));
367
368 $startTime = microtime(true);
369 $backups = JetBackupLinux::listBackups([], $pageSize, $skip);
370 $elapsed = round(microtime(true) - $startTime, 2);
371
372 $this->getLogController()->logDebug("[_fetchJBBackupsPage] Got " . count($backups) . " backups in {$elapsed}s");
373 $this->getLogController()->logDebug("[_fetchJBBackupsPage] Memory after: " . Util::bytesToHumanReadable(memory_get_usage(true)));
374 $this->getLogController()->logDebug("[_fetchJBBackupsPage] Response payload: " . Util::bytesToHumanReadable(strlen(serialize($backups))));
375
376 foreach($backups as $i => $backup) {
377 $this->getLogController()->logDebug("[_fetchJBBackupsPage] Backup[$i]: id=" . ($backup['_id'] ?? 'N/A')
378 . " created=" . ($backup['created'] ?? 'N/A')
379 . " structure=" . ($backup['backup_structure'] ?? 'N/A')
380 . " items=" . (isset($backup['items']) ? count($backup['items']) : 0)
381 );
382 }
383
384 return $backups;
385 }
386
387 public function _handleJBSnapshot($key, $backup) {
388 if(!$backup['items']) return;
389
390 $this->getLogController()->logDebug("[_handleJBSnapshot] Processing backup " . $backup['_id'] . " (" . count($backup['items']) . " items)");
391
392 if ($backup['backup_structure'] != JetBackupLinux::BACKUP_STRUCTURE_INCREMENTAL && $backup['backup_structure'] != JetBackupLinux::BACKUP_STRUCTURE_DEDUPLICATION) {
393 $this->getLogController()->logError("[_handleJBSnapshot] Backup ID" . $backup['_id'] . " is not incremental or deduplication, this type is not supported, skipping");
394 return;
395 }
396
397 try {
398 $details = Snapshot::query()
399 ->where([Engine::ENGINE, '=', Engine::ENGINE_JB])
400 ->where([Snapshot::UNIQUE_ID, '=', $backup['_id']])
401 ->getQuery()
402 ->first();
403 } catch( Exception $e) {
404 $this->getLogController()->logError("[_handleJBSnapshot] Failed importing snapshot. Error: " . $e->getMessage());
405 return;
406 }
407
408 if($details) {
409 $this->getLogController()->logMessage("\t[_handleJBSnapshot] Snapshot found on local database, skipping import");
410 $snapshot = new Snapshot($details[JetBackup::ID_FIELD]);
411 $snapshot->setReindex(false);
412 $snapshot->removeItems();
413 } else {
414 $this->getLogController()->logMessage("\t[_handleJBSnapshot] New snapshot found! Importing to local database...");
415 $snapshot = new Snapshot();
416 $snapshot->setNotes($backup['notes']);
417 }
418 $structure = $backup['backup_structure'] == JetBackupLinux::BACKUP_STRUCTURE_INCREMENTAL
419 ? BackupJob::STRUCTURE_INCREMENTAL
420 : $backup['backup_structure'];
421
422 $snapshot->setEngine(Engine::ENGINE_JB);
423 $snapshot->setBackupType(BackupJob::TYPE_ACCOUNT);
424 $snapshot->setUniqueId($backup['_id']);
425 $snapshot->setCreated(strtotime($backup['created']));
426 $snapshot->setStructure($structure);
427 $snapshot->setName(sprintf(Snapshot::SNAPSHOT_NAME_PATTERN, Util::date('Y-m-d_His', strtotime($backup['created'])), $backup['_id']));
428
429 // Save snapshot early to get ID for items (reduces memory by saving items progressively)
430 $snapshot->save();
431
432 $size = 0;
433 $contains = 0;
434 $itemCount = 0;
435
436 foreach($backup['items'] as $item_details) {
437
438 if($item_details['disabled']) {
439 $this->getLogController()->logMessage("\t[_handleJBSnapshot] Skipping item " . $item_details['_id'] . " Remote destination disabled");
440 continue;
441 }
442
443 switch($item_details['backup_contains']) {
444 case JetBackupLinux::BACKUP_TYPE_ACCOUNT_HOMEDIR:
445 $item = new SnapshotItem();
446 $item->setParentId($snapshot->getId());
447 $item->setEngine(Engine::ENGINE_JB);
448 $item->setName($item_details['name']);
449 $item->setPath($item_details['path']);
450 $item->setUniqueId($item_details['_id']);
451 $item->setCreated(strtotime($item_details['created']));
452 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
453 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR);
454 $item->save();
455 $size += intval($item_details['size']);
456 $contains |= BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR;
457 $itemCount++;
458 break;
459
460 case JetBackupLinux::BACKUP_TYPE_ACCOUNT_DATABASES:
461
462 if($item_details['name'] != DB_NAME) continue 2;
463
464 $item = new SnapshotItem();
465 $item->setParentId($snapshot->getId());
466 $item->setEngine(Engine::ENGINE_JB);
467 $item->setName($item_details['name']);
468 $item->setPath($item_details['path']);
469 $item->setUniqueId($item_details['_id']);
470 $item->setCreated(strtotime($item_details['created']));
471 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
472 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE);
473 $item->save();
474 $size += intval($item_details['size']);
475 $contains |= BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE;
476 $itemCount++;
477 break;
478 }
479 }
480
481 if($itemCount === 0) {
482 $snapshot->delete();
483 return;
484 }
485
486 if($contains != BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL) {
487 $item = new SnapshotItem();
488 $item->setParentId($snapshot->getId());
489 $item->setEngine(Engine::ENGINE_JB);
490 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
491 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL);
492 $item->setCreated(strtotime($backup['created']));
493 $item->setName('');
494 $item->setPath('');
495 $item->save();
496 }
497
498 $snapshot->setContains($contains);
499 $snapshot->setSize($size);
500 $snapshot->save();
501 }
502
503
504 /**
505 * @return void
506 * @throws ReindexException
507 */
508 public function _reindexSnapshots() : void {
509
510 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
511 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
512
513 if($this->getQueueItem()->getStatus() < Queue::STATUS_REINDEX_INDEXING_SNAPSHOTS) {
514 $this->getQueueItem()->updateStatus(Queue::STATUS_REINDEX_INDEXING_SNAPSHOTS);
515 $this->getQueueItem()->updateProgress('Indexing Snapshots');
516 }
517
518 if($this->_destination) {
519
520 $this->foreachCallable([ $this, '_fetchRootDirectory' ], [], function($i, $filename) {
521 $this->getLogController()->logDebug("Inspecting $filename");
522 if(preg_match(BackupJob::IDENTIFIER_REGEX, $filename)) {
523 $this->getLogController()->logMessage("Reindexing identifier \"$filename\"");
524
525 $this->foreachCallable([ $this, '_fetchSnapshots'], [$filename], function($s, $name) use ($filename) {
526 $this->_handleSnapshot($filename, $name);
527 }, 'snapshots_' . $filename);
528 } elseif(str_ends_with($filename, BackupJob::SNAPSHOT_SGBP_SUFFIX)) {
529 $this->_handleSGBSnapshot($filename); // Anything *.sgbp
530 }
531
532
533 });
534 } else {
535
536 try {
537 $pageSize = 25;
538 $page = 0;
539 $totalProcessed = 0;
540
541 do {
542 // Check execution time before fetching next page
543 $this->checkExecutionTime();
544
545 $skip = $page * $pageSize;
546 $pageName = 'jb_backups_page_' . $page;
547
548 $this->foreachCallable(
549 [$this, '_fetchJBBackupsPage'],
550 [$pageSize, $skip],
551 [$this, '_handleJBSnapshot'],
552 $pageName
553 );
554
555 // Check how many items were in this page to determine if more pages exist
556 $resumable = $this->getQueueItem()->getResumableTask();
557 $item = $resumable->_getItem($pageName, ResumableTask::TYPE_FOREACH);
558 $data = $item->getData();
559 $pageCount = $data ? ($data['total'] ?? 0) : 0;
560 $totalProcessed += $pageCount;
561
562 // Update sub-progress for backup-level tracking within the reindex phase
563 $this->getQueueItem()->getProgress()->setCurrentSubItem($totalProcessed);
564 // Estimate total: if full page, assume at least one more page; otherwise this is the last page
565 $estimatedTotal = $pageCount >= $pageSize ? $totalProcessed + $pageSize : $totalProcessed;
566 $this->getQueueItem()->getProgress()->setTotalSubItems($estimatedTotal);
567 $this->getQueueItem()->getProgress()->setSubMessage("$totalProcessed backups processed");
568 $this->getQueueItem()->save();
569 $this->getLogController()->logMessage("[_reindexSnapshots] Page $page completed ($pageCount backups, $totalProcessed total)");
570
571 $page++;
572 } while($pageCount >= $pageSize);
573 } catch(JetBackupLinuxException $e) {
574 throw new ReindexException($e->getMessage());
575 } catch ( DBException|ExecutionTimeException|JBException|\SleekDB\Exceptions\IOException|InvalidArgumentException $e ) {
576 }
577 }
578
579 // Reset sub-progress after reindex phase completes
580 $this->getQueueItem()->getProgress()->resetSub();
581 $this->getQueueItem()->save();
582 }
583
584 /**
585 * @throws ReindexException
586 */
587 public function _markSnapshots():void {
588
589 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
590 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
591
592 $this->getQueueItem()->updateStatus(Queue::STATUS_REINDEX_MARKING_SNAPSHOTS);
593 $this->getQueueItem()->updateProgress('Hiding all destination snapshots');
594 $this->getLogController()->logMessage("Hiding all destination snapshots");
595
596 try {
597 // mark all destination snapshots with reindex flag
598 if($this->_destination) {
599 Snapshot::query()
600 ->where([Snapshot::DESTINATION_ID, '=', $this->_destination->getId()])
601 ->where([Engine::ENGINE, '=', Engine::ENGINE_WP])
602 ->getQuery()
603 ->update([Snapshot::REINDEX => true]);
604 } else {
605 Snapshot::query()
606 ->where([Engine::ENGINE, '=', Engine::ENGINE_JB])
607 ->getQuery()
608 ->update([Snapshot::REINDEX => true]);
609 }
610 } catch( Exception $e) {
611 throw new ReindexException($e->getMessage());
612 }
613 }
614
615 /**
616 * @return array
617 * @throws ReindexException
618 */
619 public function _fetchRootDirectory():array {
620
621 $this->getLogController()->logMessage("Fetching destination root directory");
622
623 try {
624 $this->_destination->setLogController($this->getLogController());
625 $this->_destination->validate();
626 $list = $this->_destination->listDir('/');
627 } catch(ValidationException|DestinationException $e) {
628 throw new ReindexException($e->getMessage());
629 }
630
631 $output = [];
632
633 while($list->hasNext()) {
634 $output[] = $list->getNext()->getName();
635 }
636
637 if(!sizeof($output)) $this->getLogController()->logMessage("No files and directories were found on the destination root directory");
638
639 return $output;
640 }
641 }