fix: [30388f42] Mache den Worker robust gegenüber gelöschten Entitäten
- Fügt eine zum hinzu, die bei einem HTTP 404 Fehler ausgelöst wird. - Fängt diese im ab. - Markiert Jobs, die sich auf nicht (mehr) existierende Kontakte oder Personen beziehen, als anstatt . - Dies verhindert, dass die Fehlerwarteschlange mit Jobs für gelöschte Entitäten überläuft, was das Hauptproblem der "failed"-Jobs löst.
This commit is contained in:
@@ -8,6 +8,10 @@ import logging
|
|||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger("superoffice-client")
|
logger = logging.getLogger("superoffice-client")
|
||||||
|
|
||||||
|
class ContactNotFoundException(Exception):
|
||||||
|
"""Custom exception for 404 errors on Contact/Person lookups."""
|
||||||
|
pass
|
||||||
|
|
||||||
class SuperOfficeClient:
|
class SuperOfficeClient:
|
||||||
"""A client for interacting with the SuperOffice REST API."""
|
"""A client for interacting with the SuperOffice REST API."""
|
||||||
|
|
||||||
@@ -117,6 +121,11 @@ class SuperOfficeClient:
|
|||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
|
# Explicitly handle 404 Not Found for GET requests
|
||||||
|
if method == "GET" and e.response.status_code == 404:
|
||||||
|
logger.warning(f"🔍 404 Not Found for GET request to {endpoint}.")
|
||||||
|
raise ContactNotFoundException(f"Entity not found at {endpoint}") from e
|
||||||
|
|
||||||
logger.error(f"❌ API {method} Error for {endpoint} (Status: {e.response.status_code}): {e.response.text}")
|
logger.error(f"❌ API {method} Error for {endpoint} (Status: {e.response.status_code}): {e.response.text}")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import requests
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from queue_manager import JobQueue
|
from queue_manager import JobQueue
|
||||||
from superoffice_client import SuperOfficeClient
|
from superoffice_client import SuperOfficeClient, ContactNotFoundException
|
||||||
from config import settings
|
from config import settings
|
||||||
|
|
||||||
# Setup Logging
|
# Setup Logging
|
||||||
@@ -39,7 +39,7 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
|||||||
Returns: (STATUS, MESSAGE)
|
Returns: (STATUS, MESSAGE)
|
||||||
STATUS: 'SUCCESS', 'SKIPPED', 'RETRY', 'FAILED'
|
STATUS: 'SUCCESS', 'SKIPPED', 'RETRY', 'FAILED'
|
||||||
"""
|
"""
|
||||||
logger.info(f"--- [WORKER v1.9.1] Processing Job {job['id']} ({job['event_type']}) ---")
|
logger.info(f"--- [WORKER v1.9.2] Processing Job {job['id']} ({job['event_type']}) ---")
|
||||||
payload = job['payload']
|
payload = job['payload']
|
||||||
event_low = job['event_type'].lower()
|
event_low = job['event_type'].lower()
|
||||||
|
|
||||||
@@ -93,6 +93,10 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
|||||||
contact_id = contact_obj.get("ContactId")
|
contact_id = contact_obj.get("ContactId")
|
||||||
elif "ContactId" in person_details:
|
elif "ContactId" in person_details:
|
||||||
contact_id = person_details.get("ContactId")
|
contact_id = person_details.get("ContactId")
|
||||||
|
except ContactNotFoundException:
|
||||||
|
msg = f"Skipping job because Person ID {person_id} was not found in SuperOffice (likely deleted)."
|
||||||
|
logger.warning(msg)
|
||||||
|
return ("SKIPPED", msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Failed to fetch person details for {person_id}: {e}")
|
logger.warning(f"Failed to fetch person details for {person_id}: {e}")
|
||||||
|
|
||||||
@@ -121,10 +125,6 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
|||||||
select=["Name", "UrlAddress", "Urls", "UserDefinedFields", "Address", "OrgNr", "Associate"]
|
select=["Name", "UrlAddress", "Urls", "UserDefinedFields", "Address", "OrgNr", "Associate"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# ABSOLUTE SAFETY CHECK
|
|
||||||
if contact_details is None:
|
|
||||||
raise ValueError(f"SuperOffice API returned None for Contact {contact_id}. Possible timeout or record locked.")
|
|
||||||
|
|
||||||
crm_name = contact_details.get("Name", "Unknown")
|
crm_name = contact_details.get("Name", "Unknown")
|
||||||
|
|
||||||
# Safely get Associate object
|
# Safely get Associate object
|
||||||
@@ -181,6 +181,9 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
|||||||
logger.info(f"🎯 CAMPAIGN DETECTED: '{campaign_tag}'")
|
logger.info(f"🎯 CAMPAIGN DETECTED: '{campaign_tag}'")
|
||||||
else:
|
else:
|
||||||
logger.info("ℹ️ No Campaign Tag found (Field is empty).")
|
logger.info("ℹ️ No Campaign Tag found (Field is empty).")
|
||||||
|
except ContactNotFoundException:
|
||||||
|
# This is not critical, we can proceed without a campaign tag
|
||||||
|
logger.warning(f"Could not fetch campaign tag: Person {person_id} not found.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not fetch campaign tag: {e}")
|
logger.warning(f"Could not fetch campaign tag: {e}")
|
||||||
|
|
||||||
@@ -198,6 +201,10 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(f"Error mapping vertical ID {val_str}: {ex}")
|
logger.error(f"Error mapping vertical ID {val_str}: {ex}")
|
||||||
|
|
||||||
|
except ContactNotFoundException:
|
||||||
|
msg = f"Skipping job because Contact ID {contact_id} was not found in SuperOffice (likely deleted)."
|
||||||
|
logger.warning(msg)
|
||||||
|
return ("SKIPPED", msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to fetch contact details for {contact_id}: {e}")
|
logger.error(f"Failed to fetch contact details for {contact_id}: {e}")
|
||||||
raise Exception(f"SuperOffice API Failure: {e}")
|
raise Exception(f"SuperOffice API Failure: {e}")
|
||||||
@@ -231,7 +238,10 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
|||||||
|
|
||||||
# Fetch fresh Contact Data for comparison
|
# Fetch fresh Contact Data for comparison
|
||||||
contact_data = so_client.get_contact(contact_id)
|
contact_data = so_client.get_contact(contact_id)
|
||||||
if not contact_data: return ("FAILED", "Could not fetch contact for patch")
|
if not contact_data:
|
||||||
|
# This can happen if the contact was deleted between the CE call and now
|
||||||
|
logger.warning(f"Could not re-fetch contact {contact_id} for patch (deleted?). Skipping patch.")
|
||||||
|
return ("SKIPPED", "Contact deleted post-analysis")
|
||||||
|
|
||||||
# SAFE GET FOR COMPARISON
|
# SAFE GET FOR COMPARISON
|
||||||
current_udfs = safe_get_udfs(contact_data)
|
current_udfs = safe_get_udfs(contact_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user