fix: add max_alloc to prevent overlong headers error and throttle startup failure notifications
- Add -max_alloc 100000000 to FFmpeg arguments for HTTP inputs to avoid 'overlong headers' error when CDN (e.g. Douyin) returns oversized HTTP response headers exceeding FFmpeg's default 4096-byte buffer. - Add exponential backoff for repeated startup failures (30s → 15min cap) to break the tight fail→retry→re-poll loop that floods notifications. - Throttle startup failure notifications to at most one per 30 minutes per room to prevent email/webhook storms during persistent failures. - Reset backoff counter when a session successfully opens its first segment.
This commit is contained in:
@@ -324,6 +324,7 @@ public sealed partial class FfmpegService
|
||||
if (runtime.SaveMode == RecordSaveMode.SingleFile)
|
||||
{
|
||||
runtime.HasOpenedFirstSegment = true;
|
||||
ResetStartupFailureBackoff(runtime.LiveRoomId);
|
||||
currentTask.AttachProcess(runtime.ProcessId, now);
|
||||
currentTask.MarkRunning(now);
|
||||
session.AttachProcess(runtime.ProcessId, now);
|
||||
@@ -361,6 +362,7 @@ public sealed partial class FfmpegService
|
||||
if (segmentIndex == runtime.CurrentSegmentIndex)
|
||||
{
|
||||
runtime.HasOpenedFirstSegment = true;
|
||||
ResetStartupFailureBackoff(runtime.LiveRoomId);
|
||||
var finalOutputPath = NormalizeAbsolutePath(
|
||||
currentTask.OutputFilePath ??
|
||||
ResolveSegmentOutputPath(session.OutputPathPattern ?? runtime.OutputPathPattern, session.SaveMode, segmentIndex));
|
||||
@@ -435,6 +437,7 @@ public sealed partial class FfmpegService
|
||||
runtime.CurrentOutputFilePath = normalizedOpenedPath;
|
||||
runtime.ResetCurrentRecorderSegmentPaths(normalizedOpenedPath);
|
||||
runtime.HasOpenedFirstSegment = true;
|
||||
ResetStartupFailureBackoff(runtime.LiveRoomId);
|
||||
|
||||
if (runtime.DanmakuRecorder is not null)
|
||||
{
|
||||
@@ -1213,24 +1216,38 @@ public sealed partial class FfmpegService
|
||||
|
||||
if (!runtime.StopRequested && session.LiveRoomId != Guid.Empty)
|
||||
{
|
||||
_liveRoomPollingSignal.RequestImmediatePoll(session.LiveRoomId, TimeSpan.FromSeconds(2));
|
||||
var isStartupFailure = !runtime.HasOpenedFirstSegment &&
|
||||
runtime.StartupFailureKind != StartupFailureKind.None;
|
||||
var pollDelay = isStartupFailure
|
||||
? RecordStartupFailure(session.LiveRoomId)
|
||||
: TimeSpan.FromSeconds(2);
|
||||
_liveRoomPollingSignal.RequestImmediatePoll(session.LiveRoomId, pollDelay);
|
||||
}
|
||||
|
||||
if (session.Status == RecordSessionStatus.Failed && session.LiveRoom is not null)
|
||||
{
|
||||
var failureDetail = BuildExitLogDetail(runtime, process.ExitCode, effectiveOutputPath, toleratedNonZeroExit: false, finalizationError);
|
||||
await emailNotificationService.SendExceptionAsync(
|
||||
"FFmpeg",
|
||||
"Recording session exited abnormally.",
|
||||
failureDetail,
|
||||
session.LiveRoom,
|
||||
currentTask);
|
||||
await webhookNotificationService.SendExceptionAsync(
|
||||
"FFmpeg",
|
||||
"Recording session exited abnormally.",
|
||||
failureDetail,
|
||||
session.LiveRoom,
|
||||
currentTask);
|
||||
var isStartupFailure = !runtime.HasOpenedFirstSegment &&
|
||||
runtime.StartupFailureKind != StartupFailureKind.None;
|
||||
if (isStartupFailure && ShouldThrottleStartupFailureNotification(session.LiveRoomId))
|
||||
{
|
||||
// Notification suppressed — the failure has already been logged above.
|
||||
}
|
||||
else
|
||||
{
|
||||
var failureDetail = BuildExitLogDetail(runtime, process.ExitCode, effectiveOutputPath, toleratedNonZeroExit: false, finalizationError);
|
||||
await emailNotificationService.SendExceptionAsync(
|
||||
"FFmpeg",
|
||||
"Recording session exited abnormally.",
|
||||
failureDetail,
|
||||
session.LiveRoom,
|
||||
currentTask);
|
||||
await webhookNotificationService.SendExceptionAsync(
|
||||
"FFmpeg",
|
||||
"Recording session exited abnormally.",
|
||||
failureDetail,
|
||||
session.LiveRoom,
|
||||
currentTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user