perf(public): 公开读路径进程内分域缓存——meta/goods/matrix 版本域 + TTL 兜底 + in-flight 合并

- PublicCacheService:分域版本号失效(bump 即作废,不等 TTL)、loader 期间
  bump 的竞态防护(返回但不回写)、并发 miss 单飞、PUBLIC_CACHE_DISABLED
  /TTL_MS/MAX_ENTRIES 应急开关;@Global 模块
- PublicService 六端点接缓存:列表缓存全量物化(分页切片在缓存外按请求执行,
  修复'所有页返回第一页'的切片缓存错误)、详情/首页/树/标签组/树序元数据/
  族最低价聚合各按依赖域缓存
- 全写路径挂钩 bump:admin CRUD(goods/categories/countries/tags/tag-groups/
  positions/origin-goods 标签)、sync 三同步、族重算、整理全家桶——
  事务提交成功后失效对应域,product-families 经 recompute 天然覆盖
- 测试:PublicCacheService 单测 13 例 + 失效链路集成 8 例(读命中/写后立即可见
  端到端/每条写路径域断言);既有两套件测数据语义改为显式禁缓存
This commit is contained in:
yeuimu
2026-09-03 03:40:20 +08:00
parent ca38476047
commit ab79325ab0
33 changed files with 1777 additions and 927 deletions
+2
View File
@@ -1,3 +1,4 @@
import { PublicCacheService } from '../public/public-cache.service';
import { Test } from '@nestjs/testing';
import {
BadRequestException,
@@ -30,6 +31,7 @@ describe('GoodsService', () => {
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
PublicCacheService,
GoodsService,
PrismaService,
{
+9
View File
@@ -14,6 +14,7 @@ import { BatchPriorityDto } from './dto/batch-priority.dto';
import { GoodDetailDto, GoodDto, PaginatedGoods } from './dto/good.dto';
import { SyncService } from '../sync/sync.service';
import { FamilyRecomputeService } from '../product-families/family-recompute.service';
import { PublicCacheService } from '../public/public-cache.service';
import { isAutoTagGroupName } from '../product-families/auto-tag-rules';
import { randomUUID } from 'crypto';
import {
@@ -120,6 +121,7 @@ export class GoodsService {
private readonly prisma: PrismaService,
private readonly syncService: SyncService,
private readonly familyRecompute: FamilyRecomputeService,
private readonly publicCache: PublicCacheService,
) {}
async findAll(query: QueryGoodDto): Promise<PaginatedGoods> {
@@ -240,6 +242,7 @@ export class GoodsService {
// 建商品后把链接有效标签镜像到该商品(纯聚合,不派生——派生在整理流程)
await this.familyRecompute.mirrorLinkTagsToGoods(BigInt(result.originGoodId));
}
this.publicCache.bump('goods');
if (
result.originGood?.source === 'SDS' &&
result.originGood.sdsGoodId &&
@@ -314,6 +317,7 @@ export class GoodsService {
}
return good.id;
});
this.publicCache.bump('goods');
if (family) this.familyRecompute.enqueue(family.id);
return this.findOne(goodId);
}
@@ -369,6 +373,7 @@ export class GoodsService {
await tx.good.update({ where: { id }, data: goodData });
}
});
this.publicCache.bump('goods');
return this.findOne(id);
}
@@ -495,6 +500,7 @@ export class GoodsService {
// 归族联动后重算矩阵(成员变化 → 自动重算,结构化聚合)
await this.familyRecompute.recomputeFamily(familyIdForTags);
}
this.publicCache.bump('goods');
if (
result.originGood?.source === 'SDS' &&
result.originGood.sdsGoodId &&
@@ -522,6 +528,7 @@ export class GoodsService {
}
}
});
this.publicCache.bump('goods');
return { id: id.toString() };
}
@@ -539,6 +546,7 @@ export class GoodsService {
}
return { count: dto.items.length };
});
this.publicCache.bump('goods');
return result;
}
@@ -634,6 +642,7 @@ export class GoodsService {
)) {
this.syncService.queueProductDetailSync(goodId);
}
this.publicCache.bump('goods');
return result;
}