fix: sync web page preview pending deadline

This commit is contained in:
A 2026-07-22 21:42:52 +08:00
parent 76e7efd6cc
commit 10de462191
4 changed files with 53 additions and 4 deletions

View file

@ -380,7 +380,7 @@ func (r *Router) webPagePreviewMedia(ctx context.Context, message string, entiti
// resolveWebPageForRequest 为交互式读 RPC 解析链接预览先查缓存LookupWebPage命中即返回
// 不抓取不阻塞未命中才同步抓取但用受限短预算webpageRequestResolveBudget而非异步解析
// 的 20s避免慢/挂上游把 RPC worker 钉死。命中(含负缓存的 empty返回 ok=true调用方据 state
// 的 30s避免慢/挂上游把 RPC worker 钉死。命中(含负缓存的 empty返回 ok=true调用方据 state
// 决定;抓取失败返回 false。未启用返回 false。
func (r *Router) resolveWebPageForRequest(ctx context.Context, url string) (domain.MessageWebPage, bool) {
if page, ok := r.resolveAIComposeStyleWebPage(ctx, url); ok {

View file

@ -3,6 +3,7 @@ package rpc
import (
"context"
"testing"
"time"
"github.com/iamxvbaba/td/tg"
@ -27,6 +28,8 @@ func TestSendMessageAttachesWebPagePending(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
r.deps.Files.(*fakeFiles).webPagePreviewOn = true
now := time.Date(2030, time.January, 2, 3, 4, 5, 0, time.UTC)
r.clock = fixedClock{now: now}
updates, err := r.onMessagesSendMessage(WithUserID(ctx, owner.ID), &tg.MessagesSendMessageRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
@ -55,11 +58,52 @@ func TestSendMessageAttachesWebPagePending(t *testing.T) {
if url, _ := pending.GetURL(); url != wpTestURL {
t.Errorf("pending url = %q, want %q", url, wpTestURL)
}
wantDeadline := int(now.Add(webPagePendingLifetime).Unix())
if pending.Date != wantDeadline {
t.Errorf("pending date = %d, want retry deadline %d", pending.Date, wantDeadline)
}
if time.Unix(int64(pending.Date), 0).Sub(now) <= webPageResolveTimeout {
t.Errorf("pending deadline must outlive resolver timeout: deadline=%s timeout=%s", time.Unix(int64(pending.Date), 0), webPageResolveTimeout)
}
if !msg.InvertMedia {
t.Errorf("invert_media not projected onto message")
}
}
// TestSendChannelMessageUsesSameFutureWebPageDeadline 锁定频道发送也经过同一 pending
// 截止时间构造路径,避免只修私聊 echo 而频道仍被客户端立即判定过期。
func TestSendChannelMessageUsesSameFutureWebPageDeadline(t *testing.T) {
ctx := context.Background()
r, owner, channel := newRichChannelTestRouter(t)
r.deps.Files.(*fakeFiles).webPagePreviewOn = true
// 本测试只验证发送投影,不启动异步解析 goroutine。
r.webPageResolveSem = nil
now := time.Date(2030, time.February, 3, 4, 5, 6, 0, time.UTC)
r.clock = fixedClock{now: now}
updates, err := r.onMessagesSendMessage(WithUserID(ctx, owner.ID), &tg.MessagesSendMessageRequest{
Peer: &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
Message: wpTestMessage,
Entities: wpURLEntities(),
RandomID: 5106,
})
if err != nil {
t.Fatalf("send channel message: %v", err)
}
msg := newMessageFromUpdates(t, updates)
wrap, ok := msg.Media.(*tg.MessageMediaWebPage)
if !ok {
t.Fatalf("channel media = %T, want *tg.MessageMediaWebPage", msg.Media)
}
pending, ok := wrap.Webpage.(*tg.WebPagePending)
if !ok {
t.Fatalf("channel webpage = %T, want *tg.WebPagePending", wrap.Webpage)
}
if want := int(now.Add(webPagePendingLifetime).Unix()); pending.Date != want {
t.Errorf("channel pending date = %d, want retry deadline %d", pending.Date, want)
}
}
// TestSendMessageAttachesCachedDoneCard 验证URL 已缓存解析时,发送 echo 直接带 done 卡片
// (非 pending——官方行为TDesktop 据此立即渲染、不依赖异步换卡。
func TestSendMessageAttachesCachedDoneCard(t *testing.T) {

View file

@ -18,6 +18,10 @@ import (
const (
webPageResolveConcurrency = 16
webPageResolveTimeout = 30 * time.Second
// webPagePendingLifetime 是客户端在重新拉取 pending 消息前等待的窗口。
// TDesktop 与 DrKLO 都把 webPagePending.date 解释为绝对截止时间,而不是处理开始时间;
// 该窗口必须严格大于 resolver 的最大执行时间,避免正常的慢解析被客户端提前标记为失败。
webPagePendingLifetime = 2 * time.Minute
)
type webPageResolveJob struct {

View file

@ -135,9 +135,10 @@ func (r *Router) webPagePendingOrCachedMedia(ctx context.Context, rawURL string,
State: domain.MessageWebPageStatePending,
ID: domain.WebPageURLHash(normalized),
URL: normalized,
// Date=「processing started」时刻留 0=1970会被严格客户端判为 pending 早已
// 过期 → 直接显示纯文本,须填发送时刻。
Date: int(r.clock.Now().Unix()),
// date 是客户端重新拉取 pending 消息的绝对截止时间。TDesktop/DrKLO 在
// date<=now 时立即重取;若 resolver 此时仍运行TDesktop 会把占位记成 sticky
// failed后到的 done update 也不会重新显示卡片。
Date: int(r.clock.Now().Add(webPagePendingLifetime).Unix()),
ForceLargeMedia: forceLarge,
ForceSmallMedia: forceSmall,
},