Compare commits

...

11 Commits

Author SHA1 Message Date
Terranom674
86a2a0fbec Bump Bratonien Tools version to 0.9.7.1.30 2026-09-03 23:20:45 +02:00
Terranom674
c058e48fb0 Keep partial WebDAV cache runs resumable instead of global error 2026-09-03 23:20:25 +02:00
Terranom674
d20be6a39a Make WebDAV warmup batch cancellation and connector deferral deterministic 2026-09-03 23:19:42 +02:00
Terranom674
3d7114d782 Bump Bratonien Tools to 0.9.7.1.29 2026-09-03 23:15:16 +02:00
Terranom674
c20795d108 Treat connector deferrals as waiting instead of cache errors 2026-09-03 23:14:41 +02:00
Terranom674
2863a51acf Do not collapse WebDAV warmup on connector lock deferrals 2026-09-03 23:14:08 +02:00
Terranom674
ec8036d226 Bump Bratonien Tools to 0.9.7.1.28 2026-09-03 23:04:28 +02:00
Terranom674
0094942f68 Expose WebDAV cancelling and cancelled states 2026-09-03 23:04:05 +02:00
Terranom674
12834041be Route cache cancel to combined local and WebDAV handler 2026-09-03 23:03:33 +02:00
Terranom674
1f2651b8f0 Wire combined cache cancel to WebDAV worker 2026-09-03 23:03:14 +02:00
Terranom674
791d67929b Make WebDAV cache cancel stop after current batch 2026-09-03 23:02:37 +02:00
6 changed files with 377 additions and 41 deletions

View File

@@ -32,7 +32,7 @@ function bratonien_tools_get_tools()
return array(
'image_cache_clear' => array('handler' => 'bratonien_tools_clear_image_cache_atomic'),
'image_cache_build' => array('handler' => 'bratonien_tools_start_combined_image_cache_build'),
'image_cache_cancel' => array('handler' => 'bratonien_tools_cancel_main_cache_build'),
'image_cache_cancel' => array('handler' => 'bratonien_tools_cancel_combined_image_cache_build'),
'image_cache_worker_settings' => array('handler' => 'bratonien_tools_save_cache_worker_settings'),
'image_cache_webdav_warmup_settings' => array('handler' => 'bratonien_tools_save_webdav_warmup_settings'),
'image_cache_webdav_warmup_manual' => array('handler' => 'bratonien_tools_start_webdav_warmup_manual'),
@@ -86,4 +86,4 @@ function bratonien_tools_handle_watermark_engine()
? 'Bratonien-Wasserzeichenverwaltung aktiviert. Piwigos bisherige Wasserzeichen-Nutzung wurde gesichert und unterdrueckt.'
: 'Bratonien-Wasserzeichenverwaltung deaktiviert. Die zuvor gesicherte Piwigo-Wasserzeichen-Nutzung wurde wiederhergestellt.',
);
}
}

View File

