Skip to content

Conversation

strickvl
Copy link
Contributor

@strickvl strickvl commented Oct 3, 2025

This PR adds a create_app() factory function to the pipeline deployment server, enabling isolated FastAPI app instances for serverless environments like Modal while maintaining full backward compatibility with existing deployments.

Changes

Core Implementation (src/zenml/deployers/server/app.py)

  • Added create_app() factory function: Creates fresh FastAPI instances with per-instance state isolation
  • Added DeploymentAppConfig Pydantic model: Type-safe configuration for app instances (deployment_id, auth_key, test_mode, CORS, etc.)
  • Added _build_auth_dependency() helper: Constructs per-instance auth closures with scoped auth keys
  • Updated _build_invoke_router(): Now accepts optional auth_dep parameter for per-app auth injection
  • Fixed get_pipeline_service(): Changed signature to require Request parameter (no default) to comply with FastAPI dependency requirements
  • Preserved legacy module-level components: app, lifespan, verify_token, and _service remain unchanged for backward compatibility

Test Coverage (tests/unit/deployers/server/test_app.py)

Added comprehensive TestCreateAppFactory test class with 5 tests verifying:

  1. Test mode isolation: create_app() with test_mode=True doesn't initialize service or touch global _service
  2. Normal mode initialization: Service is properly initialized and stored on app.state.service
  3. Per-app auth isolation: Two apps with different auth keys enforce independent authentication policies
  4. No global state pollution: create_app() never modifies module-level _service variable
  5. State-aware service resolution: get_pipeline_service() correctly prefers app.state.service over legacy global

Motivation

The existing module-level app uses global state (_service) which causes issues in serverless environments:

  • Cross-instance leaks: Multiple app instances in the same process share the global _service
  • Auth coupling: All instances use the same environment-based auth key

Design Highlights

Per-Instance State

# Each app gets its own service stored on app.state
app1 = create_app(config=DeploymentAppConfig(deployment_id="dep-1", auth_key="key1"))
app2 = create_app(config=DeploymentAppConfig(deployment_id="dep-2", auth_key="key2"))
# app1.state.service != app2.state.service (completely independent)

Per-Instance Auth

# Different apps can enforce different auth keys
app1 = create_app(config=DeploymentAppConfig(auth_key="alpha"))
app2 = create_app(config=DeploymentAppConfig(auth_key="beta"))
# Requests to app1 require "Bearer alpha", app2 requires "Bearer beta"

Backward Compatibility

# Existing code continues working unchanged
from zenml.deployers.server.app import app  # Still works
uvicorn.run("zenml.deployers.server.app:app", ...)  # Still works

Testing

All tests pass:

# New factory tests
pytest tests/unit/deployers/server/test_app.py::TestCreateAppFactory -v  # ✅ 5/5 passed

# All app unit tests
pytest tests/unit/deployers/server/test_app.py -v  # ✅ All passed

# Integration tests
pytest tests/integration/functional/deployers/server/test_app_endpoints.py -v  # ✅ All passed

Usage Example (Modal)

from zenml.deployers.server.app import create_app, DeploymentAppConfig
import modal

app = modal.App("zenml-pipeline-deployment")

@app.function()
@modal.asgi_app()
def fastapi():
    return create_app(
        config=DeploymentAppConfig(
            deployment_id=os.environ["ZENML_DEPLOYMENT_ID"],
            auth_key=os.environ.get("ZENML_DEPLOYMENT_AUTH_KEY"),
        )
    )

Breaking Changes

None. All existing imports, CLI usage, and tests continue working without modification.

Rollout Plan

  • ✅ Implementation complete with comprehensive tests
  • ✅ Backward compatibility verified
  • ✅ Integration tests passing
  • 📝 Documentation can be updated separately to recommend create_app() for new deployments

@github-actions github-actions bot added the internal To filter out internal PRs and issues label Oct 3, 2025
@strickvl strickvl changed the title Add create_app() factory for pipeline deployment server Add create_app() factory for pipeline deployment server Oct 3, 2025
@strickvl strickvl requested a review from safoinme October 3, 2025 12:12
@safoinme
Copy link
Contributor

safoinme commented Oct 5, 2025

This looks great!

@strickvl strickvl requested a review from stefannica October 6, 2025 04:21
@strickvl strickvl marked this pull request as ready for review October 6, 2025 04:21
@strickvl strickvl removed the request for review from stefannica October 6, 2025 06:17
@stefannica stefannica self-requested a review October 6, 2025 09:49
@strickvl strickvl marked this pull request as draft October 6, 2025 10:11
@stefannica
Copy link
Contributor

I implemented a full fledged app factory pattern here: #4064

@stefannica stefannica closed this Oct 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal To filter out internal PRs and issues vibecoded

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants