Chiude le sessioni idle abbandonate prima che occupino slot e scalino Hetzner.
Il sweeper ora termina le idle dopo 30 minuti (cron ogni 10), così non restano appese a tenere accesi i warm-spare. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,12 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Chiude sessioni abbandonate che altrimenti occupano slot MediaMTX e
|
||||
# fanno scattare warm-spare / scale-out Hetzner.
|
||||
#
|
||||
# - idle: wizard aperti e mai partiti (default 30 min)
|
||||
# - connecting / reconnecting: hang senza go_live (default 6 h)
|
||||
class CleanupExpiredSessionsJob
|
||||
include Sidekiq::Job
|
||||
|
||||
def perform
|
||||
StreamSession.where(status: %w[connecting reconnecting])
|
||||
.where("updated_at < ?", 6.hours.ago)
|
||||
.find_each do |session|
|
||||
session.fail! if session.may_fail?
|
||||
Mediamtx::Client.for_session(session).delete_path(session)
|
||||
closed_idle = sweep_idle!
|
||||
closed_stale = sweep_stale_connect!
|
||||
Rails.logger.info(
|
||||
"[CleanupExpiredSessionsJob] idle_closed=#{closed_idle} stale_closed=#{closed_stale} " \
|
||||
"idle_minutes=#{self.class.idle_minutes} stale_hours=#{self.class.stale_hours}"
|
||||
)
|
||||
end
|
||||
|
||||
def self.idle_minutes
|
||||
ENV.fetch("STREAM_SESSION_IDLE_MINUTES", "30").to_i
|
||||
end
|
||||
|
||||
def self.stale_hours
|
||||
ENV.fetch("STREAM_SESSION_STALE_HOURS", "6").to_i
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sweep_idle!
|
||||
cutoff = self.class.idle_minutes.minutes.ago
|
||||
count = 0
|
||||
StreamSession.where(status: "idle").where("updated_at < ?", cutoff).find_each do |session|
|
||||
close!(session, reason: "idle_timeout")
|
||||
count += 1
|
||||
end
|
||||
count
|
||||
end
|
||||
|
||||
def sweep_stale_connect!
|
||||
cutoff = self.class.stale_hours.hours.ago
|
||||
count = 0
|
||||
StreamSession.where(status: %w[connecting reconnecting])
|
||||
.where("updated_at < ?", cutoff)
|
||||
.find_each do |session|
|
||||
close!(session, reason: "stale_#{session.status}")
|
||||
count += 1
|
||||
end
|
||||
count
|
||||
end
|
||||
|
||||
def close!(session, reason:)
|
||||
session.stream_events.create!(
|
||||
event_type: "error",
|
||||
metadata: { reason: reason },
|
||||
occurred_at: Time.current
|
||||
)
|
||||
Sessions::Stop.new(session).call
|
||||
rescue StandardError => e
|
||||
Rails.logger.warn(
|
||||
"[CleanupExpiredSessionsJob] session=#{session.id} reason=#{reason} " \
|
||||
"#{e.class}: #{e.message}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user