daemon: sched/scheduler — governor <-> store <-> engine, against an EnginePort seam
The Scheduler that D4 was waiting on. Built against CORE's engine
HEADERS (now in main); the real EnginePort and the veloxd wiring wait
for lane/core's stage-8 bodies to reach main (deferrals.md D4a/D4b) —
core/src/task/ is still .gitkeep there, so linking vdm::Engine now
would be an unresolved symbol.
- sched/engine_port — the abstract seam: start/pause/resume/cancel/
provide_auth/decide/refresh_url + the ADR 0011 admission config
(set_task_order / set_max_active_segments / set_host_segment_cap).
Keeps the Scheduler testable without a live engine and the daemon
unbound from the concrete vdm::Engine.
- sched/fake_engine_port — a recording impl for tests.
- sched/scheduler:
* owns the wire-UUID <-> vdm::TaskId map.
* tick(): snapshot queues (schedule window evaluated with an
injectable clock) + non-terminal tasks -> governor.evaluate ->
apply. to_start builds a vdm::task::DownloadSpec from the row and
calls EnginePort::start; to_resume -> resume(); to_pause ->
pause() + writes the pause_reason; priority_order -> set_task_order
over the mapped engine ids. `new` tasks are parked (startMode
manual) and skipped.
* on_engine_state(wire_id, state, err): projects an engine
transition onto the store row (state, pause_reason='auto' when an
error rides a paused transition per ADR 0013 §2, flattened error
columns) so the next tick sees ground truth. This is also the hook
event.task.state will fire from (D5).
* reconcile_after_restart(): CORE-owned states -> queued, paused
keeps its reason (ADR 0013 §5).
* reload_config(): reads connection.maxConcurrentDownloads /
maxActiveSegments + a daemon-local host-cap map, pushes caps to
the engine, updates the governor.
* Deps: injectable local-now clock and a post_to_loop marshaller
(engine callbacks arrive on engine threads; default runs inline
for tests).
Test veloxd.sched_scheduler (ASan+UBSan and TSan clean): admission +
ordering, a slot freeing on completion, queue-stop -> pause
(queue_stopped) then queue-restart -> resume (not a fresh start),
engine auto-pause -> pause_reason 'auto' + never auto-resumed,
reconcile_after_restart, reload_config caps push. 35 daemon/cli tests
green.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
// The seam between the Scheduler and CORE's engine. Everything the Scheduler drives on the
|
||||
// engine goes through this interface, so the Scheduler is unit-testable without a live
|
||||
// engine and the daemon is not bound to the concrete `vdm::Engine`. The real
|
||||
// implementation (engine_port_core, added when velox::core's stage-8 bodies reach main)
|
||||
// wraps `vdm::Engine` + `vdm::segment::SegmentBudget`; FakeEnginePort records calls.
|
||||
//
|
||||
// Task ids here are `vdm::TaskId` — the engine assigns one from start() and the Scheduler
|
||||
// keeps the wire-UUID <-> TaskId map (ADR 0013). Admission is the Scheduler's: it calls
|
||||
// start() only for a task the governor admitted, and the engine begins probing at once
|
||||
// (it does not queue). The min-1 fairness rule in SegmentBudget then guarantees each
|
||||
// started task a slot; set_task_order pushes the priority.
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "vdm/ids.hpp"
|
||||
#include "vdm/task/download.hpp"
|
||||
|
||||
namespace velox::daemon::sched {
|
||||
|
||||
class EnginePort {
|
||||
public:
|
||||
virtual ~EnginePort() = default;
|
||||
|
||||
virtual vdm::TaskId start(const vdm::task::DownloadSpec& spec,
|
||||
vdm::task::DownloadCallbacks callbacks) = 0;
|
||||
|
||||
virtual void pause(vdm::TaskId) = 0;
|
||||
virtual void resume(vdm::TaskId) = 0;
|
||||
virtual void cancel(vdm::TaskId, bool discard_partial) = 0;
|
||||
virtual void provide_auth(vdm::TaskId, const std::string& username,
|
||||
const std::string& password, bool remember) = 0;
|
||||
virtual void decide(vdm::TaskId, vdm::task::Decision) = 0;
|
||||
virtual void refresh_url(vdm::TaskId, const std::string& url) = 0;
|
||||
|
||||
// ADR 0011 admission surface. Values are DAEMON's; enforcement is the engine's.
|
||||
virtual void set_task_order(const std::vector<vdm::TaskId>& order) = 0;
|
||||
virtual void set_max_active_segments(std::uint32_t n) = 0;
|
||||
virtual void set_host_segment_cap(const std::string& host, std::uint32_t cap) = 0;
|
||||
};
|
||||
|
||||
} // namespace velox::daemon::sched
|
||||
Reference in New Issue
Block a user