Alza soft capacity fase A e aggiunge quiet hours CPX notturne.

Home a 4 e cloud a 6 con MAX_NODES=12 (~76 soft); di notte (02–07 Europe/Rome) niente scale-out/warm-spare e sweeper che chiude i CPX idle.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 09:48:50 +02:00
co-authored by Cursor
parent d52434fb2e
commit 7c7b2bf14c
16 changed files with 513 additions and 25 deletions
@@ -19,6 +19,11 @@ RSpec.describe Streams::Autoscaler do
redis.del(Streams::DnsProviders::Lab::REDIS_KEY)
StreamSession.where.not(stream_node_id: nil).update_all(stream_node_id: nil, status: "ended", ended_at: Time.current)
StreamNode.where.not(slug: Streams::NodeRegistry::HOME_SLUG).delete_all
ENV["STREAM_AUTOSCALE_QUIET_HOURS"] = "off"
end
after do
ENV.delete("STREAM_AUTOSCALE_QUIET_HOURS")
end
it "is a no-op when disabled" do
@@ -261,4 +266,34 @@ RSpec.describe Streams::Autoscaler do
expect(node.reload.status).to eq("ready")
end
end
it "blocks scale-out and warm spare during quiet hours" do
with_env(
"STREAM_AUTOSCALE_ENABLED" => "1",
"STREAM_AUTOSCALE_QUIET_HOURS" => "02:00-07:00",
"STREAM_AUTOSCALE_SOFT_FREE_SLOTS" => "2",
"STREAM_AUTOSCALE_WARM_SPARE" => "1",
"STREAM_AUTOSCALE_KIND" => "lab",
"STREAM_AUTOSCALE_MAX_NODES" => "5",
"STREAM_NODE_HOME_MAX_PUBLISHERS" => "1",
"MEDIAMTX_API_URL" => "http://mtx-home:9997",
"MEDIAMTX_RTMP_URL" => "rtmp://home.example:1935",
"HLS_PUBLIC_URL" => "https://home.example/hls"
) do
allow(Streams::QuietHours).to receive(:active?).and_return(true)
home = Streams::NodeRegistry.ensure_home_from_env!
user = User.create!(email: "qh@example.com", name: "Q", password: "Password123", role: "coach")
club = Club.create!(name: "QH", sport: "volleyball")
team = club.teams.create!(name: "T", sport: "volleyball", slug: "qh-t")
match = team.matches.create!(opponent_name: "X", scheduled_at: 1.hour.from_now)
StreamSession.create!(match: match, user: user, platform: "matchlivetv", status: "live", stream_node: home)
provisioner = instance_double(Streams::NodeProvisioner)
expect(provisioner).not_to receive(:provision_lab!)
result = described_class.reconcile!(provisioner: provisioner)
expect(result.actions).to include(:blocked_capacity)
expect(result.metrics[:quiet_hours]).to eq(true)
end
end
end