From 48ce8d8e73df0f8a42c75134267b4ceb043a551d Mon Sep 17 00:00:00 2001 From: root Date: Thu, 23 Oct 2025 12:49:45 +0000 Subject: [PATCH] Add exponential backoff for Google API calls - Use backoff library with 5s base, max 5 tries for HttpError 429/5xx - Decorate Google functions: ensure_calendar, fetch_events, create/update/delete events - Add backoff to requirements.txt --- bitbylaw/requirements.txt | 3 ++- .../steps/advoware_cal_sync/calendar_sync_event_step.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bitbylaw/requirements.txt b/bitbylaw/requirements.txt index 5e705310..f0f83cf1 100644 --- a/bitbylaw/requirements.txt +++ b/bitbylaw/requirements.txt @@ -5,4 +5,5 @@ requests redis python-dotenv google-api-python-client -google-auth \ No newline at end of file +google-auth +backoff \ No newline at end of file diff --git a/bitbylaw/steps/advoware_cal_sync/calendar_sync_event_step.py b/bitbylaw/steps/advoware_cal_sync/calendar_sync_event_step.py index c6674dce..c5c6a653 100644 --- a/bitbylaw/steps/advoware_cal_sync/calendar_sync_event_step.py +++ b/bitbylaw/steps/advoware_cal_sync/calendar_sync_event_step.py @@ -4,6 +4,7 @@ import os import datetime from datetime import timedelta import pytz +import backoff from googleapiclient.discovery import build from googleapiclient.errors import HttpError from google.oauth2 import service_account @@ -60,6 +61,7 @@ async def get_google_service(): logger.error(f"Failed to initialize Google service: {e}") raise +@backoff.on_exception(backoff.expo, HttpError, max_tries=5, base=5, giveup=lambda e: e.resp.status not in [429, 500, 502, 503, 504]) async def ensure_google_calendar(service, employee_kuerzel): """Ensure Google Calendar exists for employee.""" calendar_name = f"AW-{employee_kuerzel}" @@ -106,6 +108,7 @@ async def fetch_advoware_appointments(advoware, employee_kuerzel): logger.error(f"Failed to fetch Advoware appointments: {e}") raise +@backoff.on_exception(backoff.expo, HttpError, max_tries=5, base=5, giveup=lambda e: e.resp.status not in [429, 500, 502, 503, 504]) async def fetch_google_events(service, calendar_id): """Fetch Google events in range.""" try: @@ -287,6 +290,7 @@ async def delete_advoware_appointment(advoware, frnr): logger.error(f"Failed to delete Advoware appointment {frnr}: {e}") raise +@backoff.on_exception(backoff.expo, HttpError, max_tries=5, base=5, giveup=lambda e: e.resp.status not in [429, 500, 502, 503, 504]) async def create_google_event(service, calendar_id, data): """Create Google event from standardized data.""" start_dt = data['start'].astimezone(BERLIN_TZ) @@ -320,6 +324,7 @@ async def create_google_event(service, calendar_id, data): logger.error(f"Failed to create Google event: {e}") raise +@backoff.on_exception(backoff.expo, HttpError, max_tries=5, base=5, giveup=lambda e: e.resp.status not in [429, 500, 502, 503, 504]) async def update_google_event(service, calendar_id, event_id, data): """Update Google event.""" start_dt = data['start'].astimezone(BERLIN_TZ) @@ -351,6 +356,7 @@ async def update_google_event(service, calendar_id, event_id, data): logger.error(f"Failed to update Google event {event_id}: {e}") raise +@backoff.on_exception(backoff.expo, HttpError, max_tries=5, base=5, giveup=lambda e: e.resp.status not in [429, 500, 502, 503, 504]) async def delete_google_event(service, calendar_id, event_id): """Delete Google event.""" try: