Skip to content

[BUG]: ___retry_and_timeout #227

@ndupont-iris

Description

@ndupont-iris

Description

Sometimes, server does not respond : client script hangs until being killed

MicroStrategy Support Case

CS0944624 Implementing safe calls with mstrio

Steps To Reproduce

Difficult to reproduce since it works most of the time. When server does not respond there is no default timeout (AFAIK).

Code Snippet

Below an example of a custom function which may hang undefinitly

def create_user(conn: Connection, row: pd.Series, membership_id: str) -> str:
    """
    Creates a new user in the system using the provided connection, user data, and membership ID.

    Args:
        conn: Database connection object.
        row (dict): Dictionary containing user information with keys 'user', 'fullname', 'ldaplink', and 'trutedlogin'.
        membership_id: The ID of the membership to assign to the user.

    Returns:
        str: "Created" if the user was successfully created,
             "Already Exists" if the user already exists,
             or an error message string if an exception occurred.

    Logs:
        - Info log when a user is created or already exists.
        - Error log if user creation fails for another reason.
    """
    try:
        User.create(
            connection=conn,
            username=row["user"],
            full_name=row["fullname"],
            ldapdn=row["ldaplink"],
            trust_id=row["trutedlogin"],
            memberships=[membership_id],
            standard_auth=False,
        )
        logger.info(f"User created: {row['user']}")
        return "Created"
    except Exception as e:
        if "already exists" in str(e):
            logger.info(f"User already exists: {row['user']}")
            return "Already Exists"
        logger.error(f"Error creating user: {e}")
        return f"Error: {e}"

Additional Context (optional)

We developed a workaround through a decorator in our script below

def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(retries):
                try:
                    return func(*args, **kwargs)
                except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
                    logger.warning(f"Attempt {attempt+1} failed: {e}")
                    time.sleep(delay)
                except Exception as e:
                    logger.error(f"Unhandled exception: {e}")
                    break
            return "Error: API server not responding"

        return wrapper

    return decorator

We use it on the previous code like below

@safe_call(retries=3, delay=2)
def create_user(conn: Connection, row: pd.Series, membership_id: str) -> str:

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingnewThe item has just been created and was not seen by MSTR developer yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions