-
Notifications
You must be signed in to change notification settings - Fork 127
[FEATURE] WebSocket-based Concurrency Architecture #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release
Are you sure you want to change the base?
Changes from all commits
85c47c6
e0a063d
95563b0
1584902
3601357
600acb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| # All rights reserved. | ||
| # | ||
| # This source code is licensed under the BSD-style license found in the | ||
| # LICENSE file in the root directory of this source tree. | ||
|
|
||
| """Custom exceptions for environment server operations.""" | ||
|
|
||
| from typing import Optional | ||
|
|
||
|
|
||
| class OpenEnvError(Exception): | ||
| """Base exception for all OpenEnv errors.""" | ||
|
|
||
| pass | ||
|
|
||
|
|
||
| class ConcurrencyConfigurationError(OpenEnvError): | ||
| """ | ||
| Raised when an environment is misconfigured for concurrent sessions. | ||
|
|
||
| This error is raised during server startup when max_concurrent_envs > 1 | ||
| is specified for an environment that is not marked as CONCURRENCY_SAFE. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| environment_name: str, | ||
| max_concurrent_envs: int, | ||
| message: Optional[str] = None, | ||
| ): | ||
| self.environment_name = environment_name | ||
| self.max_concurrent_envs = max_concurrent_envs | ||
|
|
||
| if message is None: | ||
| message = ( | ||
| f"Environment '{environment_name}' is not marked as CONCURRENCY_SAFE. " | ||
| f"Cannot run with max_concurrent_envs={max_concurrent_envs}. " | ||
| f"Either set max_concurrent_envs=1 or ensure the environment " | ||
| f"properly isolates session state and set CONCURRENCY_SAFE=True." | ||
| ) | ||
|
|
||
| super().__init__(message) | ||
|
|
||
|
|
||
| class SessionCapacityError(OpenEnvError): | ||
| """ | ||
| Raised when the server cannot accept new sessions due to capacity limits. | ||
|
|
||
| This error is raised when a new WebSocket connection is attempted but | ||
| the server has already reached max_concurrent_envs active sessions. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| active_sessions: int, | ||
| max_sessions: int, | ||
| message: Optional[str] = None, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for all other exceptions defined in this module |
||
| ): | ||
| self.active_sessions = active_sessions | ||
| self.max_sessions = max_sessions | ||
|
|
||
| if message is None: | ||
| message = ( | ||
| f"Server at capacity: {active_sessions}/{max_sessions} sessions active. " | ||
| f"Cannot accept new connections." | ||
| ) | ||
|
|
||
| super().__init__(message) | ||
|
|
||
|
|
||
| class SessionNotFoundError(OpenEnvError): | ||
| """Raised when attempting to access a session that does not exist.""" | ||
|
|
||
| def __init__(self, session_id: str, message: Optional[str] = None): | ||
| self.session_id = session_id | ||
|
|
||
| if message is None: | ||
| message = f"Session '{session_id}' not found." | ||
|
|
||
| super().__init__(message) | ||
|
|
||
|
|
||
| class SessionCreationError(OpenEnvError): | ||
| """Raised when a session cannot be created.""" | ||
|
|
||
| def __init__(self, reason: str, message: Optional[str] = None): | ||
| self.reason = reason | ||
|
|
||
| if message is None: | ||
| message = f"Failed to create session: {reason}" | ||
|
|
||
| super().__init__(message) | ||
|
|
||
|
|
||
| class EnvironmentFactoryError(OpenEnvError): | ||
| """Raised when the environment factory fails to create an instance.""" | ||
|
|
||
| def __init__(self, factory_name: str, cause: Exception): | ||
| self.factory_name = factory_name | ||
| self.cause = cause | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cause is already bound to the exception object when doing So instead of doing If you need to programmatically retrieve the cause of an exception, you should check |
||
|
|
||
| message = f"Environment factory '{factory_name}' failed to create instance: {cause}" | ||
|
|
||
| super().__init__(message) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not add the option to set a custom error message. Looking at the code, it doesn't seems that you are passing the optional message once so better to not implement it (and worth case, it's possible to implement it later or to subclass this exception for a different error message).