fix: 根据 review 收紧录制器停止与 E2E 清理
This commit is contained in:
1 parent
524f3329c7
commit
ad81d2b9cf
8 files changed
+78
-18
No files matched your search
@@ -3,7 +3,8 @@ package recorders
|
||||
import "github.com/bililive-go/bililive-go/src/pkg/events"
|
||||
|
||||
const (
|
||||
RecorderStart events.EventType = "RecorderStart"
|
||||
RecorderStop events.EventType = "RecorderStop"
|
||||
RecorderRestart events.EventType = "RecorderRestart"
|
||||
RecorderStart events.EventType = "RecorderStart"
|
||||
RecorderStop events.EventType = "RecorderStop"
|
||||
RecorderRestart events.EventType = "RecorderRestart"
|
||||
RecorderStopRequested events.EventType = "RecorderStopRequested"
|
||||
)
|
||||
@@ -133,6 +133,9 @@ func (m *manager) registryListener(ctx context.Context, ed events.Dispatcher) {
|
||||
})
|
||||
ed.AddEventListener(listeners.LiveEnd, removeEvtListener)
|
||||
ed.AddEventListener(listeners.ListenStop, removeEvtListener)
|
||||
// RecorderStopRequested 仅表示 recorder 自身遇到不可重试错误,需要被回收;
|
||||
// 它不具有 LiveEnd 的全局直播状态语义,避免错误结束仍在进行的直播会话。
|
||||
ed.AddEventListener(RecorderStopRequested, removeEvtListener)
|
||||
}
|
||||
|
||||
func (m *manager) Start(ctx context.Context) error {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/bililive-go/bililive-go/src/instance"
|
||||
"github.com/bililive-go/bililive-go/src/live"
|
||||
livemock "github.com/bililive-go/bililive-go/src/live/mock"
|
||||
"github.com/bililive-go/bililive-go/src/pkg/events"
|
||||
"github.com/bililive-go/bililive-go/src/pkg/livelogger"
|
||||
"github.com/bililive-go/bililive-go/src/types"
|
||||
)
|
||||
@@ -56,6 +57,27 @@ func TestManagerAddAndRemoveRecorder(t *testing.T) {
|
||||
assert.False(t, m.HasRecorder(context.Background(), "test"))
|
||||
}
|
||||
|
||||
// TestRecorderStopRequestedRemovesOnlyRecorder 验证 recorder 的内部停止请求会被
|
||||
// manager 消费并回收 recorder,无需伪造具有全局直播状态语义的 LiveEnd。
|
||||
func TestRecorderStopRequestedRemovesOnlyRecorder(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := context.Background()
|
||||
ed := events.NewDispatcher(ctx)
|
||||
l := livemock.NewMockLive(ctrl)
|
||||
l.EXPECT().GetLiveId().Return(types.LiveID("test")).AnyTimes()
|
||||
|
||||
r := NewMockRecorder(ctrl)
|
||||
r.EXPECT().Close()
|
||||
m := &manager{savers: map[types.LiveID]Recorder{"test": r}}
|
||||
m.registryListener(ctx, ed)
|
||||
|
||||
ed.DispatchEventSync(events.NewEvent(RecorderStopRequested, l))
|
||||
|
||||
assert.False(t, m.HasRecorder(ctx, "test"))
|
||||
}
|
||||
|
||||
// TestRestartRecorderRaceWithLiveEnd 验证 RestartRecorder 和 LiveEnd(RemoveRecorder)
|
||||
// 并发执行时不会产生僵尸录制器。
|
||||
//
|
||||
|
||||
@@ -382,11 +382,11 @@ func (r *recorder) tryRecord(ctx context.Context) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err = tmpl.Execute(buf, info); err != nil {
|
||||
// 文件名模板渲染失败通常是配置错误(如引用了不存在的字段),每次重试都会
|
||||
// 重复失败。派发一次 LiveEnd 让 manager 回收该 recorder,避免无意义的 5s
|
||||
// 重试风暴与日志刷屏,也不再被误计入活跃录制数。listener 的内部状态不受
|
||||
// 影响,房间真正下播再开播时仍会以修正后的配置重新创建 recorder。
|
||||
// 重复失败。请求 manager 仅回收该 recorder,避免无意义的 5s 重试风暴与
|
||||
// 日志刷屏,也不再被误计入活跃录制数;不要派发具有全局直播状态语义的
|
||||
// LiveEnd,否则会错误地提前结束仍在进行的 live session。
|
||||
r.getLogger().WithError(err).Error("failed to render filename, stopping recorder (check out_put_tmpl)")
|
||||
r.ed.DispatchEvent(events.NewEvent(listeners.LiveEnd, r.Live))
|
||||
r.ed.DispatchEvent(events.NewEvent(RecorderStopRequested, r.Live))
|
||||
return
|
||||
}
|
||||
// 使用层级配置的 OutPutPath
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
|
||||
"github.com/bililive-go/bililive-go/src/configs"
|
||||
"github.com/bililive-go/bililive-go/src/listeners"
|
||||
"github.com/bililive-go/bililive-go/src/live"
|
||||
livemock "github.com/bililive-go/bililive-go/src/live/mock"
|
||||
"github.com/bililive-go/bililive-go/src/pkg/events"
|
||||
@@ -37,9 +36,9 @@ func TestTryRecordStopsWithoutPanicWhenFilenameRenderFails(t *testing.T) {
|
||||
l.EXPECT().GetStreamInfos().Return([]*live.StreamUrlInfo{{Url: streamURL}}, nil)
|
||||
l.EXPECT().GetLogger().Return(logger)
|
||||
|
||||
// 渲染失败时 recorder 应派发一次 LiveEnd 交由 manager 回收,而不是无限重试。
|
||||
// 渲染失败时 recorder 应请求 manager 仅回收自身,而不是错误派发 LiveEnd。
|
||||
ed := eventsmock.NewMockDispatcher(ctrl)
|
||||
ed.EXPECT().DispatchEvent(events.NewEvent(listeners.LiveEnd, l))
|
||||
ed.EXPECT().DispatchEvent(events.NewEvent(RecorderStopRequested, l))
|
||||
|
||||
cache := gcache.New(1).LRU().Build()
|
||||
if err := cache.Set(l, &live.Info{Live: l}); err != nil {
|
||||
|
||||
@@ -16,7 +16,7 @@ log:
|
||||
rotate_days: 7
|
||||
feature:
|
||||
remove_symbol_other_character: false
|
||||
out_put_tmpl: '{{.RoomName}}-{{.HostName}}/{{ now | date "2006-01-02 15-04-05" }}'
|
||||
out_put_tmpl: '{{.RoomName}}-{{.HostName}}/{{ now | date "2006-01-02 15-04-05" }}.flv'
|
||||
video_split_strategies:
|
||||
on_room_name_changed: false
|
||||
max_duration: 0s
|
||||
|
||||
@@ -18,7 +18,7 @@ feature:
|
||||
# '{{ .Live.GetPlatformCNName }}/{{ .HostName | filenameFilter }}/[{{ now | date "2006-01-02 15-04-05"}}][{{ .HostName | filenameFilter }}][{{ .RoomName | filenameFilter }}].flv'
|
||||
# ./平台名称/主播名字/[时间戳][主播名字][房间名字].flv
|
||||
# https://github.com/bililive-go/bililive-go/wiki/More-Tips
|
||||
out_put_tmpl: '{{.RoomName}}-{{.HostName}}/{{ now | date "2006-01-02 15-04-05" }}'
|
||||
out_put_tmpl: '{{.RoomName}}-{{.HostName}}/{{ now | date "2006-01-02 15-04-05" }}.flv'
|
||||
video_split_strategies:
|
||||
on_room_name_changed: false
|
||||
max_duration: 0s
|
||||
|
||||
@@ -13,19 +13,55 @@ const DEV_STREAM_URL = `${DEV_STREAM_SERVER}/live/test.flv`;
|
||||
// bililive-go 后端地址
|
||||
const BGO_SERVER = 'http://127.0.0.1:8080';
|
||||
|
||||
interface LiveInfo {
|
||||
id: string;
|
||||
live_url?: string;
|
||||
}
|
||||
|
||||
const preExistingLiveIds = new Set<string>();
|
||||
let baselineCaptured = false;
|
||||
|
||||
// 本地运行时 Playwright 可能复用开发者已经启动的 bililive-go。先记录既有房间,
|
||||
// 避免测试清理误删开发者的持久化配置。
|
||||
test.beforeAll(async () => {
|
||||
const ctx = await apiRequest.newContext();
|
||||
try {
|
||||
const res = await ctx.get(`${BGO_SERVER}/api/lives`);
|
||||
if (!res.ok()) {
|
||||
return;
|
||||
}
|
||||
const lives: LiveInfo[] = await res.json();
|
||||
if (!Array.isArray(lives)) {
|
||||
return;
|
||||
}
|
||||
for (const live of lives) {
|
||||
if (live.id) {
|
||||
preExistingLiveIds.add(live.id);
|
||||
}
|
||||
}
|
||||
baselineCaptured = true;
|
||||
} finally {
|
||||
await ctx.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// 本文件会通过 UI 添加 dev 直播间;由于所有 spec 共用同一个后端进程(workers=1),
|
||||
// 若不清理,残留的监控房间会一直被判定为"在播",导致后续 spec(如 update-*)的
|
||||
// active_recordings 断言失败。这里在文件所有用例结束后统一删除所有直播间,保证隔离。
|
||||
// active_recordings 断言失败。这里只删除本文件新建的 dev 直播间,既保证测试隔离,
|
||||
// 也不会在复用本地服务时误删开发者已有的监控房间。
|
||||
test.afterAll(async () => {
|
||||
if (!baselineCaptured) {
|
||||
return;
|
||||
}
|
||||
const ctx = await apiRequest.newContext();
|
||||
try {
|
||||
const res = await ctx.get(`${BGO_SERVER}/api/lives`);
|
||||
if (res.ok()) {
|
||||
const lives = await res.json();
|
||||
const lives: LiveInfo[] = await res.json();
|
||||
if (Array.isArray(lives)) {
|
||||
for (const l of lives) {
|
||||
if (l?.id) {
|
||||
await ctx.delete(`${BGO_SERVER}/api/lives/${l.id}`);
|
||||
for (const live of lives) {
|
||||
if (live.id && live.live_url === DEV_STREAM_URL && !preExistingLiveIds.has(live.id)) {
|
||||
await ctx.delete(`${BGO_SERVER}/api/lives/${live.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,4 +284,3 @@ test.describe('紧急情况:停止所有录制', () => {
|
||||
expect(count).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user