diff --git a/fruitshorts-module-playback/src/main/java/com/fruitshorts/playback/service/WatchProgressBuffer.java b/fruitshorts-module-playback/src/main/java/com/fruitshorts/playback/service/WatchProgressBuffer.java index 3cf622b..ad60849 100644 --- a/fruitshorts-module-playback/src/main/java/com/fruitshorts/playback/service/WatchProgressBuffer.java +++ b/fruitshorts-module-playback/src/main/java/com/fruitshorts/playback/service/WatchProgressBuffer.java @@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.redis.core.SessionCallback; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Component; import java.time.Duration; @@ -19,7 +20,7 @@ import java.util.Map; /** * 观看进度 Redis 缓冲(write-behind)。 * - *
高并发上报的落地形态:POST 只写 Redis(每次上报 1 次写),由 + *
高并发上报的落地形态:POST 只写 Redis(Lua 脚本一次往返完成写状态与入队),由
* {@link WatchProgressFlusher} 定时把缓冲按「账号或设备 + drama」合并后批量 UPSERT
* 进权威表 watch_progress。Redis 不可用时 put 返回 false,调用方回退同步写库。
*
@@ -45,6 +46,22 @@ public class WatchProgressBuffer {
private static final int MAX_DRAMAS_PER_IDENTITY = 30;
private static final Duration TTL = Duration.ofHours(12);
+ /**
+ * 上报热路径的单次往返脚本:写状态、续 TTL、入待落库队列、截断队列一次做完。
+ * 拆成多条命令时进程若死在「写状态」与「入队」之间,该剧进度只留在 HASH 里、
+ * 永远不会落库,12 小时后被 TTL 清掉。返回 1 表示该身份的剧数超限,需要补充裁剪。
+ */
+ private static final DefaultRedisScript