-
Notifications
You must be signed in to change notification settings - Fork 45
LCORE-455: Removed global vars to make compatible with uvicorn workers > 1 #604
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
Conversation
WalkthroughThis PR replaces module-level auth dependency variables with inline Depends(get_auth_dependency()) across many FastAPI endpoints, adjusts related imports, and introduces lazy-loading of authentication configuration in authentication/init.py. It also refactors application startup to use FastAPI’s lifespan context and updates the CLI to set a config path environment variable for worker-local config loading. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant FastAPI App
participant Lifespan as Lifespan Context
participant Config
participant Llama as Llama Stack Client
participant MCP as MCP Registry
participant DB
User->>FastAPI App: Start process
activate FastAPI App
FastAPI App->>Lifespan: enter()
activate Lifespan
Lifespan->>Config: Load configuration
Lifespan->>Llama: Initialize / check version
Lifespan->>MCP: Register servers
Lifespan->>DB: Initialize and create tables
Lifespan-->>FastAPI App: ready
deactivate Lifespan
note over FastAPI App: App serves requests
sequenceDiagram
autonumber
participant Client
participant Endpoint
participant DI as Depends(get_auth_dependency())
participant Auth as authentication.get_auth_dependency
participant Cfg as configuration
participant FS as File Loader
Client->>Endpoint: HTTP request
Endpoint->>DI: Resolve dependency
DI->>Auth: get_auth_dependency()
Auth->>Cfg: Read auth module
alt Config not loaded (LogicError)
Auth->>FS: Load from LIGHTSPEED_STACK_CONFIG_PATH
FS-->>Cfg: Configuration loaded
Auth->>Cfg: Re-read auth module
end
Auth-->>DI: Auth dependency
DI-->>Endpoint: Auth tuple
Endpoint-->>Client: Response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
src/app/endpoints/models.py (1)
77-79
: Avoid logging secrets; redact Llama Stack configuration.Logging the entire configuration at INFO risks leaking API keys and sensitive data. Log only non-secret fields (e.g., url, use_as_library_client) and move to DEBUG if needed.
-logger.info("Llama stack config: %s", llama_stack_configuration) +logger.info( + "Llama stack configured: url=%s, library_client=%s", + getattr(llama_stack_configuration, "url", "<unset>"), + getattr(llama_stack_configuration, "use_as_library_client", "<unset>"), +)src/app/endpoints/query.py (3)
199-201
: Avoid logging secrets; redact Llama Stack configuration.Same concern as in models.py. Do not print the full configuration; log only safe fields.
-logger.info("Llama stack config: %s", configuration.llama_stack_configuration) +safe_cfg = configuration.llama_stack_configuration +logger.info( + "Llama stack configured: url=%s, library_client=%s", + getattr(safe_cfg, "url", "<unset>"), + getattr(safe_cfg, "use_as_library_client", "<unset>"), +)
317-321
: Bug: constructing AnyUrl at runtime will raise. Pass a str and let Pydantic validate.pydantic.AnyUrl is a type, not a runtime constructor. AnyUrl(chunk.source) will error.
- doc_url=( - AnyUrl(chunk.source) - if chunk.source.startswith("http") - else None - ), + doc_url=chunk.source if chunk.source.startswith("http") else None,
309-334
: Referenced documents computed but not used (shadowing bug).You build referenced_docs from rag_chunks but return the earlier referenced_documents from retrieve_response. Use the newly built list when present.
- referenced_docs = [] + referenced_docs = [] doc_sources = set() for chunk in summary.rag_chunks: if chunk.source and chunk.source not in doc_sources: doc_sources.add(chunk.source) referenced_docs.append( ReferencedDocument( - doc_url=( - AnyUrl(chunk.source) - if chunk.source.startswith("http") - else None - ), + doc_url=chunk.source if chunk.source.startswith("http") else None, doc_title=chunk.source, ) ) logger.info("Building final response...") + # Prefer chunk-derived docs when available, otherwise keep those parsed from turn steps + referenced_documents = referenced_docs or referenced_documents response = QueryResponse( conversation_id=conversation_id, response=summary.llm_response, rag_chunks=summary.rag_chunks if summary.rag_chunks else [], tool_calls=tool_calls if tool_calls else None, - referenced_documents=referenced_documents, + referenced_documents=referenced_documents, )src/app/main.py (2)
55-73
: Fix app metadata init to avoid pre-load config access; update after startup.Initialize with placeholders, then set title/summary/description inside lifespan after configuration is loaded.
-app = FastAPI( - title=f"{service_name} service - OpenAPI", - summary=f"{service_name} service API specification.", - description=f"{service_name} service API specification.", +app = FastAPI( + title="Lightspeed service - OpenAPI", + summary="Lightspeed service API specification.", + description="Lightspeed service API specification.", version=version.__version__, @@ - lifespan=lifespan, + lifespan=lifespan, )And inside lifespan, right after loading configuration:
@@ - configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"]) + # Load configuration and update app metadata + configuration.load_configuration(config_path) + service_name = configuration.configuration.name + _app.title = f"{service_name} service - OpenAPI" + _app.summary = f"{service_name} service API specification." + _app.description = f"{service_name} service API specification."
86-88
: Middleware decorator must use 'http'.@app.middleware("") is invalid; use "http" so the middleware registers.
-@app.middleware("") +@app.middleware("http") async def rest_api_metrics(
🧹 Nitpick comments (3)
src/app/endpoints/models.py (2)
7-7
: Use canonical FastAPI import for Depends.Import Depends from fastapi, not fastapi.params, to match project convention and avoid confusion. As per coding guidelines.
-from fastapi.params import Depends +from fastapi import Depends
50-51
: Add response_model for better schema guarantees.Expose the success schema via response_model to enforce and document the response type.
-@router.get("/models", responses=models_responses) +@router.get("/models", response_model=ModelsResponse, responses=models_responses)src/app/endpoints/query.py (1)
171-173
: Add response_model to document and enforce the success payload.This improves OpenAPI and client generation.
-@router.post("/query", responses=query_response) +@router.post("/query", response_model=QueryResponse, responses=query_response)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
src/app/endpoints/authorized.py
(2 hunks)src/app/endpoints/config.py
(2 hunks)src/app/endpoints/conversations.py
(4 hunks)src/app/endpoints/conversations_v2.py
(4 hunks)src/app/endpoints/feedback.py
(4 hunks)src/app/endpoints/health.py
(3 hunks)src/app/endpoints/info.py
(2 hunks)src/app/endpoints/metrics.py
(1 hunks)src/app/endpoints/models.py
(2 hunks)src/app/endpoints/query.py
(1 hunks)src/app/endpoints/root.py
(2 hunks)src/app/endpoints/streaming_query.py
(1 hunks)src/app/main.py
(2 hunks)src/authentication/__init__.py
(2 hunks)src/lightspeed_stack.py
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use absolute imports for internal modules (e.g., from auth import get_auth_dependency)
Files:
src/app/endpoints/query.py
src/app/endpoints/metrics.py
src/app/endpoints/streaming_query.py
src/app/endpoints/conversations_v2.py
src/lightspeed_stack.py
src/app/main.py
src/app/endpoints/authorized.py
src/app/endpoints/conversations.py
src/authentication/__init__.py
src/app/endpoints/health.py
src/app/endpoints/feedback.py
src/app/endpoints/models.py
src/app/endpoints/config.py
src/app/endpoints/info.py
src/app/endpoints/root.py
src/app/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use standard FastAPI imports (from fastapi import APIRouter, HTTPException, Request, status, Depends) in FastAPI app code
Files:
src/app/endpoints/query.py
src/app/endpoints/metrics.py
src/app/endpoints/streaming_query.py
src/app/endpoints/conversations_v2.py
src/app/main.py
src/app/endpoints/authorized.py
src/app/endpoints/conversations.py
src/app/endpoints/health.py
src/app/endpoints/feedback.py
src/app/endpoints/models.py
src/app/endpoints/config.py
src/app/endpoints/info.py
src/app/endpoints/root.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py
: All modules start with descriptive module-level docstrings explaining purpose
Use logger = logging.getLogger(name) for module logging after import logging
Define type aliases at module level for clarity
All functions require docstrings with brief descriptions
Provide complete type annotations for all function parameters and return types
Use typing_extensions.Self in model validators where appropriate
Use modern union syntax (str | int) and Optional[T] or T | None consistently
Function names use snake_case with descriptive, action-oriented prefixes (get_, validate_, check_)
Avoid in-place parameter modification; return new data structures instead of mutating arguments
Use appropriate logging levels: debug, info, warning, error with clear messages
All classes require descriptive docstrings explaining purpose
Class names use PascalCase with conventional suffixes (Configuration, Error/Exception, Resolver, Interface)
Abstract base classes should use abc.ABC and @AbstractMethod for interfaces
Provide complete type annotations for all class attributes
Follow Google Python docstring style for modules, classes, and functions, including Args, Returns, Raises, Attributes sections as needed
Files:
src/app/endpoints/query.py
src/app/endpoints/metrics.py
src/app/endpoints/streaming_query.py
src/app/endpoints/conversations_v2.py
src/lightspeed_stack.py
src/app/main.py
src/app/endpoints/authorized.py
src/app/endpoints/conversations.py
src/authentication/__init__.py
src/app/endpoints/health.py
src/app/endpoints/feedback.py
src/app/endpoints/models.py
src/app/endpoints/config.py
src/app/endpoints/info.py
src/app/endpoints/root.py
src/{app/**/*.py,client.py}
📄 CodeRabbit inference engine (CLAUDE.md)
Use async def for I/O-bound operations and external API calls
Files:
src/app/endpoints/query.py
src/app/endpoints/metrics.py
src/app/endpoints/streaming_query.py
src/app/endpoints/conversations_v2.py
src/app/main.py
src/app/endpoints/authorized.py
src/app/endpoints/conversations.py
src/app/endpoints/health.py
src/app/endpoints/feedback.py
src/app/endpoints/models.py
src/app/endpoints/config.py
src/app/endpoints/info.py
src/app/endpoints/root.py
src/app/endpoints/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
In API endpoints, raise FastAPI HTTPException with appropriate status codes for error handling
Files:
src/app/endpoints/query.py
src/app/endpoints/metrics.py
src/app/endpoints/streaming_query.py
src/app/endpoints/conversations_v2.py
src/app/endpoints/authorized.py
src/app/endpoints/conversations.py
src/app/endpoints/health.py
src/app/endpoints/feedback.py
src/app/endpoints/models.py
src/app/endpoints/config.py
src/app/endpoints/info.py
src/app/endpoints/root.py
**/__init__.py
📄 CodeRabbit inference engine (CLAUDE.md)
Package init.py files contain brief package descriptions
Files:
src/authentication/__init__.py
🧬 Code graph analysis (15)
src/app/endpoints/query.py (1)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
src/app/endpoints/metrics.py (4)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)src/authorization/middleware.py (1)
authorize
(111-122)src/metrics/utils.py (1)
setup_model_metrics
(20-57)src/models/config.py (2)
config
(138-144)Action
(311-350)
src/app/endpoints/streaming_query.py (1)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
src/app/endpoints/conversations_v2.py (4)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)src/authorization/middleware.py (1)
authorize
(111-122)src/models/config.py (2)
config
(138-144)Action
(311-350)src/models/responses.py (3)
ConversationDeleteResponse
(513-546)ConversationResponse
(463-510)ConversationsListResponseV2
(670-677)
src/lightspeed_stack.py (2)
src/configuration.py (1)
configuration
(65-69)src/runners/uvicorn.py (1)
start_uvicorn
(12-31)
src/app/main.py (6)
src/app/database.py (2)
create_tables
(29-31)initialize_database
(101-129)src/client.py (3)
AsyncLlamaStackClientHolder
(18-55)load
(23-47)get_client
(49-55)src/configuration.py (2)
configuration
(65-69)load_configuration
(52-58)src/log.py (1)
get_logger
(7-13)src/utils/common.py (1)
register_mcp_servers_async
(15-34)src/utils/llama_stack_version.py (1)
check_llama_stack_version
(22-39)
src/app/endpoints/authorized.py (1)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
src/app/endpoints/conversations.py (2)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)src/authorization/middleware.py (1)
authorize
(111-122)
src/authentication/__init__.py (1)
src/configuration.py (4)
configuration
(65-69)LogicError
(32-33)authentication_configuration
(100-105)load_configuration
(52-58)
src/app/endpoints/health.py (3)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)src/authorization/middleware.py (1)
authorize
(111-122)src/models/responses.py (2)
ProviderHealthStatus
(199-219)ReadinessResponse
(222-275)
src/app/endpoints/feedback.py (2)
src/models/responses.py (1)
ForbiddenResponse
(448-460)src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
src/app/endpoints/models.py (2)
src/authorization/middleware.py (1)
authorize
(111-122)src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
src/app/endpoints/config.py (1)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
src/app/endpoints/info.py (4)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)src/authorization/middleware.py (1)
authorize
(111-122)src/client.py (1)
AsyncLlamaStackClientHolder
(18-55)src/configuration.py (1)
configuration
(65-69)
src/app/endpoints/root.py (1)
src/authentication/__init__.py (1)
get_auth_dependency
(14-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-pr
- GitHub Check: e2e_tests
🔇 Additional comments (26)
src/lightspeed_stack.py (2)
77-79
: LGTM! Environment variable pattern enables worker-local config loading.The env var approach correctly addresses the process isolation issue with uvicorn workers > 1. Each worker can now independently load configuration via
get_auth_dependency()
.
60-60
: Configuration validation is consistent
Both the main process (at line 60) and each worker inget_auth_dependency
invoke the sameconfiguration.load_configuration
, so all schema and permission errors are validated identically in both contexts.src/app/endpoints/authorized.py (1)
40-40
: LGTM! Dependency injection pattern correctly migrated.The change from module-level
auth_dependency
to inlineDepends(get_auth_dependency())
enables per-worker authentication setup, fixing the multi-worker compatibility issue.src/app/endpoints/conversations_v2.py (1)
95-95
: LGTM! Consistent dependency injection pattern across all conversation endpoints.All three endpoints (
get_conversations_list
,get_conversation
,delete_conversation
) correctly migrated from module-level auth dependency to per-request resolution.Also applies to: 125-125, 161-161
src/app/endpoints/streaming_query.py (1)
572-572
: LGTM! Streaming endpoint correctly migrated to per-request auth dependency.The change ensures each worker process can independently authenticate streaming query requests, which is critical for the high-concurrency streaming endpoint.
src/app/endpoints/config.py (1)
64-64
: LGTM! Config endpoint correctly uses per-request auth dependency.The migration is consistent with other endpoints. Note that
get_auth_dependency()
may load configuration as a side effect (lines 19-24 in src/authentication/init.py), and this endpoint also returns configuration—this is acceptable since the lazy loading ensures config is available before this handler executes.src/app/endpoints/models.py (1)
54-55
: Auth DI change looks good.Per-request Depends(get_auth_dependency()) aligns with worker-per-process model and removes globals.
src/app/endpoints/query.py (1)
176-178
: Auth DI change looks good.Switch to Depends(get_auth_dependency()) is correct and consistent.
src/app/endpoints/root.py (1)
781-783
: Auth DI change looks good.Per-request dependency is consistent with the rest of the API.
src/app/endpoints/health.py (2)
81-83
: Auth DI change looks good.Readiness handler now uses per-request auth dependency; consistent with PR goals.
127-129
: Auth DI change looks good.Liveness handler updated consistently.
src/authentication/__init__.py (2)
4-4
: Import added for environment variable access.The
os
import is appropriately added to support readingLIGHTSPEED_STACK_CONFIG_PATH
from the environment.
7-9
: Imports reorganized for explicit dependency access.The imports now explicitly reference
AuthInterface
andLogicError
withconfiguration
as a module import, supporting the new lazy-loading pattern. This follows the coding guideline for absolute imports.src/app/endpoints/info.py (2)
6-6
: FastAPI imports follow standard order.The import statement correctly includes
Depends
alongside other FastAPI components as per coding guidelines.
40-40
: Dependency injection updated for per-request resolution.The endpoint now uses
Depends(get_auth_dependency())
instead of a precomputedauth_dependency
, aligning with the PR's goal to support multiple Uvicorn workers by eliminating shared global state. Each request will now obtain a fresh authentication dependency.src/app/endpoints/metrics.py (2)
5-5
: FastAPI dependencies consolidated.Import statement appropriately groups FastAPI components including
Depends
, following standard import conventions.
24-24
: Authentication dependency updated for multi-worker compatibility.The parameter now uses
Depends(get_auth_dependency())
for per-request dependency resolution, consistent with the PR's objective to eliminate global state for Uvicorn multi-worker deployments.src/app/endpoints/feedback.py (5)
3-3
: Import added for JSON serialization.The
json
import supportsjson.dump()
usage at line 156 instore_feedback()
. This import is necessary and correctly placed.
6-6
: Datetime imports consolidated.
UTC
anddatetime
are now imported together in a single statement, improving import hygiene.
22-22
: ForbiddenResponse imported for endpoint responses.The import is correctly added to support the response model at line 44 in the
feedback_response
dictionary.
90-90
: Feedback endpoint updated for per-request dependency resolution.The
auth
parameter now usesDepends(get_auth_dependency())
, aligning with the multi-worker compatibility goal by removing reliance on module-level state.
186-186
: Status update endpoint updated for per-request dependency resolution.Consistent with the other endpoint, the
auth
parameter usesDepends(get_auth_dependency())
to support worker-isolated authentication.src/app/endpoints/conversations.py (4)
6-6
: FastAPI imports standardized.
Depends
is now imported alongside other FastAPI components, following the coding guideline for standard FastAPI imports.
181-181
: List endpoint updated for multi-worker compatibility.The
get_conversations_list_endpoint_handler
now usesDepends(get_auth_dependency())
for per-request authentication, eliminating shared module-level state.
243-243
: Get endpoint updated for multi-worker compatibility.The
get_conversation_endpoint_handler
now usesDepends(get_auth_dependency())
for per-request authentication, consistent with the PR's objective.
371-371
: Delete endpoint updated for multi-worker compatibility.The
delete_conversation_endpoint_handler
now usesDepends(get_auth_dependency())
for per-request authentication, completing the migration away from module-level dependency instances.
logger.info("Initializing app") | ||
|
||
|
||
service_name = configuration.configuration.name |
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.
Critical: configuration accessed before it’s loaded.
service_name = configuration.configuration.name will raise LogicError at import time because configuration is loaded later in lifespan. Move name resolution into lifespan and update app metadata after loading.
-service_name = configuration.configuration.name
+# Defer service name resolution until configuration is loaded in lifespan
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/app/main.py around line 26, the code reads
configuration.configuration.name at import time which raises LogicError because
the configuration is loaded later in the app lifespan; move the service name
resolution into the app lifespan/startup routine where configuration is loaded,
remove the top-level access, and after loading configuration assign the name to
the app metadata (for example app.title or app.state.metadata['name']) so the
application metadata is updated only after configuration is available.
configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"]) | ||
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack) | ||
client = AsyncLlamaStackClientHolder().get_client() | ||
# check if the Llama Stack version is supported by the service |
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.
Handle missing LIGHTSPEED_STACK_CONFIG_PATH gracefully.
Direct indexing os.environ[...] will crash with KeyError. Validate and fail with a clear message (or provide a safe default if desired).
- configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"])
+ config_path = os.getenv("LIGHTSPEED_STACK_CONFIG_PATH")
+ if not config_path:
+ logger.error("LIGHTSPEED_STACK_CONFIG_PATH is not set; cannot load configuration")
+ raise RuntimeError("Required env var LIGHTSPEED_STACK_CONFIG_PATH is not set")
+ configuration.load_configuration(config_path)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"]) | |
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack) | |
client = AsyncLlamaStackClientHolder().get_client() | |
# check if the Llama Stack version is supported by the service | |
config_path = os.getenv("LIGHTSPEED_STACK_CONFIG_PATH") | |
if not config_path: | |
logger.error("LIGHTSPEED_STACK_CONFIG_PATH is not set; cannot load configuration") | |
raise RuntimeError("Required env var LIGHTSPEED_STACK_CONFIG_PATH is not set") | |
configuration.load_configuration(config_path) | |
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack) | |
client = AsyncLlamaStackClientHolder().get_client() | |
# check if the Llama Stack version is supported by the service |
try: | ||
module = configuration.authentication_configuration.module | ||
except LogicError: | ||
# Only load once if not already loaded | ||
config_path = os.getenv( | ||
"LIGHTSPEED_STACK_CONFIG_PATH", | ||
"tests/configuration/lightspeed-stack.yaml", | ||
) | ||
configuration.load_configuration(config_path) | ||
module = configuration.authentication_configuration.module |
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.
🧩 Analysis chain
Review the default configuration path and add concurrency protection.
Three concerns with the lazy-loading implementation:
-
Test path as production default: Line 24 defaults to
"tests/configuration/lightspeed-stack.yaml"
, which is a test fixture path. In a production multi-worker deployment, this path may not exist or contain inappropriate test configuration. Consider using a production-appropriate default or making the environment variable mandatory. -
Misleading comment: Line 21 states "Only load once if not already loaded", but the code does not prevent multiple concurrent workers or threads from simultaneously calling
configuration.load_configuration(config_path)
when they all encounterLogicError
. If multiple requests hit this code path concurrently before configuration loads, race conditions may occur. -
Repeated loading on exception: Each time
LogicError
is raised (configuration not loaded), the code will callload_configuration
. While this ensures eventual loading, the comment implies single execution which is not guaranteed.
Verify if concurrent initialization is protected elsewhere:
🏁 Script executed:
#!/bin/bash
# Check if configuration.load_configuration has thread-safety mechanisms
ast-grep --pattern $'def load_configuration($$$) -> $RET:
$$$'
Length of output: 539
🏁 Script executed:
#!/bin/bash
# show the lazy‐load block in authentication module
sed -n '1,50p' src/authentication/__init__.py
# search for explicit locks
rg -n 'threading\.Lock' -C2 .
Length of output: 2104
Enforce safe, production-ready configuration loading
- Replace the test-fixture default (
"tests/configuration/lightspeed-stack.yaml"
) with a production-appropriate path or makeLIGHTSPEED_STACK_CONFIG_PATH
mandatory. - Surround
configuration.load_configuration(config_path)
with a synchronization primitive (e.g. module-levelthreading.Lock
) to prevent concurrent races. - Update or remove the “Only load once if not already loaded” comment to reflect the actual single-load guarantee under concurrency. [src/authentication/init.py:18–27]
🤖 Prompt for AI Agents
In src/authentication/__init__.py around lines 18 to 27, the code uses a
test-fixture default config path and loads configuration without
synchronization; change this to require LIGHTSPEED_STACK_CONFIG_PATH to be set
(remove the "tests/..." default) or replace it with a production path, wrap the
call to configuration.load_configuration(config_path) in a module-level
threading.Lock to prevent concurrent races during lazy load, and update the
comment to reflect that the lock enforces a single-load guarantee under
concurrency.
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.
Nice one, LGTM
Description
When running Uvicorn with
workers > 1
, LCS did not work properly because each worker is a separate process, and global variables are not shared across processes.To fix this, the code was changed to initialize necessary resources within each worker process, rather than relying on global variables.
Note: To load the configuration across processes, an environment variable was added to track the location of the config file.
Type of change
Related Tickets & Documents
Checklist before requesting a review
Testing
Change the lightspeed_stack.yaml config to increase number of workers from 1 to any number >1. Then stress test an endpoint which uses llama-stack (/query, /streaming_query)
LCS was configured to have 2 / 4 workers and I tested /query and /streaming_query endpoints. A stress test would need to be performed.
Summary by CodeRabbit
Refactor
Bug Fixes
Chores