Skip to content

Conversation

@swaroopAkkineniWorkos
Copy link

@swaroopAkkineniWorkos swaroopAkkineniWorkos commented Nov 11, 2025

Description

Updating python library sdk to always generating an idempotencyKey if not present when POSTing audit-logs events.
Also updated to add retryability to endpoints. I set it up to not be enabled by default

@linear
Copy link

linear bot commented Nov 11, 2025

@swaroopAkkineniWorkos
Copy link
Author

/review

@swaroopAkkineniWorkos swaroopAkkineniWorkos added the enhancement New feature or request label Nov 11, 2025
@swaroopAkkineniWorkos swaroopAkkineniWorkos marked this pull request as ready for review November 11, 2025 14:20
@swaroopAkkineniWorkos swaroopAkkineniWorkos requested a review from a team as a code owner November 11, 2025 14:20
@swaroopAkkineniWorkos swaroopAkkineniWorkos requested review from a team, PavanKulkarni, ajworkos, csrbarber, gcarvelli, jonatascastro12, katie-west and nave91 and removed request for a team and PavanKulkarni November 11, 2025 14:20
@swaroopAkkineniWorkos swaroopAkkineniWorkos changed the title Ent 3983 python idempotency retry Default to adding idempotecy-key to audit-logs/events if not present, added in retryability logic to endpoints Nov 11, 2025
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 11, 2025

Greptile Overview

Greptile Summary

This PR implements automatic retry logic with exponential backoff and idempotency key auto-generation for audit log events.

Key Changes

  • Added RetryConfig dataclass to configure retry behavior (max retries, delays, jitter)
  • Implemented retry logic in both SyncHTTPClient and AsyncHTTPClient with exponential backoff
  • Retries on status codes 408, 500, 502, 504 and network/timeout exceptions
  • Auto-generates UUID v4 for idempotency keys when not provided in AuditLogs.create_event()
  • Enables retries by default for audit log event creation with RetryConfig()
  • Comprehensive test coverage for both sync and async retry scenarios

Implementation Details

  • Retry logic uses exponential backoff: base_delay * 2^attempt capped at max_delay
  • Adds 25% jitter to prevent thundering herd
  • Default configuration: 3 max retries, 1s base delay, 30s max delay
  • Retry logic is opt-in via retry_config parameter on request() method
  • Idempotency keys ensure safe retries for audit log events

The implementation follows best practices for resilient HTTP clients and includes excellent test coverage. The cursor configuration files (.cursor/) are unrelated to the main feature but appear to be developer tooling additions.

Confidence Score: 5/5

  • This PR is safe to merge with high confidence
  • The implementation is well-designed with comprehensive test coverage for both sync and async clients. The retry logic correctly handles retryable errors, implements exponential backoff with jitter, and respects max retry limits. Auto-generation of idempotency keys ensures safe retries for audit log events. The code follows established patterns in the codebase and includes proper error handling. All custom security rules are followed (no logging of sensitive fields, no SQL concatenation, no CORS issues).
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
workos/utils/_base_http_client.py 5/5 Added retry configuration dataclass and helper methods for exponential backoff with jitter
workos/utils/http_client.py 5/5 Implemented retry logic with exponential backoff for both sync and async HTTP clients
workos/audit_logs.py 5/5 Added automatic UUID v4 generation for idempotency keys and enabled retries for audit log events
tests/test_http_client_retry.py 5/5 Comprehensive test suite for retry logic covering status codes, network errors, and backoff behavior
tests/test_audit_logs.py 5/5 Added test to verify auto-generation of idempotency keys when not explicitly provided

Sequence Diagram

sequenceDiagram
    participant Client as SDK Client
    participant AuditLogs as AuditLogs Module
    participant HTTPClient as HTTP Client
    participant Retry as Retry Logic
    participant API as WorkOS API

    Client->>AuditLogs: create_event(organization_id, event)
    AuditLogs->>AuditLogs: Generate UUID v4 for idempotency key
    AuditLogs->>HTTPClient: request(path, headers, retry_config)
    
    loop Retry Loop (max 3 attempts)
        HTTPClient->>API: POST /audit_logs/events
        
        alt Success (2xx)
            API-->>HTTPClient: 200 OK
            HTTPClient-->>AuditLogs: Success
            AuditLogs-->>Client: None
        else Retryable Error (408, 500, 502, 504)
            API-->>HTTPClient: Error Response
            HTTPClient->>Retry: Check if retryable
            Retry->>Retry: Calculate backoff delay
            Retry->>Retry: Sleep with exponential backoff + jitter
            HTTPClient->>API: Retry POST /audit_logs/events
        else Network/Timeout Error
            API--xHTTPClient: Connection Error
            HTTPClient->>Retry: Check if retryable
            Retry->>Retry: Calculate backoff delay
            Retry->>Retry: Sleep with exponential backoff + jitter
            HTTPClient->>API: Retry POST /audit_logs/events
        else Non-Retryable Error (4xx except 408)
            API-->>HTTPClient: 4xx Error
            HTTPClient-->>AuditLogs: Raise Exception
            AuditLogs-->>Client: Exception
        end
    end
    
    Note over HTTPClient,API: Max retries exhausted
    HTTPClient-->>AuditLogs: Raise Exception
    AuditLogs-->>Client: Exception
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

swaroopakkineni added 2 commits November 11, 2025 09:29
Copy link

@nave91 nave91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes are perfect!

I found an issue before with this sdk: Can we return the response object when creating the audit log event? Especially for cases where schema validation fails for an event, the caller has no way of knowing that.

headers["idempotency-key"] = idempotency_key

# Enable retries for audit log event creation with default retryConfig
self._http_client.request(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we return this response object back to the user?

I think we should also add some test cases when the schema validation fails.

headers["idempotency-key"] = idempotency_key
# Auto-generate UUID v4 if not provided
if idempotency_key is None:
idempotency_key = str(uuid.uuid4())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question - should we prefix with a string so we can differentiate sdk generated key vs customer?



@dataclass
class RetryConfig:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 for using dataclasses, this keeps the retry config abstracted away 👌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

3 participants