fix(playback): 删除观看历史时同步清理进度缓冲
缓冲已是读接口的权威来源,只删库行会被读覆盖层读回、并被下一次落库把行复活。 缓冲补 remove/clear(HDEL/DEL),删除路径先清缓冲再删库行,读覆盖层按库行 deleted 过滤;落库时缓冲状态已失效的条目直接丢弃,不再整批退回队列。
This commit is contained in:
1 parent
657190d5a8
commit
44b7abcd0f
2 files changed
+54
-8
No files matched your search
+28
@@ -93,6 +93,34 @@ public class WatchProgressBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除某部剧的缓冲状态,用于用户删除单条观看记录。
|
||||||
|
*
|
||||||
|
* <p>缓冲是读接口的权威来源,删库行必须同步删缓冲,否则会被读覆盖层读回;
|
||||||
|
* 同时缓冲状态消失后,dirty 队列里该条引用在落库时会被丢弃(见
|
||||||
|
* {@code WatchProgressService#flushRefs}),不会再被写回库。
|
||||||
|
*/
|
||||||
|
public boolean remove(InteractionIdentity identity, long dramaId) {
|
||||||
|
try {
|
||||||
|
redis.opsForHash().delete(BUF_KEY_PREFIX + identityKey(identity), Long.toString(dramaId));
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("观看进度缓冲删除失败: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清空某身份的全部缓冲状态,用于用户清空观看历史。 */
|
||||||
|
public boolean clear(InteractionIdentity identity) {
|
||||||
|
try {
|
||||||
|
redis.delete(BUF_KEY_PREFIX + identityKey(identity));
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("观看进度缓冲清空失败: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 读单个剧的缓冲状态;无缓冲或解码失败返回 null。 */
|
/** 读单个剧的缓冲状态;无缓冲或解码失败返回 null。 */
|
||||||
public BufferedProgress get(InteractionIdentity identity, long dramaId) {
|
public BufferedProgress get(InteractionIdentity identity, long dramaId) {
|
||||||
return get(identityKey(identity), dramaId);
|
return get(identityKey(identity), dramaId);
|
||||||
|
|||||||
+26
-8
@@ -279,14 +279,18 @@ public class WatchProgressService {
|
|||||||
List<Long> extraDramaIds = extra.stream()
|
List<Long> extraDramaIds = extra.stream()
|
||||||
.map(WatchProgressBuffer.BufferedProgress::dramaId)
|
.map(WatchProgressBuffer.BufferedProgress::dramaId)
|
||||||
.toList();
|
.toList();
|
||||||
Set<Long> withRow = progressMapper.selectAnyForIdentityIn(
|
Map<Long, WatchProgress> rowByDrama = progressMapper.selectAnyForIdentityIn(
|
||||||
identity.userId(), identity.deviceId(), extraDramaIds)
|
identity.userId(), identity.deviceId(), extraDramaIds)
|
||||||
.stream()
|
.stream()
|
||||||
.map(WatchProgress::getDramaId)
|
.collect(Collectors.toMap(WatchProgress::getDramaId, row -> row, (a, b) -> a));
|
||||||
.collect(Collectors.toSet());
|
|
||||||
for (WatchProgressBuffer.BufferedProgress buffered : extra) {
|
for (WatchProgressBuffer.BufferedProgress buffered : extra) {
|
||||||
|
WatchProgress existing = rowByDrama.get(buffered.dramaId());
|
||||||
|
// 用户已删除该剧(库里是逻辑删除行):缓冲状态不能被读覆盖层再读回来
|
||||||
|
if (existing != null && existing.getDeleted() != null && existing.getDeleted() != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
merged.add(transientRow(buffered));
|
merged.add(transientRow(buffered));
|
||||||
if (!withRow.contains(buffered.dramaId())) {
|
if (existing == null) {
|
||||||
total++;
|
total++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,6 +310,12 @@ public class WatchProgressService {
|
|||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void remove(InteractionIdentity identity, Long dramaId) {
|
public void remove(InteractionIdentity identity, Long dramaId) {
|
||||||
|
// 缓冲是读接口的权威来源:先删缓冲再删库行,否则删掉的记录会被读覆盖层读回、
|
||||||
|
// 并被下一次落库把行复活。
|
||||||
|
WatchProgressBuffer buffer = bufferProvider.getIfAvailable();
|
||||||
|
if (buffer != null && dramaId != null) {
|
||||||
|
buffer.remove(identity, dramaId);
|
||||||
|
}
|
||||||
progressMapper.delete(identityQuery(identity)
|
progressMapper.delete(identityQuery(identity)
|
||||||
.eq(WatchProgress::getDramaId, dramaId));
|
.eq(WatchProgress::getDramaId, dramaId));
|
||||||
}
|
}
|
||||||
@@ -317,6 +327,10 @@ public class WatchProgressService {
|
|||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void clear(InteractionIdentity identity) {
|
public void clear(InteractionIdentity identity) {
|
||||||
|
WatchProgressBuffer buffer = bufferProvider.getIfAvailable();
|
||||||
|
if (buffer != null) {
|
||||||
|
buffer.clear(identity);
|
||||||
|
}
|
||||||
progressMapper.delete(identityQuery(identity));
|
progressMapper.delete(identityQuery(identity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,8 +352,7 @@ public class WatchProgressService {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
flushRefs(buffer, refs);
|
return flushRefs(buffer, refs);
|
||||||
return refs.size();
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.warn("观看进度缓冲合并失败,整批退回队列稍后重试: {}", ex.getMessage());
|
log.warn("观看进度缓冲合并失败,整批退回队列稍后重试: {}", ex.getMessage());
|
||||||
buffer.requeue(refs);
|
buffer.requeue(refs);
|
||||||
@@ -347,7 +360,7 @@ public class WatchProgressService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushRefs(WatchProgressBuffer buffer, List<WatchProgressBuffer.IdentityRef> refs) {
|
private int flushRefs(WatchProgressBuffer buffer, List<WatchProgressBuffer.IdentityRef> refs) {
|
||||||
// 1) pipeline 批量读缓冲状态,key -> (dramaId -> state)
|
// 1) pipeline 批量读缓冲状态,key -> (dramaId -> state)
|
||||||
List<String> identityKeys = refs.stream()
|
List<String> identityKeys = refs.stream()
|
||||||
.map(WatchProgressBuffer.IdentityRef::key)
|
.map(WatchProgressBuffer.IdentityRef::key)
|
||||||
@@ -372,7 +385,11 @@ public class WatchProgressService {
|
|||||||
ref.userId(), ref.deviceId(), ref.dramaId()));
|
ref.userId(), ref.deviceId(), ref.dramaId()));
|
||||||
}
|
}
|
||||||
if (pendingRefs.isEmpty()) {
|
if (pendingRefs.isEmpty()) {
|
||||||
throw new IllegalStateException("缓冲状态读取为空,ref=" + refs.size());
|
// 缓冲状态已消失(用户清空/删除历史,或 HASH 12h TTL 到期)。此时 ref 已无
|
||||||
|
// 可落库的内容,必须丢弃:退回队列会在下个周期被原样弹出、再清空、再退回,
|
||||||
|
// 这批僵尸条目将永久占住 dirty 队列配额。
|
||||||
|
log.debug("观看进度缓冲条目已无对应状态,丢弃 {} 条", refs.size());
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) 分块成对查询已有行(每块 100 组 OR 索引条件)
|
// 3) 分块成对查询已有行(每块 100 组 OR 索引条件)
|
||||||
@@ -415,6 +432,7 @@ public class WatchProgressService {
|
|||||||
}
|
}
|
||||||
session.commit();
|
session.commit();
|
||||||
}
|
}
|
||||||
|
return pendingRefs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 计算单条缓冲的合并动作:只改内存对象不触库,产出归入三组待执行列表。 */
|
/** 计算单条缓冲的合并动作:只改内存对象不触库,产出归入三组待执行列表。 */
|
||||||
|
|||||||
Reference in new issue
Block a user