@@ -57,6 +57,11 @@ function bratonien_tools_webdav_warmup_status_file_for_connection($connection_id
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-webdav-warmup.status-'.(int)$connection_id.'.json';
}
function bratonien_tools_webdav_warmup_cancel_file_for_connection($connection_id)
{
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-webdav-warmup.cancel-'.(int)$connection_id;
}
function bratonien_tools_webdav_warmup_write_status($connection_id, $state, $message, array $extra=array())
{
$file = bratonien_tools_webdav_warmup_status_file_for_connection($connection_id);
@@ -180,6 +185,10 @@ function bratonien_tools_start_webdav_warmup_mode($mode, $message)
$run_id = date('YmdHis').'-'.bin2hex(random_bytes(4));
foreach ($connections as $connection_id=>$runtime)
{
// Ein altes Abbruchsignal darf niemals den nächsten bewusst gestarteten
// Lauf stoppen. Es wird unmittelbar vor dem neuen Start entfernt.
@unlink(bratonien_tools_webdav_warmup_cancel_file_for_connection($connection_id));
bratonien_tools_webdav_warmup_write_status(
$connection_id,
'queued',
@@ -224,6 +233,72 @@ function bratonien_tools_start_webdav_cache_rebuild()
);
}
function bratonien_tools_request_webdav_cache_cancel()
{
$requested = 0;
foreach (bratonien_tools_webdav_warmup_connections() as $connection_id=>$runtime)
{
$status_file = bratonien_tools_webdav_warmup_status_file_for_connection($connection_id);
$status = null;
if (is_file($status_file) && is_readable($status_file))
{
$raw = @file_get_contents($status_file);
$status = $raw !== false ? json_decode($raw, true) : null;
}
if (!is_array($status)) $status = array();
$state = (string)($status['state'] ?? 'idle');
if (!in_array($state, array('queued','scan','running','cancelling'), true)) continue;
$cancel = bratonien_tools_webdav_warmup_cancel_file_for_connection($connection_id);
if (@file_put_contents($cancel, (string)time()."\n", LOCK_EX) === false)
{
throw new RuntimeException('WebDAV-Abbruchsignal für Verbindung #'.(int)$connection_id.' konnte nicht geschrieben werden.');
}
@chmod($cancel, 0664);
$extra = $status;
unset($extra['state'], $extra['message'], $extra['connection_id'], $extra['updated_at']);
$extra['cancel_requested'] = true;
bratonien_tools_webdav_warmup_write_status(
$connection_id,
'cancelling',
'Abbruch angefordert. Der aktuell laufende Batch wird noch vollständig beendet; danach werden keine weiteren Nextcloud-Originale geladen.',
$extra
);
$requested++;
}
return $requested;
}
function bratonien_tools_cancel_combined_image_cache_build()
{
$webdav = bratonien_tools_request_webdav_cache_cancel();
$local = false;
if (function_exists('bratonien_tools_main_cache_process_active') && function_exists('bratonien_tools_main_cache_is_running'))
{
$local = bratonien_tools_main_cache_process_active() || bratonien_tools_main_cache_is_running();
}
if ($local && function_exists('bratonien_tools_request_main_cache_cancel'))
{
bratonien_tools_request_main_cache_cancel();
}
if ($webdav > 0 && $local)
{
return array('message'=>'Cache-Abbruch angefordert. Der aktuelle WebDAV-Batch wird noch beendet; lokale Cache-Worker erhalten ebenfalls sofort das Abbruchsignal.');
}
if ($webdav > 0)
{
return array('message'=>'WebDAV-Cache-Abbruch angefordert. Der aktuell laufende Batch wird noch vollständig beendet; danach ist Schluss.');
}
if ($local)
{
return array('message'=>'Lokaler Piwigo-Cache-Abbruch wurde angefordert.');
}
return array('message'=>'Es läuft aktuell kein Cache-Aufbau.');
}
function bratonien_tools_has_local_cache_sources()
{
if (!function_exists('bratonien_tools_webdav_materialize_source_info')) return true;
@@ -316,4 +391,4 @@ function bratonien_tools_get_webdav_warmup_status()
if ($latest === null || (int)($data['updated_at'] ?? 0) > (int)($latest['updated_at'] ?? 0)) $latest = $data;
}
return is_array($latest) ? array_merge($status, $latest) : $status;
}
}

View File

@@ -40,7 +40,7 @@ function bratonien_tools_status_webdav_label(array $status)
$state = (string)($status['state'] ?? 'idle');
$prefix = $connection > 0 ? 'WebDAV #'.$connection.': ' : 'WebDAV: ';
if ($state === 'queued') return $prefix.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'Worker wartet auf den Start.');
if ($state === 'queued' || $state === 'waiting') return $prefix.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'Worker wartet auf den nächsten sicheren Startpunkt.');
if ($state === 'scan')
{
$sources = (int)($status['shadow_sources'] ?? 0);
@@ -64,6 +64,9 @@ function bratonien_tools_status_webdav_label(array $status)
if ($total > 0) $parts[] = $completed.' / '.$total.' Quellen dieser Stufe abgeschlossen';
return implode(' · ', $parts).'.';
}
if ($state === 'cancelling') return $prefix.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'Abbruch angefordert; aktueller Batch wird noch beendet.');
if ($state === 'cancelled') return $prefix.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'Cache-Aufbau wurde abgebrochen.');
if ($state === 'partial') return $prefix.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'Einzelne Quellen sind noch offen und werden beim nächsten Lauf erneut versucht.');
if ($state === 'preempted') return $prefix.'Stufe 2 wurde nach einem vollständigen Batch unterbrochen, damit neue Connector-Inhalte zuerst verarbeitet werden können.';
if ($state === 'complete') return $prefix.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'Verarbeitung abgeschlossen.');
if ($state === 'error' || $state === 'fatal') return $prefix.'FEHLER: '.((string)($status['message'] ?? '') !== '' ? (string)$status['message'] : 'WebDAV-Verarbeitung fehlgeschlagen.');
@@ -93,6 +96,10 @@ usort($webdav, function($a, $b) {
});
$webdav_active = false;
$webdav_waiting = false;
$webdav_partial = false;
$webdav_cancelling = false;
$webdav_cancelled = false;
$webdav_error = false;
$webdav_pending = false;
$webdav_latest = 0;
@@ -120,16 +127,16 @@ foreach ($webdav as &$status)
$webdav_source_total += $selected;
$webdav_progress_total += $selected * 2;
}
elseif ($state === 'running')
elseif ($state === 'running' || $state === 'cancelling')
{
$selected = max(0, (int)($status['selected_total'] ?? 0));
$selected = max(0, (int)($status['selected_total'] ?? $status['selected'] ?? 0));
$stage = max(1, (int)($status['stage'] ?? 1));
$stage_completed = max(0, min($selected, (int)($status['stage_completed'] ?? 0)));
$webdav_source_total += $selected;
$webdav_progress_total += $selected * 2;
$webdav_progress_completed += $stage >= 2 ? $selected + $stage_completed : $stage_completed;
}
elseif ($state === 'complete' || $state === 'error' || $state === 'fatal' || $state === 'preempted')
elseif (in_array($state, array('complete','error','fatal','preempted','cancelled','waiting','partial'), true))
{
$selected = max(0, (int)($status['selected'] ?? 0));
if ($selected > 0)
@@ -138,13 +145,19 @@ foreach ($webdav as &$status)
$stage2_completed = max(0, (int)($status['stage2_completed'] ?? 0));
$stage1_failed = max(0, (int)($status['stage1_failed'] ?? 0));
$stage2_failed = max(0, (int)($status['stage2_failed'] ?? 0));
$stage1_deferred = max(0, (int)($status['stage1_deferred'] ?? 0));
$stage2_deferred = max(0, (int)($status['stage2_deferred'] ?? 0));
$webdav_source_total += $selected;
$webdav_progress_total += $selected * 2;
$webdav_progress_completed += min($selected * 2, $stage1_completed + $stage2_completed + $stage1_failed + $stage2_failed);
$webdav_progress_completed += min($selected * 2, $stage1_completed + $stage2_completed + $stage1_failed + $stage2_failed + $stage1_deferred + $stage2_deferred);
}
}
if (in_array($state, array('queued','scan','running'), true)) $webdav_active = true;
if ($state === 'waiting') $webdav_waiting = true;
if ($state === 'partial') $webdav_partial = true;
if ($state === 'cancelling') $webdav_cancelling = true;
if ($state === 'cancelled') $webdav_cancelled = true;
if ($state === 'preempted') $webdav_pending = true;
if (in_array($state, array('error','fatal'), true)) $webdav_error = true;
if ($state !== 'complete') $webdav_all_complete = false;
@@ -155,7 +168,7 @@ unset($status);
$local_state = (string)($local['state'] ?? 'idle');
$local_updated = (int)($local['updated_at'] ?? 0);
$local_stale = in_array($local_state, array('running','queued'), true) && $local_updated > 0 && (time() - $local_updated) > 45;
if ($local_stale && !$webdav_active)
if ($local_stale && !$webdav_active && !$webdav_cancelling && !$webdav_waiting)
{
$local['state'] = 'error';
$local['message'] = 'Der lokale Piwigo-Teil liefert seit mehr als 45 Sekunden keinen Fortschritt.';
@@ -180,7 +193,15 @@ $local_completed = max(0, min($local_total, (int)($local['completed'] ?? 0)));
$combined_total = $local_total + $webdav_progress_total;
$combined_completed = $local_completed + min($webdav_progress_total, $webdav_progress_completed);
if ($webdav_active)
if ($webdav_cancelling || $local_state === 'cancelling')
{
$overall['state'] = 'cancelling';
$overall['total'] = $combined_total;
$overall['completed'] = $combined_completed;
$overall['message'] = 'Cache-Aufbau wird nach dem aktuell laufenden Batch beendet'.($webdav_source_total > 0 ? ' · '.$webdav_source_total.' WebDAV-Quellen' : '').'.';
$overall['current'] = implode(' ', $webdav_lines);
}
elseif ($webdav_active)
{
$overall['state'] = 'running';
$overall['total'] = $combined_total;
@@ -191,6 +212,23 @@ if ($webdav_active)
$overall['message'] = 'Gesamtaufbau läuft'.($webdav_source_total > 0 ? ' · '.$webdav_source_total.' WebDAV-Quellen' : '').'. '.$local_label;
$overall['current'] = implode(' ', $webdav_lines);
}
elseif ($webdav_waiting)
{
$overall['state'] = 'queued';
$overall['total'] = $combined_total;
$overall['completed'] = $combined_completed;
$overall['message'] = 'Cache-Aufbau pausiert für laufende Connector-Synchronisierung'.($webdav_source_total > 0 ? ' · '.$webdav_source_total.' WebDAV-Quellen' : '').'. Der Indexstand bleibt erhalten.';
$overall['current'] = implode(' ', $webdav_lines);
}
elseif ($webdav_partial)
{
$overall['state'] = 'queued';
$overall['total'] = $combined_total;
$overall['completed'] = $combined_completed;
$overall['errors'] = 0;
$overall['message'] = 'Cache-Aufbau bleibt fortsetzbar: einzelne Quellen sind noch offen; der restliche Bestand wurde weiterverarbeitet.';
$overall['current'] = implode(' ', $webdav_lines);
}
elseif ($webdav_error || $local_state === 'error')
{
$overall['state'] = 'error';
@@ -211,6 +249,14 @@ elseif ($webdav_pending)
$overall['message'] = 'Cache-Aufbau wartet auf priorisierte Connector-Inhalte'.($webdav_source_total > 0 ? ' · '.$webdav_source_total.' WebDAV-Quellen' : '').'. '.$local_label;
$overall['current'] = implode(' ', $webdav_lines);
}
elseif ($webdav_cancelled || $local_state === 'cancelled')
{
$overall['state'] = 'cancelled';
$overall['total'] = $combined_total;
$overall['completed'] = $combined_completed;
$overall['message'] = 'Cache-Aufbau wurde nach dem letzten vollständig abgeschlossenen Batch beendet.';
$overall['current'] = implode(' ', $webdav_lines);
}
elseif ($webdav_all_complete && $local_state === 'complete')
{
$overall['state'] = 'complete';
@@ -224,12 +270,9 @@ elseif ($webdav_all_complete && $local_state === 'complete')
}
else
{
// Ein alter complete-Status darf niemals einen neuen Lauf als fertig markieren.
// Idle/unklare WebDAV-Zustände bleiben sichtbar, bis ein echter Lauf einen
// aktuellen queued/scan/running/complete/error-Zustand geschrieben hat.
$overall['state'] = $local_state === 'complete' ? 'idle' : $local_state;
$overall['message'] = $local_label;
if ($webdav_lines) $overall['current'] = implode(' ', $webdav_lines);
}
echo json_encode($overall, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
echo json_encode($overall, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

View File

@@ -1,7 +1,7 @@
<?php
/*
Plugin Name: Bratonien Tools
Version: 0.9.7.1.27
Version: 0.9.7.1.30
Description: Erweiterbare Administrationswerkzeuge fuer die Bratonien-Piwigo-Installation.
Plugin URI: https://github.com/Terranom674/Piwigo_Bratonien_Tools
Author: Bratonien

View File

@@ -23,7 +23,7 @@ $_SERVER['REQUEST_URI'] = '/';
$_SERVER['SCRIPT_NAME'] = '/plugins/bratonien_tools/runtime/lib/webdav-cache-warmup.php';
$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];
$_SERVER['QUERY_STRING'] = '';
$_SERVER['HTTP_USER_AGENT'] = 'Bratonien-WebDAV-Cache-Worker/0.9.7.1.17';
$_SERVER['HTTP_USER_AGENT'] = 'Bratonien-WebDAV-Cache-Worker/0.9.7.1.30';
$_SERVER['HTTPS'] = 'off';
require_once(PHPWG_ROOT_PATH.'include/common.inc.php');
@@ -54,6 +54,16 @@ function bratonien_tools_cache_warmup_status_file($connection_id)
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-webdav-warmup.status-'.(int)$connection_id.'.json';
}
function bratonien_tools_cache_warmup_cancel_file($connection_id)
{
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-webdav-warmup.cancel-'.(int)$connection_id;
}
function bratonien_tools_cache_warmup_cancel_pending($connection_id)
{
return is_file(bratonien_tools_cache_warmup_cancel_file($connection_id));
}
function bratonien_tools_cache_warmup_priority_file($state_dir)
{
return rtrim((string)$state_dir, '/').'/webdav-cache-warmup-priority-sync';
@@ -247,7 +257,7 @@ function bratonien_tools_cache_warmup_download(array $source, array $credentials
CURLOPT_RETURNTRANSFER=>false,
CURLOPT_FAILONERROR=>false,
CURLOPT_FILE=>$fp,
CURLOPT_USERAGENT=>'Bratonien-WebDAV-Cache-Worker/0.9.7.1.17',
CURLOPT_USERAGENT=>'Bratonien-WebDAV-Cache-Worker/0.9.7.1.30',
));
$ok = curl_exec($ch);
$errno = curl_errno($ch);
@@ -484,26 +494,70 @@ function bratonien_tools_cache_warmup_stage_pending(array $selected, array $inde
return $pending;
}
function bratonien_tools_cache_warmup_stage_completed_count(array $selected, array $index, $stage)
{
return count($selected) - count(bratonien_tools_cache_warmup_stage_pending($selected, $index, $stage));
}
function bratonien_tools_cache_warmup_run_stage($connection_id, $stage, array $selected, array &$index, array $credentials, $batch_size, $priority_file='')
{
$pending = bratonien_tools_cache_warmup_stage_pending($selected, $index, $stage);
if (!$pending) return array('failed'=>0, 'preempted'=>false, 'completed'=>0);
if (!$pending) return array('failed'=>0, 'deferred'=>0, 'waiting'=>false, 'preempted'=>false, 'cancelled'=>false, 'completed'=>0);
if (bratonien_tools_cache_warmup_cancel_pending($connection_id))
{
return array('failed'=>0, 'deferred'=>0, 'waiting'=>false, 'preempted'=>false, 'cancelled'=>true, 'completed'=>0);
}
if ($stage === 2 && bratonien_tools_cache_warmup_priority_pending($priority_file))
{
return array('failed'=>0, 'preempted'=>true, 'completed'=>0);
return array('failed'=>0, 'deferred'=>0, 'waiting'=>false, 'preempted'=>true, 'cancelled'=>false, 'completed'=>0);
}
$temp_root = PHPWG_ROOT_PATH.'upload/bratonien-webdav-warmup';
if (!is_dir($temp_root) && !@mkdir($temp_root, 0775, true) && !is_dir($temp_root)) throw new RuntimeException('Worker-Temp-Verzeichnis konnte nicht angelegt werden.');
$failed = 0;
$deferred = 0;
$completed = 0;
$batch_number = 0;
foreach (array_chunk(array_keys($pending), max(1, (int)$batch_size)) as $keys)
{
// Harte Batch-Grenze: Sobald ein Abbruchsignal existiert, darf dieser
// Batch gar nicht mehr begonnen und damit auch kein weiteres Original
// aus Nextcloud geladen werden.
if (bratonien_tools_cache_warmup_cancel_pending($connection_id))
{
return array('failed'=>$failed, 'deferred'=>$deferred, 'waiting'=>false, 'preempted'=>false, 'cancelled'=>true, 'completed'=>$completed);
}
if ($stage === 2 && bratonien_tools_cache_warmup_priority_pending($priority_file))
{
return array('failed'=>$failed, 'deferred'=>$deferred, 'waiting'=>false, 'preempted'=>true, 'cancelled'=>false, 'completed'=>$completed);
}
// Der Connector-Lock wird VOR jedem Download geprueft und fuer den
// kompletten Batch gemeinsam gehalten. Ist der Connector aktiv, werden
// exakt null Bilder dieses Batches geladen.
$batch_sync_lock = null;
$batch_sync_detail = '';
if (!bratonien_tools_cache_warmup_sync_guard($credentials, $batch_sync_lock, $batch_sync_detail))
{
bratonien_tools_cache_warmup_status($connection_id, 'waiting', 'Connector-Synchronisierung läuft. Der nächste Batch startet erst danach; es wurden für diesen Batch keine Nextcloud-Originale geladen.', array(
'stage'=>$stage,
'batch'=>$batch_number + 1,
'selected_total'=>count($selected),
'stage_completed'=>$completed,
'deferred_by_connector'=>true,
));
return array('failed'=>$failed, 'deferred'=>$deferred, 'waiting'=>true, 'preempted'=>false, 'cancelled'=>false, 'completed'=>$completed);
}
$batch_number++;
$dir = $temp_root.'/connection-'.(int)$connection_id.'-stage'.$stage.'-'.date('YmdHis').'-'.bin2hex(random_bytes(4));
if (!@mkdir($dir, 0775, true) && !is_dir($dir)) throw new RuntimeException('Worker-Batch-Verzeichnis konnte nicht angelegt werden.');
if (!@mkdir($dir, 0775, true) && !is_dir($dir))
{
bratonien_tools_cache_warmup_unlock($batch_sync_lock);
throw new RuntimeException('Worker-Batch-Verzeichnis konnte nicht angelegt werden.');
}
$downloads = array();
foreach ($keys as $key)
@@ -518,28 +572,42 @@ function bratonien_tools_cache_warmup_run_stage($connection_id, $stage, array $s
}
}
bratonien_tools_cache_warmup_status($connection_id, 'running', 'Piwigo verarbeitet WebDAV-Quellen aus dem Worker-Index.', array(
'stage'=>$stage,
'batch'=>$batch_number,
'batch_requested'=>count($keys),
'batch_downloaded'=>count($downloads),
'selected_total'=>count($selected),
'stage_completed'=>$completed,
));
$cancel_requested = bratonien_tools_cache_warmup_cancel_pending($connection_id);
bratonien_tools_cache_warmup_status(
$connection_id,
$cancel_requested ? 'cancelling' : 'running',
$cancel_requested
? 'Abbruch angefordert. Der bereits geladene aktuelle Batch wird vollständig beendet; danach ist Schluss.'
: 'Piwigo verarbeitet WebDAV-Quellen aus dem Worker-Index.',
array(
'stage'=>$stage,
'batch'=>$batch_number,
'batch_requested'=>count($keys),
'batch_downloaded'=>count($downloads),
'selected_total'=>count($selected),
'stage_completed'=>$completed,
'cancel_requested'=>$cancel_requested,
)
);
foreach ($downloads as $key=>$file)
{
$result = bratonien_tools_cache_warmup_process($pending[$key], $file, $credentials, $stage);
bratonien_tools_cache_warmup_log('source', array('stage'=>$stage, 'source'=>$key, 'ok'=>!empty($result['ok']), 'message'=>(string)($result['message'] ?? '')));
bratonien_tools_cache_warmup_log('source', array('stage'=>$stage, 'source'=>$key, 'ok'=>!empty($result['ok']), 'defer'=>!empty($result['defer']), 'message'=>(string)($result['message'] ?? '')));
if (!empty($result['fatal']))
{
foreach ($downloads as $cleanup) @unlink($cleanup);
@rmdir($dir);
bratonien_tools_cache_warmup_unlock($batch_sync_lock);
throw new RuntimeException('Sicherheitsabbruch bei '.$key.': '.(string)$result['message']);
}
if (empty($result['ok']))
{
$failed++;
// Temporäre Sperren oder parallel belegte Quellen sind kein Fehler.
// Der Index bleibt offen und der nächste Lauf versucht nur diese
// Quelle erneut.
if (!empty($result['defer'])) $deferred++;
else $failed++;
continue;
}
@@ -554,14 +622,20 @@ function bratonien_tools_cache_warmup_run_stage($connection_id, $stage, array $s
foreach ($downloads as $file) @unlink($file);
@rmdir($dir);
bratonien_tools_cache_warmup_unlock($batch_sync_lock);
if (bratonien_tools_cache_warmup_cancel_pending($connection_id))
{
return array('failed'=>$failed, 'deferred'=>$deferred, 'waiting'=>false, 'preempted'=>false, 'cancelled'=>true, 'completed'=>$completed);
}
if ($stage === 2 && bratonien_tools_cache_warmup_priority_pending($priority_file))
{
return array('failed'=>$failed, 'preempted'=>true, 'completed'=>$completed);
return array('failed'=>$failed, 'deferred'=>$deferred, 'waiting'=>false, 'preempted'=>true, 'cancelled'=>false, 'completed'=>$completed);
}
}
return array('failed'=>$failed, 'preempted'=>false, 'completed'=>$completed);
return array('failed'=>$failed, 'deferred'=>$deferred, 'waiting'=>false, 'preempted'=>false, 'cancelled'=>false, 'completed'=>$completed);
}
function bratonien_tools_cache_warmup_run($connection_id, $mode)
@@ -618,6 +692,7 @@ function bratonien_tools_cache_warmup_run($connection_id, $mode)
{
if ($mode === 'periodic' || $mode === 'manual') $index['last_periodic_at'] = time();
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
@unlink(bratonien_tools_cache_warmup_cancel_file($connection_id));
bratonien_tools_cache_warmup_status($connection_id, 'complete', 'Keine neuen oder geänderten Shadow-Quellen gefunden.', array('mode'=>$mode, 'shadow_sources'=>count($scan['sources'])));
return 0;
}
@@ -632,6 +707,29 @@ function bratonien_tools_cache_warmup_run($connection_id, $mode)
}
$stage1 = bratonien_tools_cache_warmup_run_stage($connection_id, 1, $selected, $index, $credentials, $settings['batch_size'], $priority_file);
if (!empty($stage1['cancelled']))
{
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
@unlink(bratonien_tools_cache_warmup_cancel_file($connection_id));
bratonien_tools_cache_warmup_status($connection_id, 'cancelled', 'Cache-Aufbau nach dem vollständig abgeschlossenen aktuellen Batch beendet.', array(
'mode'=>$mode,
'shadow_sources'=>count($scan['sources']),
'selected'=>count($selected),
'stage1_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 1),
'stage2_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 2),
'stage1_failed'=>(int)$stage1['failed'],
'stage1_deferred'=>(int)$stage1['deferred'],
'stage2_failed'=>0,
'stage2_deferred'=>0,
));
return 0;
}
if (!empty($stage1['waiting']))
{
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
return 0;
}
$stage2_candidates = array();
foreach ($selected as $key=>$item)
{
@@ -640,30 +738,79 @@ function bratonien_tools_cache_warmup_run($connection_id, $mode)
}
$stage2 = bratonien_tools_cache_warmup_run_stage($connection_id, 2, $stage2_candidates, $index, $credentials, $settings['batch_size'], $priority_file);
if (!empty($stage2['cancelled']))
{
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
@unlink(bratonien_tools_cache_warmup_cancel_file($connection_id));
bratonien_tools_cache_warmup_status($connection_id, 'cancelled', 'Cache-Aufbau nach dem vollständig abgeschlossenen aktuellen Batch beendet.', array(
'mode'=>$mode,
'shadow_sources'=>count($scan['sources']),
'selected'=>count($selected),
'stage1_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 1),
'stage2_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 2),
'stage1_failed'=>(int)$stage1['failed'],
'stage1_deferred'=>(int)$stage1['deferred'],
'stage2_failed'=>(int)$stage2['failed'],
'stage2_deferred'=>(int)$stage2['deferred'],
));
return 0;
}
if (!empty($stage2['waiting']))
{
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
return 0;
}
if (!empty($stage2['preempted']))
{
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
bratonien_tools_cache_warmup_status($connection_id, 'preempted', 'Stufe 2 wurde nach einem vollständigen Batch für neue Connector-Inhalte freigegeben.', array(
'mode'=>$mode,
'selected'=>count($selected),
'stage1_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 1),
'stage2_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 2),
'stage1_failed'=>(int)$stage1['failed'],
'stage1_deferred'=>(int)$stage1['deferred'],
'stage2_failed'=>(int)$stage2['failed'],
'stage2_deferred'=>(int)$stage2['deferred'],
));
return ((int)$stage1['failed'] + (int)$stage2['failed']) > 0 ? 2 : 0;
return 0;
}
if ($mode === 'periodic' || $mode === 'manual') $index['last_periodic_at'] = time();
bratonien_tools_webdav_source_index_save($state_dir, $connection_id, $index);
@unlink(bratonien_tools_cache_warmup_cancel_file($connection_id));
$failed = (int)$stage1['failed'] + (int)$stage2['failed'];
bratonien_tools_cache_warmup_status($connection_id, $failed ? 'error' : 'complete', $failed ? 'Worker-Lauf mit einzelnen Fehlern beendet.' : ($mode === 'rebuild' ? 'Piwigo hat den vollständigen Shadow-Bestand für den Cache-Rebuild verarbeitet.' : 'Worker-Lauf vollständig beendet.'), array(
$deferred = (int)$stage1['deferred'] + (int)$stage2['deferred'];
if ($failed > 0 || $deferred > 0)
{
bratonien_tools_cache_warmup_status($connection_id, 'partial', 'Worker-Lauf beendet; einzelne Quellen bleiben offen und werden beim nächsten Lauf erneut versucht. Der übrige Bestand wurde weiterverarbeitet.', array(
'mode'=>$mode,
'shadow_sources'=>count($scan['sources']),
'selected'=>count($selected),
'stage1_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 1),
'stage2_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 2),
'stage1_failed'=>(int)$stage1['failed'],
'stage1_deferred'=>(int)$stage1['deferred'],
'stage2_failed'=>(int)$stage2['failed'],
'stage2_deferred'=>(int)$stage2['deferred'],
));
return 0;
}
bratonien_tools_cache_warmup_status($connection_id, 'complete', $mode === 'rebuild' ? 'Piwigo hat den vollständigen Shadow-Bestand für den Cache-Rebuild verarbeitet.' : 'Worker-Lauf vollständig beendet.', array(
'mode'=>$mode,
'shadow_sources'=>count($scan['sources']),
'selected'=>count($selected),
'stage1_completed'=>(int)$stage1['completed'],
'stage2_completed'=>(int)$stage2['completed'],
'stage1_failed'=>(int)$stage1['failed'],
'stage2_failed'=>(int)$stage2['failed'],
'stage1_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 1),
'stage2_completed'=>bratonien_tools_cache_warmup_stage_completed_count($selected, $index, 2),
'stage1_failed'=>0,
'stage1_deferred'=>0,
'stage2_failed'=>0,
'stage2_deferred'=>0,
));
return $failed ? 2 : 0;
return 0;
}
finally
{

View File

@@ -23,7 +23,7 @@ $_SERVER['REQUEST_URI'] = '/';
$_SERVER['SCRIPT_NAME'] = '/plugins/bratonien_tools/runtime/webdav-warmup-dispatch.php';
$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];
$_SERVER['QUERY_STRING'] = '';
$_SERVER['HTTP_USER_AGENT'] = 'Bratonien-WebDAV-Worker-Dispatcher/0.9.7.1.23';
$_SERVER['HTTP_USER_AGENT'] = 'Bratonien-WebDAV-Worker-Dispatcher/0.9.7.1.29';
$_SERVER['HTTPS'] = 'off';
require_once(PHPWG_ROOT_PATH.'include/common.inc.php');
@@ -66,6 +66,43 @@ function bratonien_tools_webdav_dispatch_failure_lines(array $output)
return $failures;
}
function bratonien_tools_webdav_dispatch_only_connector_deferrals(array $output)
{
$seen = false;
foreach ($output as $line)
{
$line = trim((string)$line);
if ($line === '') continue;
if (strpos($line, '[BRAT-WORKER] fatal ') !== false || strpos($line, 'download_failed') !== false || stripos($line, 'RESTORE FEHLGESCHLAGEN') !== false)
{
return false;
}
if (strpos($line, ' ok=0 ') !== false)
{
$seen = true;
if (strpos($line, 'message=Connector-Synchronisierung läuft.') === false && strpos($line, 'message=Connector-Mapping hat sich seit dem Batch-Download geändert.') === false && strpos($line, 'message=Shadow-Tree hat sich seit dem Batch-Download geändert.') === false)
{
return false;
}
}
}
return $seen;
}
function bratonien_tools_webdav_dispatch_connector_busy($state_dir)
{
$lock = @fopen(rtrim((string)$state_dir, '/').'/webdav-sync.lock', 'c');
if (!$lock) return false;
if (@flock($lock, LOCK_SH | LOCK_NB))
{
@flock($lock, LOCK_UN);
fclose($lock);
return false;
}
fclose($lock);
return true;
}
$mode = 'periodic';
$wait = false;
$connection_filter = 0;
@@ -147,6 +184,20 @@ foreach (bratonien_tools_nc_connector_connections() as $connection)
}
@chmod($priority_file, 0664);
}
elseif (bratonien_tools_webdav_dispatch_connector_busy($state_dir))
{
if (function_exists('bratonien_tools_webdav_warmup_write_status'))
{
bratonien_tools_webdav_warmup_write_status(
$connection_id,
'waiting',
'Connector-Synchronisierung läuft. Der Cache-Worker startet erst danach; es werden vorher keine Nextcloud-Originale geladen.',
array('mode'=>$mode, 'deferred_by_connector'=>true)
);
}
fwrite(STDOUT, "WebDAV-Worker für Verbindung #{$connection_id} wartet auf den Connector-Sync.\n");
continue;
}
$worker_command = escapeshellarg(PHP_BINARY).' '.escapeshellarg($worker)
.' --connection-id='.$connection_id
@@ -167,12 +218,32 @@ foreach (bratonien_tools_nc_connector_connections() as $connection)
}
if ($exit !== 0)
{
$deferred = bratonien_tools_webdav_dispatch_only_connector_deferrals($output);
if ($deferred)
{
if (function_exists('bratonien_tools_webdav_warmup_write_status'))
{
$existing = bratonien_tools_webdav_dispatch_existing_status($connection_id);
$extra = is_array($existing) ? $existing : array();
unset($extra['state'], $extra['message'], $extra['connection_id'], $extra['updated_at']);
$extra['worker_exit_code'] = (int)$exit;
$extra['deferred_by_connector'] = true;
bratonien_tools_webdav_warmup_write_status(
$connection_id,
'waiting',
'Connector-Synchronisierung hat während des Batches Vorrang erhalten. Der bisherige Indexstand bleibt erhalten; die offenen Quellen werden später fortgesetzt und gelten nicht als Fehler.',
$extra
);
}
continue;
}
$result = $exit;
if (function_exists('bratonien_tools_webdav_warmup_write_status'))
{
$existing = bratonien_tools_webdav_dispatch_existing_status($connection_id);
$failures = bratonien_tools_webdav_dispatch_failure_lines($output);
$terminal = is_array($existing) && in_array((string)($existing['state'] ?? ''), array('error','fatal','preempted'), true);
$terminal = is_array($existing) && in_array((string)($existing['state'] ?? ''), array('error','fatal','preempted','cancelled'), true);
if ($terminal)
{