Add sync recovery commands for interrupted syncs
- Add wp mls recovery list to show resumable syncs - Add wp mls recovery auto to auto-resume most recent failed sync - Add wp mls recovery cleanup to mark stale syncs (>1hr) as failed - Track last_next_link during incremental sync pagination - Add get_resumable_syncs(), cleanup_stale_syncs(), auto_resume() methods 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -317,6 +317,17 @@ class MLS_Sync_Engine {
|
||||
|
||||
// Check for next page
|
||||
if (isset($response['@odata.nextLink'])) {
|
||||
// Save progress for resume capability
|
||||
if (!$dry_run) {
|
||||
$this->update_sync_state(array(
|
||||
'last_next_link' => $response['@odata.nextLink'],
|
||||
'records_processed' => $this->stats['processed'],
|
||||
'records_created' => $this->stats['created'],
|
||||
'records_updated' => $this->stats['updated'],
|
||||
'records_deleted' => $this->stats['deleted'],
|
||||
));
|
||||
}
|
||||
|
||||
$start_time = microtime(true);
|
||||
$this->emit_progress('api_request', array(
|
||||
'method' => 'GET',
|
||||
@@ -809,4 +820,87 @@ class MLS_Sync_Engine {
|
||||
'stats' => $this->db->get_stats(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resumable (failed or interrupted) syncs
|
||||
*
|
||||
* @return array List of resumable sync states
|
||||
*/
|
||||
public function get_resumable_syncs() {
|
||||
global $wpdb;
|
||||
|
||||
// Find failed syncs that have a next_link (can be resumed)
|
||||
// Also find "running" syncs older than 1 hour (likely interrupted)
|
||||
$one_hour_ago = date('Y-m-d H:i:s', strtotime('-1 hour'));
|
||||
|
||||
return $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT * FROM {$this->db->sync_state_table()}
|
||||
WHERE (status = 'failed' AND last_next_link IS NOT NULL)
|
||||
OR (status = 'running' AND updated_at < %s)
|
||||
ORDER BY updated_at DESC",
|
||||
$one_hour_ago
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent resumable sync
|
||||
*
|
||||
* @return object|null Sync state or null
|
||||
*/
|
||||
public function get_latest_resumable() {
|
||||
$resumable = $this->get_resumable_syncs();
|
||||
return !empty($resumable) ? $resumable[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark stale running syncs as failed
|
||||
* Call this on startup to clean up interrupted syncs
|
||||
*
|
||||
* @param int $hours_threshold Hours after which running sync is considered stale
|
||||
* @return int Number of syncs marked as failed
|
||||
*/
|
||||
public function cleanup_stale_syncs($hours_threshold = 1) {
|
||||
global $wpdb;
|
||||
|
||||
$threshold_time = date('Y-m-d H:i:s', strtotime("-{$hours_threshold} hour"));
|
||||
|
||||
$updated = $wpdb->query($wpdb->prepare(
|
||||
"UPDATE {$this->db->sync_state_table()}
|
||||
SET status = 'failed', last_error = 'Sync interrupted (stale)'
|
||||
WHERE status = 'running' AND updated_at < %s",
|
||||
$threshold_time
|
||||
));
|
||||
|
||||
if ($updated > 0) {
|
||||
$this->logger->info('Cleaned up stale syncs', array('count' => $updated));
|
||||
}
|
||||
|
||||
return $updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-resume the most recent failed/interrupted sync
|
||||
*
|
||||
* @param callable|null $progress_callback Progress callback
|
||||
* @return array|null Sync results or null if nothing to resume
|
||||
*/
|
||||
public function auto_resume($progress_callback = null) {
|
||||
// First clean up any stale syncs
|
||||
$this->cleanup_stale_syncs();
|
||||
|
||||
// Find the most recent resumable sync
|
||||
$resumable = $this->get_latest_resumable();
|
||||
|
||||
if (!$resumable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->logger->info('Auto-resuming sync', array(
|
||||
'sync_id' => $resumable->id,
|
||||
'type' => $resumable->sync_type,
|
||||
'processed' => $resumable->records_processed,
|
||||
));
|
||||
|
||||
return $this->resume_sync($resumable->id, $progress_callback);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user