task-15: async batched last_used_at touch refresh
This commit is contained in:
@@ -179,6 +179,13 @@ public class SimilarAsinProperties {
|
||||
*/
|
||||
private long chunkMergePayloadMaxBytes = 16L * 1024L * 1024L;
|
||||
|
||||
/**
|
||||
* Task 15:图片缓存 last_used_at 异步批量刷新的缓冲阈值。
|
||||
* lookup 命中先入内存缓冲(按 url_hash 去重),达到该阈值时立即批量 touch;
|
||||
* 其余由定时 flush 兜底,把逐图 UPDATE 合并为批量 UPDATE。
|
||||
*/
|
||||
private int imageCacheTouchFlushThreshold = 1000;
|
||||
|
||||
/**
|
||||
* P0-4:抢 Coze 提交锁失败后下次重试间隔(毫秒)。
|
||||
* 原硬编码 500ms,会在指数退避算法中作为基础值(500/1000/2000/4000ms 上限 4000)。
|
||||
|
||||
+50
-2
@@ -6,6 +6,7 @@ import com.nanri.aiimage.modules.similarasin.util.SimilarAsinImageEmbedder;
|
||||
import com.nanri.aiimage.modules.similarasin.util.SimilarAsinImageEmbedder.ResizedImage;
|
||||
import com.nanri.aiimage.modules.task.mapper.TaskImageCacheMapper;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskImageCacheEntity;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -26,6 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -71,12 +73,57 @@ public class SimilarAsinImagePrefetchService {
|
||||
private final ExecutorService prefetchPool = Executors.newFixedThreadPool(PREFETCH_POOL_SIZE,
|
||||
namedFactory("similar-asin-prefetch"));
|
||||
|
||||
/**
|
||||
* Task 15:last_used_at 异步批量刷新的内存缓冲(按 url_hash 去重)。
|
||||
* lookup 命中不再同步 touchLastUsed,而是先入缓冲;达到阈值立即批量刷新,
|
||||
* 其余由定时任务兜底,把逐图 UPDATE 合并为批量 UPDATE。
|
||||
*/
|
||||
private final Set<String> pendingTouches = java.util.concurrent.ConcurrentHashMap.newKeySet();
|
||||
|
||||
private final ScheduledExecutorService touchFlushScheduler =
|
||||
Executors.newSingleThreadScheduledExecutor(namedFactory("similar-asin-touch-flush"));
|
||||
|
||||
/** Task 15:定时兜底刷新周期(秒)。 */
|
||||
private static final long TOUCH_FLUSH_INTERVAL_SECONDS = 30L;
|
||||
|
||||
@PostConstruct
|
||||
public void startTouchFlushScheduler() {
|
||||
touchFlushScheduler.scheduleWithFixedDelay(this::flushPendingTouches,
|
||||
TOUCH_FLUSH_INTERVAL_SECONDS, TOUCH_FLUSH_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
prefetchPool.shutdownNow();
|
||||
touchFlushScheduler.shutdownNow();
|
||||
flushPendingTouches();
|
||||
inflight.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task 15:把缓冲中的 last_used_at 批量刷新到 DB。
|
||||
* 按单批上限分片;失败抛出(由定时任务/调用方决定吞掉或重试),
|
||||
* 成功后缓冲清空,不残留。空缓冲直接返回。
|
||||
*/
|
||||
public void flushPendingTouches() {
|
||||
if (pendingTouches.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> batch = new ArrayList<>(pendingTouches);
|
||||
for (int start = 0; start < batch.size(); start += CACHE_LOOKUP_BATCH_SIZE) {
|
||||
taskImageCacheMapper.touchLastUsedBatch(batch.subList(start,
|
||||
Math.min(start + CACHE_LOOKUP_BATCH_SIZE, batch.size())));
|
||||
}
|
||||
pendingTouches.removeAll(batch);
|
||||
}
|
||||
|
||||
private void bufferTouch(String urlHash) {
|
||||
pendingTouches.add(urlHash);
|
||||
if (pendingTouches.size() >= properties.getImageCacheTouchFlushThreshold()) {
|
||||
flushPendingTouches();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* P2-11:由 {@code mergeCozeRowsIntoChunk} 调用,把 cozeRows 中的图片 url 异步丢入预热队列。
|
||||
* 同 task 串行入队(用 inflight map 排队),避免多个 batch 同时打爆图片源站。
|
||||
@@ -265,7 +312,8 @@ public class SimilarAsinImagePrefetchService {
|
||||
}
|
||||
|
||||
/**
|
||||
* P2-11:DB cache 直读入口。命中时同步 touchLastUsed,便于 LRU 清理。
|
||||
* P2-11:DB cache 直读入口。命中时先入异步批量刷新缓冲(Task 15),
|
||||
* 由阈值/定时 flush 批量 touchLastUsed,便于 LRU 清理,减少逐图 UPDATE。
|
||||
* 失败/未命中返回 null,由调用方走回退路径。
|
||||
*/
|
||||
public byte[] lookup(String url) {
|
||||
@@ -286,7 +334,7 @@ public class SimilarAsinImagePrefetchService {
|
||||
}
|
||||
byte[] bytes = taskImageCacheMapper.selectBytesByUrlHash(urlHash);
|
||||
if (bytes != null && bytes.length > 0) {
|
||||
taskImageCacheMapper.touchLastUsed(urlHash);
|
||||
bufferTouch(urlHash);
|
||||
return bytes;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
||||
Reference in New Issue
Block a user