Pārlūkot izejas kodu

Enable pc prod without redis (#85)

* Make app callable

* Enable pc prod without redis

* Add missing code
Nikhil Rao 2 gadi atpakaļ
vecāks
revīzija
d39bcc7d38
4 mainītis faili ar 37 papildinājumiem un 12 dzēšanām
  1. 8 0
      pynecone/app.py
  2. 1 3
      pynecone/constants.py
  3. 3 1
      pynecone/state.py
  4. 25 8
      pynecone/utils.py

+ 8 - 0
pynecone/app.py

@@ -74,6 +74,14 @@ class App(Base):
         """
         """
         return f"<App state={self.state.__name__}>"
         return f"<App state={self.state.__name__}>"
 
 
+    def __call__(self) -> fastapi.FastAPI:
+        """Run the backend api instance.
+
+        Returns:
+            The backend api.
+        """
+        return self.api
+
     def add_default_endpoints(self):
     def add_default_endpoints(self):
         """Add the default endpoints."""
         """Add the default endpoints."""
         # To test the server.
         # To test the server.

+ 1 - 3
pynecone/constants.py

@@ -56,12 +56,10 @@ BUN_PATH = "$HOME/.bun/bin/bun"
 INSTALL_BUN = "curl https://bun.sh/install | bash"
 INSTALL_BUN = "curl https://bun.sh/install | bash"
 # Command to run the backend in dev mode.
 # Command to run the backend in dev mode.
 RUN_BACKEND = "uvicorn --log-level critical --reload --host 0.0.0.0".split()
 RUN_BACKEND = "uvicorn --log-level critical --reload --host 0.0.0.0".split()
-# The number of workers to run in production mode by default.
-NUM_WORKERS = (os.cpu_count() or 1) * 2 + 1
 # The default timeout when launching the gunicorn server.
 # The default timeout when launching the gunicorn server.
 TIMEOUT = 120
 TIMEOUT = 120
 # The command to run the backend in production mode.
 # The command to run the backend in production mode.
-RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --bind 0.0.0.0:8000 --workers {NUM_WORKERS} --threads {NUM_WORKERS} --preload --timeout {TIMEOUT} --log-level debug".split()
+RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --bind 0.0.0.0:8000 --preload --timeout {TIMEOUT} --log-level debug".split()
 
 
 # Compiler variables.
 # Compiler variables.
 # The extension for compiled Javascript files.
 # The extension for compiled Javascript files.

+ 3 - 1
pynecone/state.py

@@ -8,6 +8,8 @@ import traceback
 from abc import ABC
 from abc import ABC
 from typing import Any, Callable, ClassVar, Dict, List, Optional, Sequence, Set, Type
 from typing import Any, Callable, ClassVar, Dict, List, Optional, Sequence, Set, Type
 
 
+from redis import Redis
+
 from pynecone import constants, utils
 from pynecone import constants, utils
 from pynecone.base import Base
 from pynecone.base import Base
 from pynecone.event import Event, EventHandler, window_alert
 from pynecone.event import Event, EventHandler, window_alert
@@ -476,7 +478,7 @@ class StateManager(Base):
     token_expiration: int = constants.TOKEN_EXPIRATION
     token_expiration: int = constants.TOKEN_EXPIRATION
 
 
     # The redis client to use.
     # The redis client to use.
-    redis: Any = None
+    redis: Optional[Redis] = None
 
 
     def setup(self, state: Type[State]):
     def setup(self, state: Type[State]):
         """Set up the state manager.
         """Set up the state manager.

+ 25 - 8
pynecone/utils.py

@@ -29,6 +29,7 @@ from typing import (
 
 
 import plotly.graph_objects as go
 import plotly.graph_objects as go
 from plotly.io import to_json
 from plotly.io import to_json
+from redis import Redis
 from rich.console import Console
 from rich.console import Console
 
 
 from pynecone import constants
 from pynecone import constants
@@ -385,6 +386,20 @@ def run_frontend_prod(app) -> subprocess.Popen:
     return subprocess.Popen(command, cwd=constants.WEB_DIR)
     return subprocess.Popen(command, cwd=constants.WEB_DIR)
 
 
 
 
+def get_num_workers() -> int:
+    """Get the number of backend worker processes.
+
+    Returns:
+        The number of backend worker processes.
+    """
+    if get_redis() is None:
+        # If there is no redis, then just use 1 worker.
+        return 1
+
+    # Use the number of cores * 2 + 1.
+    return (os.cpu_count() or 1) * 2 + 1
+
+
 def run_backend(app):
 def run_backend(app):
     """Run the backend.
     """Run the backend.
 
 
@@ -403,7 +418,14 @@ def run_backend_prod(app) -> None:
     Args:
     Args:
         app: The app.
         app: The app.
     """
     """
-    command = constants.RUN_BACKEND_PROD + [f"{app.__name__}:{constants.API_VAR}"]
+    num_workers = get_num_workers()
+    command = constants.RUN_BACKEND_PROD + [
+        "--workers",
+        str(num_workers),
+        "--threads",
+        str(num_workers),
+        f"{app.__name__}:{constants.APP_VAR}()",
+    ]
     subprocess.call(command)
     subprocess.call(command)
 
 
 
 
@@ -918,20 +940,15 @@ def get_hydrate_event(state) -> str:
     return get_event(state, constants.HYDRATE)
     return get_event(state, constants.HYDRATE)
 
 
 
 
-def get_redis():
+def get_redis() -> Optional[Redis]:
     """Get the redis client.
     """Get the redis client.
 
 
     Returns:
     Returns:
         The redis client.
         The redis client.
     """
     """
-    try:
-        import redis  # type: ignore
-    except:
-        return None
-
     config = get_config()
     config = get_config()
     if config.redis_url is None:
     if config.redis_url is None:
         return None
         return None
     redis_url, redis_port = config.redis_url.split(":")
     redis_url, redis_port = config.redis_url.split(":")
     print("Using redis at", config.redis_url)
     print("Using redis at", config.redis_url)
-    return redis.Redis(host=redis_url, port=int(redis_port), db=0)
+    return Redis(host=redis_url, port=int(redis_port), db=0)