Add ordering config
This commit is contained in:
parent
ae1669cd8f
commit
7f682cea02
@ -3,6 +3,7 @@ import os
|
|||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
from typing import Awaitable
|
from typing import Awaitable
|
||||||
|
|
||||||
from multidict import CIMultiDict # type: ignore
|
from multidict import CIMultiDict # type: ignore
|
||||||
@ -19,6 +20,31 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Python 3.11 changed the way Enum formatting works for mixed-in types.
|
||||||
|
if sys.version_info < (3, 11, 0):
|
||||||
|
|
||||||
|
class StrEnum(str, Enum):
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
from enum import StrEnum # type: ignore # noqa
|
||||||
|
|
||||||
|
|
||||||
|
class UpperStrEnum(StrEnum):
|
||||||
|
def _generate_next_value_(name, start, count, last_values):
|
||||||
|
return name.upper()
|
||||||
|
|
||||||
|
def __eq__(self, value: object) -> bool:
|
||||||
|
value = str(value).upper()
|
||||||
|
return super().__eq__(value)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.value)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
def enable_windows_color_support():
|
def enable_windows_color_support():
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from pathlib import Path
|
|||||||
from typing import Any, Callable, Dict, Optional, Sequence, Union
|
from typing import Any, Callable, Dict, Optional, Sequence, Union
|
||||||
from warnings import filterwarnings
|
from warnings import filterwarnings
|
||||||
|
|
||||||
from sanic.constants import LocalCertCreator
|
from sanic.constants import LocalCertCreator, RestartOrder
|
||||||
from sanic.errorpages import DEFAULT_FORMAT, check_error_format
|
from sanic.errorpages import DEFAULT_FORMAT, check_error_format
|
||||||
from sanic.helpers import Default, _default
|
from sanic.helpers import Default, _default
|
||||||
from sanic.http import Http
|
from sanic.http import Http
|
||||||
@ -63,6 +63,7 @@ DEFAULT_CONFIG = {
|
|||||||
"REQUEST_MAX_SIZE": 100000000, # 100 megabytes
|
"REQUEST_MAX_SIZE": 100000000, # 100 megabytes
|
||||||
"REQUEST_TIMEOUT": 60, # 60 seconds
|
"REQUEST_TIMEOUT": 60, # 60 seconds
|
||||||
"RESPONSE_TIMEOUT": 60, # 60 seconds
|
"RESPONSE_TIMEOUT": 60, # 60 seconds
|
||||||
|
"RESTART_ORDER": RestartOrder.SHUTDOWN_FIRST,
|
||||||
"TLS_CERT_PASSWORD": "",
|
"TLS_CERT_PASSWORD": "",
|
||||||
"TOUCHUP": _default,
|
"TOUCHUP": _default,
|
||||||
"USE_UVLOOP": _default,
|
"USE_UVLOOP": _default,
|
||||||
@ -110,6 +111,7 @@ class Config(dict, metaclass=DescriptorMeta):
|
|||||||
REQUEST_MAX_SIZE: int
|
REQUEST_MAX_SIZE: int
|
||||||
REQUEST_TIMEOUT: int
|
REQUEST_TIMEOUT: int
|
||||||
RESPONSE_TIMEOUT: int
|
RESPONSE_TIMEOUT: int
|
||||||
|
RESTART_ORDER: Union[str, RestartOrder]
|
||||||
SERVER_NAME: str
|
SERVER_NAME: str
|
||||||
TLS_CERT_PASSWORD: str
|
TLS_CERT_PASSWORD: str
|
||||||
TOUCHUP: Union[Default, bool]
|
TOUCHUP: Union[Default, bool]
|
||||||
@ -194,6 +196,10 @@ class Config(dict, metaclass=DescriptorMeta):
|
|||||||
self.LOCAL_CERT_CREATOR = LocalCertCreator[
|
self.LOCAL_CERT_CREATOR = LocalCertCreator[
|
||||||
self.LOCAL_CERT_CREATOR.upper()
|
self.LOCAL_CERT_CREATOR.upper()
|
||||||
]
|
]
|
||||||
|
elif attr == "RESTART_ORDER" and not isinstance(
|
||||||
|
self.RESTART_ORDER, RestartOrder
|
||||||
|
):
|
||||||
|
self.RESTART_ORDER = RestartOrder[self.RESTART_ORDER.upper()]
|
||||||
elif attr == "DEPRECATION_FILTER":
|
elif attr == "DEPRECATION_FILTER":
|
||||||
self._configure_warnings()
|
self._configure_warnings()
|
||||||
|
|
||||||
|
@ -1,19 +1,9 @@
|
|||||||
from enum import Enum, auto
|
from enum import auto
|
||||||
|
|
||||||
|
from sanic.compat import UpperStrEnum
|
||||||
|
|
||||||
|
|
||||||
class HTTPMethod(str, Enum):
|
class HTTPMethod(UpperStrEnum):
|
||||||
def _generate_next_value_(name, start, count, last_values):
|
|
||||||
return name.upper()
|
|
||||||
|
|
||||||
def __eq__(self, value: object) -> bool:
|
|
||||||
value = str(value).upper()
|
|
||||||
return super().__eq__(value)
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return hash(self.value)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return self.value
|
|
||||||
|
|
||||||
GET = auto()
|
GET = auto()
|
||||||
POST = auto()
|
POST = auto()
|
||||||
@ -24,15 +14,19 @@ class HTTPMethod(str, Enum):
|
|||||||
DELETE = auto()
|
DELETE = auto()
|
||||||
|
|
||||||
|
|
||||||
class LocalCertCreator(str, Enum):
|
class LocalCertCreator(UpperStrEnum):
|
||||||
def _generate_next_value_(name, start, count, last_values):
|
|
||||||
return name.upper()
|
|
||||||
|
|
||||||
AUTO = auto()
|
AUTO = auto()
|
||||||
TRUSTME = auto()
|
TRUSTME = auto()
|
||||||
MKCERT = auto()
|
MKCERT = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class RestartOrder(UpperStrEnum):
|
||||||
|
|
||||||
|
SHUTDOWN_FIRST = auto()
|
||||||
|
STARTUP_FIRST = auto()
|
||||||
|
|
||||||
|
|
||||||
HTTP_METHODS = tuple(HTTPMethod.__members__.values())
|
HTTP_METHODS = tuple(HTTPMethod.__members__.values())
|
||||||
SAFE_HTTP_METHODS = (HTTPMethod.GET, HTTPMethod.HEAD, HTTPMethod.OPTIONS)
|
SAFE_HTTP_METHODS = (HTTPMethod.GET, HTTPMethod.HEAD, HTTPMethod.OPTIONS)
|
||||||
IDEMPOTENT_HTTP_METHODS = (
|
IDEMPOTENT_HTTP_METHODS = (
|
||||||
|
16
sanic/log.py
16
sanic/log.py
@ -1,22 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from enum import Enum
|
from typing import Any, Dict
|
||||||
from typing import TYPE_CHECKING, Any, Dict
|
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from sanic.compat import is_atty
|
from sanic.compat import StrEnum, is_atty
|
||||||
|
|
||||||
|
|
||||||
# Python 3.11 changed the way Enum formatting works for mixed-in types.
|
|
||||||
if sys.version_info < (3, 11, 0):
|
|
||||||
|
|
||||||
class StrEnum(str, Enum):
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not TYPE_CHECKING:
|
|
||||||
from enum import StrEnum
|
|
||||||
|
|
||||||
|
|
||||||
LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict( # no cov
|
LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict( # no cov
|
||||||
|
@ -814,6 +814,7 @@ class StartupMixin(metaclass=SanicMeta):
|
|||||||
cls._get_context(),
|
cls._get_context(),
|
||||||
(monitor_pub, monitor_sub),
|
(monitor_pub, monitor_sub),
|
||||||
worker_state,
|
worker_state,
|
||||||
|
primary.config.RESTART_ORDER,
|
||||||
)
|
)
|
||||||
if cls.should_auto_reload():
|
if cls.should_auto_reload():
|
||||||
reload_dirs: Set[Path] = primary.state.reload_dirs.union(
|
reload_dirs: Set[Path] = primary.state.reload_dirs.union(
|
||||||
|
@ -5,6 +5,7 @@ from signal import signal as signal_func
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from sanic.compat import OS_IS_WINDOWS
|
from sanic.compat import OS_IS_WINDOWS
|
||||||
|
from sanic.constants import RestartOrder
|
||||||
from sanic.exceptions import ServerKilled
|
from sanic.exceptions import ServerKilled
|
||||||
from sanic.log import error_logger, logger
|
from sanic.log import error_logger, logger
|
||||||
from sanic.worker.process import ProcessState, Worker, WorkerProcess
|
from sanic.worker.process import ProcessState, Worker, WorkerProcess
|
||||||
@ -28,6 +29,7 @@ class WorkerManager:
|
|||||||
context,
|
context,
|
||||||
monitor_pubsub,
|
monitor_pubsub,
|
||||||
worker_state,
|
worker_state,
|
||||||
|
restart_order: RestartOrder,
|
||||||
):
|
):
|
||||||
self.num_server = number
|
self.num_server = number
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -37,6 +39,7 @@ class WorkerManager:
|
|||||||
self.worker_state = worker_state
|
self.worker_state = worker_state
|
||||||
self.worker_state[self.MAIN_IDENT] = {"pid": self.pid}
|
self.worker_state[self.MAIN_IDENT] = {"pid": self.pid}
|
||||||
self.terminated = False
|
self.terminated = False
|
||||||
|
self.restart_order = restart_order
|
||||||
|
|
||||||
if number == 0:
|
if number == 0:
|
||||||
raise RuntimeError("Cannot serve with no workers")
|
raise RuntimeError("Cannot serve with no workers")
|
||||||
@ -55,7 +58,14 @@ class WorkerManager:
|
|||||||
def manage(self, ident, func, kwargs, transient=False):
|
def manage(self, ident, func, kwargs, transient=False):
|
||||||
container = self.transient if transient else self.durable
|
container = self.transient if transient else self.durable
|
||||||
container.append(
|
container.append(
|
||||||
Worker(ident, func, kwargs, self.context, self.worker_state)
|
Worker(
|
||||||
|
ident,
|
||||||
|
func,
|
||||||
|
kwargs,
|
||||||
|
self.context,
|
||||||
|
self.worker_state,
|
||||||
|
self.restart_order,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -7,6 +7,7 @@ from signal import SIGINT
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import Any, Dict, Set
|
from typing import Any, Dict, Set
|
||||||
|
|
||||||
|
from sanic.constants import RestartOrder
|
||||||
from sanic.log import Colors, logger
|
from sanic.log import Colors, logger
|
||||||
|
|
||||||
|
|
||||||
@ -28,13 +29,22 @@ class ProcessState(IntEnum):
|
|||||||
class WorkerProcess:
|
class WorkerProcess:
|
||||||
SERVER_LABEL = "Server"
|
SERVER_LABEL = "Server"
|
||||||
|
|
||||||
def __init__(self, factory, name, target, kwargs, worker_state):
|
def __init__(
|
||||||
|
self,
|
||||||
|
factory,
|
||||||
|
name,
|
||||||
|
target,
|
||||||
|
kwargs,
|
||||||
|
worker_state,
|
||||||
|
restart_order: RestartOrder,
|
||||||
|
):
|
||||||
self.state = ProcessState.IDLE
|
self.state = ProcessState.IDLE
|
||||||
self.factory = factory
|
self.factory = factory
|
||||||
self.name = name
|
self.name = name
|
||||||
self.target = target
|
self.target = target
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
self.worker_state = worker_state
|
self.worker_state = worker_state
|
||||||
|
self.restart_order = restart_order
|
||||||
if self.name not in self.worker_state:
|
if self.name not in self.worker_state:
|
||||||
self.worker_state[self.name] = {
|
self.worker_state[self.name] = {
|
||||||
"server": self.SERVER_LABEL in self.name
|
"server": self.SERVER_LABEL in self.name
|
||||||
@ -96,8 +106,11 @@ class WorkerProcess:
|
|||||||
self.name,
|
self.name,
|
||||||
self.pid,
|
self.pid,
|
||||||
)
|
)
|
||||||
self._old_process = self._current_process
|
|
||||||
self.set_state(ProcessState.RESTARTING, force=True)
|
self.set_state(ProcessState.RESTARTING, force=True)
|
||||||
|
if self.restart_order is RestartOrder.SHUTDOWN_FIRST:
|
||||||
|
self._current_process.terminate()
|
||||||
|
else:
|
||||||
|
self._old_process = self._current_process
|
||||||
self.kwargs.update(
|
self.kwargs.update(
|
||||||
{"config": {k.upper(): v for k, v in kwargs.items()}}
|
{"config": {k.upper(): v for k, v in kwargs.items()}}
|
||||||
)
|
)
|
||||||
@ -107,8 +120,9 @@ class WorkerProcess:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise RuntimeError("Restart failed")
|
raise RuntimeError("Restart failed")
|
||||||
|
|
||||||
termination_thread = Thread(target=self.wait_to_terminate)
|
if self.restart_order is RestartOrder.STARTUP_FIRST:
|
||||||
termination_thread.start()
|
termination_thread = Thread(target=self.wait_to_terminate)
|
||||||
|
termination_thread.start()
|
||||||
|
|
||||||
self.worker_state[self.name] = {
|
self.worker_state[self.name] = {
|
||||||
**self.worker_state[self.name],
|
**self.worker_state[self.name],
|
||||||
@ -118,7 +132,7 @@ class WorkerProcess:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def wait_to_terminate(self):
|
def wait_to_terminate(self):
|
||||||
# TODO: Add a timeout
|
# TODO: Add a timeout?
|
||||||
while self.state is not ProcessState.ACKED:
|
while self.state is not ProcessState.ACKED:
|
||||||
...
|
...
|
||||||
else:
|
else:
|
||||||
@ -163,6 +177,7 @@ class Worker:
|
|||||||
server_settings,
|
server_settings,
|
||||||
context: BaseContext,
|
context: BaseContext,
|
||||||
worker_state: Dict[str, Any],
|
worker_state: Dict[str, Any],
|
||||||
|
restart_order: RestartOrder,
|
||||||
):
|
):
|
||||||
self.ident = f"{self.WORKER_PREFIX}{ident}"
|
self.ident = f"{self.WORKER_PREFIX}{ident}"
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -170,6 +185,7 @@ class Worker:
|
|||||||
self.server_settings = server_settings
|
self.server_settings = server_settings
|
||||||
self.worker_state = worker_state
|
self.worker_state = worker_state
|
||||||
self.processes: Set[WorkerProcess] = set()
|
self.processes: Set[WorkerProcess] = set()
|
||||||
|
self.restart_order = restart_order
|
||||||
self.create_process()
|
self.create_process()
|
||||||
|
|
||||||
def create_process(self) -> WorkerProcess:
|
def create_process(self) -> WorkerProcess:
|
||||||
@ -179,6 +195,7 @@ class Worker:
|
|||||||
target=self.serve,
|
target=self.serve,
|
||||||
kwargs={**self.server_settings},
|
kwargs={**self.server_settings},
|
||||||
worker_state=self.worker_state,
|
worker_state=self.worker_state,
|
||||||
|
restart_order=self.restart_order,
|
||||||
)
|
)
|
||||||
self.processes.add(process)
|
self.processes.add(process)
|
||||||
return process
|
return process
|
||||||
|
Loading…
x
Reference in New Issue
Block a user