Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/production.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Supported managers are `systemd` and `supervisor`.
A new bench deploys one bench process plus admin and the two redis servers, because
`[lite_mode] enabled` is the default. Turn lite mode off and the set becomes web,
socketio, admin, workers, and redis - see [Lite Mode](configuration.md#lite-mode).
Supervisor also runs a `schedule` process; systemd's worker pool runs the scheduler itself.

Each workload unit sets `LimitNOFILE=65535`. A systemd user unit gets 1024
descriptors by default, which is too few for a lite-mode bench process and for
Expand Down
11 changes: 11 additions & 0 deletions pilot/managers/processes/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def prod_process_definitions(self) -> list[ProcessDefinition]:
self.socketio_definition(),
self.admin_definition(),
*worker_defs,
self.schedule_definition(),
]
defs.append(self.redis_definition("redis_cache", "redis_cache.conf"))
defs.append(self.redis_definition("redis_queue", "redis_queue.conf"))
Expand Down Expand Up @@ -218,6 +219,16 @@ def worker_definitions(self, queue: str, count: int) -> list[ProcessDefinition]:
for i in range(1, count + 1)
]

def schedule_definition(self) -> ProcessDefinition:
"""Plain `frappe worker` has no scheduler; lite and worker-pool run their own."""
return ProcessDefinition(
name="schedule",
argv=[str(self.python), "-m", "frappe.utils.bench_helper", "frappe", "schedule"],
log_file=self.bench.logs_path / "schedule.log",
env=self.python_env(),
working_dir=self.bench.sites_path,
)

def redis_definition(self, name: str, config_filename: str) -> ProcessDefinition:
from pilot.managers.redis import redis_server_binary

Expand Down
26 changes: 21 additions & 5 deletions tests/pilot/core/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,12 +699,12 @@ def test_bench_init_apps_comes_from_config(tmp_path: Path) -> None:
def test_process_definitions_returns_correct_count(tmp_path: Path) -> None:
bench = make_bench(tmp_path)
# workers: default=2, short=1, long=1 => 4 worker processes
# plus web, socketio, redis_cache, redis_queue = 4
# plus web, socketio, schedule, redis_cache, redis_queue = 5
# plus admin, watch (on by default in dev) = 2
# total = 10
# total = 11
process_manager = ProcessManager(bench)
definitions = process_manager._process_definitions()
assert len(definitions) == 10
assert len(definitions) == 11
assert "watch" in [pd.name for pd in definitions]
assert "admin-ui" not in [pd.name for pd in definitions]

Expand All @@ -713,15 +713,15 @@ def test_process_definitions_watch_admin_js_adds_vite_ui(tmp_path: Path) -> None
bench = make_bench(tmp_path)
definitions = ProcessManager(bench, watch_admin_js=True)._process_definitions()
assert "admin-ui" in [pd.name for pd in definitions]
assert len(definitions) == 11
assert len(definitions) == 12


def test_process_definitions_can_disable_app_watch(tmp_path: Path) -> None:
bench = make_bench(tmp_path)
bench.config.watch_apps_js = False
definitions = ProcessManager(bench)._process_definitions()
assert "watch" not in [pd.name for pd in definitions]
assert len(definitions) == 9
assert len(definitions) == 10


def test_run_processes_survives_noncritical_exit(tmp_path: Path) -> None:
Expand Down Expand Up @@ -750,6 +750,22 @@ def test_watch_definition_is_noncritical_frappe_watch(tmp_path: Path) -> None:
assert all(pd.critical for pd in definitions if pd.name != "watch")


def test_process_definitions_run_frappe_scheduler(tmp_path: Path) -> None:
bench = make_bench(tmp_path)
definitions = ProcessManager(bench)._process_definitions()
schedule = next(pd for pd in definitions if pd.name == "schedule")
assert "frappe schedule" in shlex.join(schedule.argv)
assert schedule.working_dir == bench.sites_path


def test_systemd_definitions_leave_scheduling_to_worker_pool(tmp_path: Path) -> None:
bench = make_bench(tmp_path)
bench.config.production.process_manager = "systemd"
names = [pd.name for pd in ProcessManager(bench)._prod_process_definitions()]
assert "worker_pool" in names
assert "schedule" not in names


def test_process_definitions_worker_names_are_numbered(tmp_path: Path) -> None:
bench = make_bench(tmp_path)
process_manager = ProcessManager(bench)
Expand Down
Loading