owpengram-server/internal/rpc/expiry_dispatcher.go
2026-07-24 14:50:17 +08:00

105 lines
2.9 KiB
Go

package rpc
import (
"context"
"time"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
)
// ExpiryDispatcher deletes messages whose per-message TTL snapshot has expired.
type ExpiryDispatcher struct {
router *Router
log *zap.Logger
batch int
interval time.Duration
maxIdle time.Duration
}
func NewExpiryDispatcher(router *Router, log *zap.Logger) *ExpiryDispatcher {
if log == nil {
log = zap.NewNop()
}
return &ExpiryDispatcher{
router: router,
log: log,
batch: defaultExpiryDispatchBatch,
interval: defaultExpiryDispatchTick,
maxIdle: defaultIdleDispatchMaxInterval,
}
}
func (d *ExpiryDispatcher) Run(ctx context.Context) {
if d == nil || d.router == nil {
return
}
runIdleBackoffLoop(ctx, d.interval, d.maxIdle, d.DispatchOnce)
}
func (d *ExpiryDispatcher) DispatchOnce(ctx context.Context) bool {
if d == nil || d.router == nil {
return false
}
now := int(d.router.clock.Now().Unix())
private := d.dispatchPrivate(ctx, now)
channels := d.dispatchChannels(ctx, now)
return private || channels
}
func (d *ExpiryDispatcher) dispatchPrivate(ctx context.Context, now int) bool {
if d.router.deps.Messages == nil {
return false
}
ttlSvc, ok := d.router.deps.Messages.(historyTTLMessagesService)
if !ok {
return false
}
requests, err := ttlSvc.ClaimExpiredPrivateMessages(ctx, now, d.batch)
if err != nil {
d.log.Warn("claim expired private messages", zap.Error(err))
return false
}
for _, req := range requests {
res, err := d.router.deps.Messages.DeleteMessages(ctx, req.OwnerUserID, req)
if err != nil {
d.log.Warn("delete expired private messages", zap.Int64("owner_user_id", req.OwnerUserID), zap.Ints("ids", req.IDs), zap.Error(err))
continue
}
if d.router.hasReliableUpdateDispatch() {
continue
}
for _, deleted := range res.Deleted {
if deleted.Event.Type != "" {
d.router.pushUserUpdates(ctx, deleted.UserID, tgUpdateForOutboxEvent(deleted.Event))
}
}
}
return len(requests) > 0
}
func (d *ExpiryDispatcher) dispatchChannels(ctx context.Context, now int) bool {
if d.router.deps.Channels == nil {
return false
}
ttlSvc, ok := d.router.deps.Channels.(channelHistoryTTLService)
if !ok {
return false
}
requests, err := ttlSvc.ClaimExpiredMessages(ctx, now, d.batch)
if err != nil {
d.log.Warn("claim expired channel messages", zap.Error(err))
return false
}
for _, req := range requests {
res, err := d.router.deps.Channels.DeleteMessages(ctx, req.UserID, req)
if err != nil {
d.log.Warn("delete expired channel messages", zap.Int64("channel_id", req.ChannelID), zap.Ints("ids", req.IDs), zap.Error(err))
continue
}
d.router.enqueueChannelFanout(ctx, channelFanoutMessageBox, req.UserID, req.ChannelID, res.Event.Pts, res.Recipients, func(_ context.Context, viewerUserID int64) *tg.Updates {
return d.router.channelDeleteMessagesUpdates(viewerUserID, res.Channel, res.Event)
})
}
return len(requests) > 0
}