Major Changes: - Add StreamingService with OpenAI Compatible format - Upgrade Chat component V2 with Ant Design X integration - Implement AIA module with 12 intelligent agents - Update API routes to unified /api/v1 prefix - Update system documentation Backend (~1300 lines): - common/streaming: OpenAI Compatible adapter - modules/aia: 12 agents, conversation service, streaming integration - Update route versions (RVW, PKB to v1) Frontend (~3500 lines): - modules/aia: AgentHub + ChatWorkspace (100% prototype restoration) - shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook - Update API endpoints to v1 Documentation: - AIA module status guide - Universal capabilities catalog - System overview updates - All module documentation sync Tested: Stream response verified, authentication working Status: AIA V2.0 core completed (85%)
634 lines
22 KiB
Markdown
634 lines
22 KiB
Markdown
# **Postgres-Only <20>刻<EFBFBD><E588BB>嗆<EFBFBD>閫<EFBFBD><E996AB><EFBFBD>寞<EFBFBD>**
|
||
|
||
## **<2A>婙<EFBFBD>?<3F>W<EFBFBD>敺桀<E695BA> AI <20>a<EFBFBD><EFBD81><EFBFBD><EFBFBD><EFBFBD>舫<EFBFBD><E888AB><EFBFBD><EFBFBD><EFBFBD>鞉𧋦<E99E89><F0A78BA6><EFBFBD>舀<EFBFBD><E88880>?*
|
||
|
||
<EFBFBD><EFBFBD>𧋦嚗鯝1.0
|
||
<EFBFBD><EFBFBD>鍂<EFBFBD>箸艶嚗?-2鈭箏<E988AD><E7AE8F>𥕦𣪧<F0A595A6>麄<EFBFBD><E9BA84>ode.js/Fastify <20><><EFBFBD>舀<EFBFBD><E88880><EFBFBD>燵<EFBFBD>䔶<EFBFBD> SAE <20>函蔡<E587BD>臬<EFBFBD>
|
||
<EFBFBD>詨<EFBFBD><EFBFBD>格<EFBFBD>嚗𡁜銁銝滚<EFBFBD><EFBFBD>?Redis <20><><EFBFBD><EFBFBD>𣂷<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>唬<EFBFBD>銝𡁶漣<F0A181B6><E6BCA3>遙<EFBFBD>⊿<EFBFBD><E28ABF>𨰜<EFBFBD><F0A8B09C><EFBFBD>摮䀝<E691AE>隡朞<E99AA1>蝞∠<E89D9E>嚗䔶<E59A97><E494B6>?2撠𤩺𧒄+ <20>蹂遙<E8B982>∠<EFBFBD>蝏嘥笆<E598A5>舫<EFBFBD><E888AB>扼<EFBFBD>?
|
||
## **1\. <20>扯<EFBFBD><E689AF>䁅<EFBFBD> (Executive Summary)**
|
||
|
||
<EFBFBD><EFBFBD>笆<EFBFBD>烐䲮敶枏<EFBFBD>嚗㇈AU \< 5000嚗厩<E59A97>銝𡁜𦛚閫<F0A69B9A>芋銝𢛶<E98A9D>𦦵迅摰𡁏<E691B0>找<EFBFBD><E689BE><EFBFBD><EFBFBD>萘<EFBFBD><E89098>条裦霂㗇<E99C82>嚗峕𧋦<E5B395>寞<EFBFBD>銝餃<E98A9D><E9A483><EFBFBD>鍂 **"Postgres-Only" (<28>刻<EFBFBD><E588BB>唳旿摨?** <20>嗆<EFBFBD><E59786>?
|
||
<EFBFBD>朞<EFBFBD><EFBFBD>拍鍂 PostgreSQL <20><><EFBFBD>蝥抒鸌<E68A92>改<EFBFBD>憒?SKIP LOCKED <20><>㦤<EFBFBD>嗚<EFBFBD><E5979A>SONB 摮睃<E691AE><E79D83><EFBFBD>nlogged Tables嚗㚁<E59A97><E39A81>睲賑<E79DB2>臭誑摰<E8AA91><E691B0><EFBFBD>蹂誨 Redis <20>?*隞餃𦛚<E9A483>笔<EFBFBD>**<2A>?*蝻枏<E89DBB>**<2A>?*隡朞<E99AA1>摮睃<E691AE>**銝剔<E98A9D>雿𦦵鍂<F0A6A6B5>?
|
||
**<EFBFBD>条裦<EFBFBD>嗥<EFBFBD>嚗?*
|
||
|
||
1. **<EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD><EFBFBD>**嚗𡁶宏<F0A181B6>?Redis 銝剝𡢿隞塚<E99A9E>蝟餌<E89D9F>憭齿<E686AD>摨阡<E691A8>雿?50%<25>?
|
||
2. **<EFBFBD>唳旿撘箔<EFBFBD><EFBFBD>?*嚗帋<E59A97><E5B88B>⊥㺭<E28AA5>桐<EFBFBD>隞餃𦛚<E9A483>嗆<EFBFBD><E59786>銁<EFBFBD>䔶<EFBFBD>鈭见𦛚銝剜<E98A9D>鈭歹<E988AD>敶餃<E695B6><E9A483>寥膄<E5AFA5>𨅯<EFBFBD>撣<EFBFBD><E692A3>鈭见𦛚<E8A781>嗪<EFBFBD><E597AA>押<EFBFBD>?
|
||
3. **<EFBFBD>園<EFBFBD>憭𡝗<EFBFBD><EFBFBD>?*嚗𡁜<E59A97><F0A1819C>函緵<E587BD>?RDS 韏<><E99F8F>嚗峕<E59A97>撟渲<E6929F><E6B8B2><EFBFBD>㺭<EFBFBD><E3BAAD><EFBFBD>銝剝𡢿隞嗉晶<E59789>具<EFBFBD>?
|
||
4. **隡<><E99AA1>蝥批虾<E689B9>?*嚗帋<E59A97><E5B88B>?RDS <20><>䌊<EFBFBD>典<EFBFBD>隞賭<E99A9E> PITR嚗<52>𧒄<EFBFBD>渡<EFBFBD><E6B8A1>W<EFBFBD>嚗㕑<E59A97><E39591>𨥈<EFBFBD>靽嗪<E99DBD>隞餃𦛚<E9A483>笔<EFBFBD><E7AC94>唳旿<E594B3>𨀣偶銝滢腺憭晦<E686AD>腈<EFBFBD>?
|
||
## **2\. <20>桅<EFBFBD><E6A185>峕艶銝擧<E98A9D><E693A7>?*
|
||
|
||
### **2.1 敶枏<E695B6><E69E8F>𤤿<EFBFBD>嚗𡁻鵭隞餃𦛚<E9A483><F0A69B9A><EFBFBD>撘望<E69298>?*
|
||
|
||
<EFBFBD>睲賑<EFBFBD><EFBFBD><EFBFBD><EFBFBD>⊥<EFBFBD><EFBFBD>𪙛<EFBFBD>𨀣<EFBFBD><EFBFBD>桀<EFBFBD>摨栞圾<EFBFBD>鐥<EFBFBD>嘥<EFBFBD><EFBFBD>𨅯<EFBFBD>璅∪<EFBFBD>鈭文<EFBFBD>撉諹<EFBFBD><EFBFBD>嘅<EFBFBD><EFBFBD>閙活隞餃𦛚<EFBFBD>埈𧒄<EFBFBD>航<EFBFBD><EFBFBD>輯噢 **2撠𤩺𧒄**<EFBFBD>?
|
||
* **<2A>啁𠶖**嚗帋蝙<E5B88B>典<EFBFBD>摮㗛<E691AE><E3979B>梹<EFBFBD>MemoryQueue嚗剹<E59A97>?
|
||
* **憌𡡞埯**嚗𡁜銁 Serverless (SAE) <20>臬<EFBFBD>銝页<E98A9D>摰硺<E691B0><E7A1BA>航<EFBFBD><E888AA>䭾<EFBFBD>瘚<EFBFBD><E7989A>蝻拙捆<E68B99><E68D86><EFBFBD>撣<EFBFBD>凒<EFBFBD>唳<EFBFBD><E594B3><EFBFBD><EFBFBD>皞W枂<EFBCB7>屸<EFBFBD><E5B1B8>園<EFBFBD>瘥<EFBFBD><E798A5><EFBFBD><EFBFBD><EFBFBD>阡<EFBFBD>瘥<EFBFBD><E798A5><EFBFBD><EFBFBD><EFBFBD>銝剔<E98A9D>隞餃𦛚餈𥕦漲<F0A595A6>喳<EFBFBD>銝W仃嚗<E4BB83>紡<EFBFBD>渡鍂<E6B8A1>瑚遙<E7919A>∪仃韐乓<E99F90>?
|
||
### **2.2 撣貉<E692A3>霂臬躹嚗𡁜蘨<F0A1819C>?Redis <20>賣<EFBFBD><E8B3A3>踝<EFBFBD>**
|
||
|
||
銝𡁶<EFBFBD>撣貉<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>寡恕銝綽<EFBFBD><EFBFBD>𨅯<EFBFBD>憿餃<EFBFBD><EFBFBD>?Redis (BullMQ) <20>滩<EFBFBD>摰䂿緵隞餃𦛚<E9A483><F0A69B9A><EFBFBD><EFBFBD>硔<EFBFBD><E7A194><EFBFBD>?
|
||
* **<2A>漤底**嚗朞<E59A97><E69C9E>舀<EFBFBD><E88880>扳<EFBFBD>萘輕<E89098><E8BC95>遙<EFBFBD>⊥<EFBFBD>銋<EFBFBD><E98A8B><EFBFBD><EFBFBD>瓲敹<E793B2>糓\*\*<2A>𨀣<EFBFBD>銋<EFBFBD><E98A8B>摮睃<E691AE><E79D83>𩄼*\*嚗諹<E59A97>屸<EFBFBD> Redis <20>祈澈<E7A588><E6BE88>ostgreSQL <20>峕甅<E5B395>瑕<EFBFBD><E79195><EFBFBD><EFBFBD><EFBFBD>𤥁<EFBFBD><F0A4A581>𨥈<EFBFBD>銝𥪜銁鈭见𦛚摰匧<E691B0><E58CA7>找<EFBFBD>隡䀝<E99AA1> Redis<69>?
|
||
## **3\. <20>詨<EFBFBD>閫<EFBFBD><E996AB><EFBFBD>寞<EFBFBD>嚗䥪ostgres-Only <20>嗆<EFBFBD>**
|
||
|
||
<EFBFBD>祆䲮獢<EFBFBD><EFBFBD> Redis <20><><EFBFBD>憭扳瓲敹<E793B2><E695B9><EFBFBD>踝<EFBFBD><E8B89D>笔<EFBFBD><E7AC94><EFBFBD><EFBFBD>摮塩<E691AE><E5A1A9><EFBFBD>霂嘅<E99C82><E59885>券<EFBFBD><E588B8>嗆<EFBFBD><E59786>?PostgreSQL<51>?
|
||
### **3.1 <20>蹂誨 Redis <20>笔<EFBFBD>嚗帋蝙<E5B88B>?pg-boss**
|
||
|
||
<EFBFBD>睲賑撘訫<EFBFBD> Node.js 摨?**pg-boss**嚗<><E59A97><EFBFBD>拍鍂 PostgreSQL <20>?FOR UPDATE SKIP LOCKED <20>寞<EFBFBD>改<EFBFBD>摰䂿緵鈭<E7B7B5><E988AD><EFBFBD>扯<EFBFBD><E689AF><EFBFBD>𦜖<EFBFBD>惩<EFBFBD><E683A9>笔<EFBFBD><E7AC94>?
|
||
#### **<2A>嗆<EFBFBD><E59786>餉<EFBFBD>**
|
||
|
||
1. **<EFBFBD>仿<EFBFBD>**嚗鋫PI <20>交𤣰霂瑟<E99C82>嚗<EFBFBD><E59A97>隞餃𦛚<E9A483><F0A69B9A>㺭<EFBFBD>殷<EFBFBD>JSON嚗匧<E59A97><E58CA7>?job 銵具<E98AB5>?*甇斗<E79487>雿𨅯銁瘥怎<E798A5>蝥批<E89DA5><E689B9>琜<EFBFBD><E7909C>唳旿蝡见朖摰匧<E691B0><E58CA7>賜<EFBFBD><E8B39C>?*
|
||
2. **憭<><E686AD>**嚗阳orker 餈𤤿<E9A488>隞擧㺭<E693A7>桀<EFBFBD><E6A180>𧼮<EFBFBD>隞餃𦛚嚗<F0A69B9A>僎<EFBFBD><E5838E><EFBFBD>霂亥<E99C82>霈啣<E99C88><E595A3>?
|
||
3. **摰寧<E691B0>**嚗𡁜<E59A97><F0A1819C>?SAE 摰硺<E691B0><E7A1BA>典<EFBFBD><E585B8><EFBFBD><EFBFBD>蝔衤葉撏拇<E6928F>嚗<EFBFBD><E59A97> OOM嚗㚁<E59A97><E39A81>唳旿摨㯄<E691A8>隡𡁜銁頞<E98A81>𧒄<EFBFBD>舘䌊<E88898>券<EFBFBD><E588B8>橘<EFBFBD><E6A998>嗡<EFBFBD>摮䀹暑<E480B9>?Worker 摰硺<E691B0>隡𡁶<E99AA1><F0A181B6>單𦻖蝞∟砲隞餃𦛚<E9A483>滩<EFBFBD><E6BBA9>?
|
||
#### **隞<><E99A9E>摰䂿緵<E482BF><E7B7B5><EFBFBD>**
|
||
|
||
import PgBoss from 'pg-boss';
|
||
|
||
const boss \= new PgBoss({
|
||
connectionString: process.env.DATABASE\_URL,
|
||
schema: 'job\_queue', // <20>祉<EFBFBD>Schema嚗䔶<E59A97>瘙⊥<E79899>銝𡁜𦛚銵?
|
||
max: 5 // 撟嗅<E6929F><E59785>批<EFBFBD>嚗䔶<E59A97><E494B6>?DeepSeek API
|
||
});
|
||
|
||
await boss.start();
|
||
|
||
// 瘨<>晶<EFBFBD><E699B6><EFBFBD>銋?(Worker)
|
||
await boss.work('screening-task', {
|
||
// <20>喲睸<E596B2>滨蔭嚗朞挽蝵桅<E89DB5><E6A185><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>銝?4撠𤩺𧒄
|
||
// <20>喃蝙隞餃𦛚頝?3.9撠𤩺𧒄嚗<EFBFBD>蘨閬?Worker 瘣餌<E798A3>嚗<EFBFBD>停銝滢<E98A9D>鋡急𦜖韏?
|
||
// 憒<><E68692> Worker 甇颱<E79487>嚗屸<E59A97>餈<EFBFBD><E9A488>嚗䔶遙<E494B6>∟䌊<E2889F>券<EFBFBD>霂?
|
||
expireInSeconds: 14400,
|
||
retryLimit: 3
|
||
}, async (job) \=\> {
|
||
// 銝𡁜𦛚<F0A1819C>餉<EFBFBD>...
|
||
});
|
||
|
||
### **3.2 <20>蹂誨 Redis 蝻枏<E89DBB>嚗𡁜抅鈭?Table <20>?KV 摮睃<E691AE>**
|
||
|
||
撖嫣<EFBFBD> AI 蝏𤘪<E89D8F>蝻枏<E89DBB>嚗<EFBFBD><E59A97><EFBFBD>漤<EFBFBD>憭滩<E686AD><E6BBA9>?LLM嚗㚁<E59A97>Postgres <20><>䰻霂a<E99C82>笔漲嚗?-3ms嚗匧笆鈭𡒊鍂<F0A1928A>瑚<EFBFBD>撉䕘<E69289>蝘垍漣蝑匧<E89D91>嚗㗇䔉霂游<E99C82><E6B8B8>典虾隞交𦻖<E4BAA4>𨰜<EFBFBD>?
|
||
#### **<2A>扯<EFBFBD>霈箄<E99C88>**
|
||
|
||
```
|
||
摰鮋<EFBFBD>撟嗅<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?- 敶枏<E695B6>閫<EFBFBD>芋: 500 MAU
|
||
- 撜啣<E6929C>澆僎<E6BE86>? < 50 QPS嚗<53><E59A97>蝡舀<E89DA1><E88880>蛛<EFBFBD>
|
||
- Postgres<65>賢<EFBFBD>: 5銝? QPS嚗<53><E59A97><EFBFBD>閙䰻霂g<E99C82>
|
||
- <20>扯<EFBFBD>雿䠷<E99BBF>: 1000<30>?
|
||
<EFBFBD>滚<EFBFBD><EFBFBD>園𡢿撖寞<EFBFBD>嚗?- Redis: 0.15ms嚗<EFBFBD><EFBFBD>蝏?霂餃<E99C82>嚗?- Postgres: 1.5ms嚗<EFBFBD><EFBFBD>蝏?<3F>亥砭嚗?- 撌桀<E6928C>: 1.35ms
|
||
- <20>冽<EFBFBD><E586BD>毺䰻: <20>𩤃<EFBFBD><F0A9A483>餉<EFBFBD>埈𧒄200ms銝剖<E98A9D>瘥?< 1%嚗?
|
||
蝏栞捏嚗𡁜銁<EFBFBD>交暑10銝<EFBFBD>誑銝页<EFBFBD>Postgres<EFBFBD>扯<EFBFBD>摰<EFBFBD><EFBFBD>憭毺鍂
|
||
```
|
||
|
||
#### **<2A>唳旿摨栞挽霈?*
|
||
|
||
```prisma
|
||
model AppCache {
|
||
id Int @id @default(autoincrement())
|
||
key String @unique
|
||
value Json // 撖孵<E69296> Redis <20>?Value
|
||
expiresAt DateTime // 撖孵<E69296> Redis <20>?TTL
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([expiresAt]) // 蝝W<E89D9D><EFBCB7>其<EFBFBD>敹恍<E695B9><E6818D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㺭<EFBFBD>? @@index([key, expiresAt]) // 憭滚<E686AD>蝝W<E89D9D>隡睃<E99AA1><E79D83>亥砭
|
||
@@map("app_cache")
|
||
}
|
||
```
|
||
|
||
#### **撠<><E692A0> Service嚗<65><E59A97><EFBFBD>渡<EFBFBD>嚗?*
|
||
|
||
```typescript
|
||
// <20><>辣嚗颹ackend/src/common/cache/PostgresCacheAdapter.ts
|
||
|
||
import { prisma } from '../../lib/prisma';
|
||
import type { CacheAdapter } from './types';
|
||
import { logger } from '../logging/index';
|
||
|
||
export class PostgresCacheAdapter implements CacheAdapter {
|
||
|
||
/**
|
||
* <20>瑕<EFBFBD>蝻枏<E89DBB>嚗<EFBFBD>蒂<EFBFBD>埝<EFBFBD><E59F9D>𣳇膄嚗? */
|
||
async get<T = any>(key: string): Promise<T | null> {
|
||
try {
|
||
const record = await prisma.appCache.findUnique({
|
||
where: { key }
|
||
});
|
||
|
||
if (!record) return null;
|
||
|
||
// 璉<><E79289>交糓<E4BAA4>西<EFBFBD><E8A5BF>? if (record.expiresAt < new Date()) {
|
||
// <20>埝<EFBFBD><E59F9D>𣳇膄嚗𡁻◇<F0A181BB>𧢲<EFBFBD><F0A7A2B2><EFBFBD><EFBFBD>撘<EFBFBD>郊嚗䔶<E59A97><E494B6>餃<EFBFBD>嚗? this.deleteAsync(key);
|
||
return null;
|
||
}
|
||
|
||
return record.value as T;
|
||
|
||
} catch (error) {
|
||
logger.error('[PostgresCache] 霂餃<E99C82>憭梯揖', { key, error });
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 霈曄蔭蝻枏<E89DBB>
|
||
*/
|
||
async set(key: string, value: any, ttlSeconds: number = 3600): Promise<void> {
|
||
try {
|
||
const expiresAt = new Date(Date.now() + ttlSeconds * 1000);
|
||
|
||
await prisma.appCache.upsert({
|
||
where: { key },
|
||
create: { key, value, expiresAt },
|
||
update: { value, expiresAt }
|
||
});
|
||
|
||
logger.debug('[PostgresCache] <20>坔<EFBFBD><E59D94>𣂼<EFBFBD>', { key, ttl: ttlSeconds });
|
||
|
||
} catch (error) {
|
||
logger.error('[PostgresCache] <20>坔<EFBFBD>憭梯揖', { key, error });
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* <20>𣳇膄蝻枏<E89DBB>
|
||
*/
|
||
async delete(key: string): Promise<boolean> {
|
||
try {
|
||
await prisma.appCache.delete({ where: { key } });
|
||
return true;
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 撘<>郊<EFBFBD>𣳇膄嚗<E88684><E59A97><EFBFBD>餃<EFBFBD>銝餅<E98A9D>蝔页<E89D94>
|
||
*/
|
||
private deleteAsync(key: string): void {
|
||
prisma.appCache.delete({ where: { key } })
|
||
.catch(err => logger.debug('[PostgresCache] <20>埝<EFBFBD><E59F9D>𣳇膄憭梯揖', { key, err }));
|
||
}
|
||
|
||
/**
|
||
* <20>寥<EFBFBD><E5AFA5>𣳇膄
|
||
*/
|
||
async deleteMany(pattern: string): Promise<number> {
|
||
try {
|
||
const result = await prisma.appCache.deleteMany({
|
||
where: { key: { contains: pattern } }
|
||
});
|
||
return result.count;
|
||
} catch (error) {
|
||
logger.error('[PostgresCache] <20>寥<EFBFBD><E5AFA5>𣳇膄憭梯揖', { pattern, error });
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 皜<>征<EFBFBD><E5BE81><EFBFBD>厩<EFBFBD>摮? */
|
||
async flush(): Promise<void> {
|
||
try {
|
||
await prisma.appCache.deleteMany({});
|
||
logger.info('[PostgresCache] 蝻枏<E89DBB>撌脫<E6928C>蝛?);
|
||
} catch (error) {
|
||
logger.error('[PostgresCache] 皜<EFBFBD>征憭梯揖', { error });
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* <20>臬𢆡摰𡁏𧒄皜<F0A79284><E79A9C>隞餃𦛚嚗<F0A69B9A><E59A97><EFBFBD>寞<EFBFBD><E5AF9E><EFBFBD><EFBFBD><EFBFBD>脫迫<E884AB>餃<EFBFBD>嚗? */
|
||
export function startCacheCleanupTask() {
|
||
setInterval(async () => {
|
||
try {
|
||
// 瘥𤩺活<F0A4A9BA>芸<EFBFBD><E88AB8>?000<30>∟<EFBFBD><E2889F><EFBFBD>㺭<EFBFBD>? const result = await prisma.$executeRaw`
|
||
DELETE FROM app_cache
|
||
WHERE id IN (
|
||
SELECT id FROM app_cache
|
||
WHERE expires_at < NOW()
|
||
LIMIT 1000
|
||
)
|
||
`;
|
||
|
||
if (result > 0) {
|
||
logger.info('[PostgresCache] 皜<EFBFBD><EFBFBD>餈<EFBFBD><EFBFBD><EFBFBD>唳旿', { count: result });
|
||
}
|
||
|
||
} catch (error) {
|
||
logger.error('[PostgresCache] 摰𡁏𧒄皜<EFBFBD><EFBFBD>憭梯揖', { error });
|
||
}
|
||
}, 60000); // 瘥誩<E798A5><E8AAA9><EFBFBD><EFBFBD>銵䔶<E98AB5>甈?
|
||
logger.info('[PostgresCache] 摰𡁏𧒄皜<EFBFBD><EFBFBD>隞餃𦛚撌脣鍳<EFBFBD>剁<EFBFBD>瘥誩<EFBFBD><EFBFBD>?000<EFBFBD>∴<EFBFBD>');
|
||
}
|
||
```
|
||
|
||
#### **<2A>扯<EFBFBD>隡睃<E99AA1><E79D83><EFBFBD>撌?*
|
||
|
||
1. **蝝W<E89D9D>隡睃<E99AA1>**嚗䫤@@index([key, expiresAt])` 閬<><E996AC><EFBFBD>亥砭嚗峕<E59A97><E5B395><EFBFBD><EFBFBD>噼”
|
||
2. **<EFBFBD>埝<EFBFBD><EFBFBD>𣳇膄**嚗朞粉<E69C9E>𡝗𧒄憿箔噶皜<E599B6><E79A9C>嚗<EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD>頧?3. **<EFBFBD><EFBFBD>鸌皜<EFBFBD><EFBFBD>**嚗𡁏<E59A97>甈‥IMIT 1000嚗峕神蝘垍漣摰峕<E691B0>
|
||
4. **餈墧𦻖瘙惩<E79899><E683A9>?*嚗䥪risma<6D>芸𢆡蝞∠<E89D9E>嚗峕<E59A97>憸嘥<E686B8>撘<EFBFBD><E69298><EFBFBD>
|
||
|
||
### **3.3 <20>蹂誨 Redis 隡朞<E99AA1>嚗䬙onnect-pg-simple**
|
||
|
||
雿輻鍂<EFBFBD>鞟<EFBFBD><EFBFBD><EFBFBD>冗<EFBFBD>箸䲮獢<EFBFBD><EFBFBD>撠?Session 摮睃<E691AE><E79D83>?Postgres <20>?session 銵其葉<E585B6><E89189>AE 憭𡁜<E686AD>靘钅<E99D98><E99285>臬<EFBFBD>嚗𣬚鍂<F0A3AC9A>瑟<EFBFBD><E7919F><EFBFBD><EFBFBD>齿鰵<E9BDBF>餃<EFBFBD><E9A483>?
|
||
## **4\. 瘛勗漲撖寞<E69296>嚗帋蛹隞<E89BB9>銋?Postgres <20>𨅯枂嚗?*
|
||
|
||
| 蝏游漲 | <20>寞<EFBFBD> A: 隡删<E99AA1> Redis (BullMQ) | <20>寞<EFBFBD> B: Postgres (pg-boss) | <20>瑁<EFBFBD><E79181>笔<EFBFBD> |
|
||
| :---- | :---- | :---- | :---- |
|
||
| **<EFBFBD>唳旿銝<EFBFBD><EFBFBD>湔<EFBFBD>?* | **撘?* (<28><><EFBFBD>銝<EFBFBD><E98A9D>湔<EFBFBD>折𠗕憸? 隞餃𦛚<E9A483>?Redis嚗䔶<E59A97><E494B6>∪銁 DB<44><42>𥅾 DB 鈭见𦛚<E8A781>墧<EFBFBD>嚗朙edis 隞餃𦛚<E9A483>航<EFBFBD><E888AA>䭾<EFBFBD><E4ADBE>墧<EFBFBD><E5A2A7>?| **撘?* (鈭见𦛚蝥批<E89DA5>摮鞉<E691AE>? 隞餃𦛚<E9A483>仿<EFBFBD>銝𦒘<E98A9D><F0A69298>⊥㺭<E28AA5>桀<EFBFBD><E6A180>亙銁<E4BA99>䔶<EFBFBD>銝?DB 鈭见𦛚銝准<E98A9D><E58786><EFBFBD>銋<EFBFBD><E98A8B><EFBFBD>琜<EFBFBD>閬<EFBFBD><E996AC><EFBFBD>刻揖<E588BB>?| **Postgres** |
|
||
| **餈鞟輕憭齿<E686AD>摨?* | **擃?* <20><>蝏湔擪 Redis 摰硺<E691B0><E7A1BA><EFBFBD>PC <20>賢<EFBFBD><E8B3A2>𨰻<EFBFBD><F0A8B0BB><EFBFBD><EFBFBD>批<EFBFBD>摮条<E691AE><E69DA1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>銋<EFBFBD><E98A8B>蝑𣇉裦<F0A38789>?| **<EFBFBD>?* 憭滨鍂<E6BBA8>唳<EFBFBD> RDS<44><53><EFBFBD>隞賬<E99A9E><E8B3AC><EFBFBD><EFBFBD>扼<EFBFBD><E689BC><EFBFBD>摰孵<E691B0><E5ADB5>梢燵<E6A2A2>䔶<EFBFBD> RDS <20>条恣<E69DA1>?| **Postgres** |
|
||
| **憭<>遢銝擧<E98A9D>憭?* | **<EFBFBD>圈𠗕** Redis RDB/AOF <20>W<EFBFBD><EFBCB7>航<EFBFBD>隡帋腺憭望<E686AD><E69C9B>𤾸<EFBFBD>蝘垍<E89D98><E59E8D>唳旿<E594B3>?| **摰𣬚<E691B0>** RDS <20>舀<EFBFBD> PITR (<28>㗇𧒄<E39787>渡<EFBFBD><E6B8A1>W<EFBFBD>)<29><>秤<EFBFBD>牐遙<E78990>∪虾蝎曄&<E69B84>墧<EFBFBD><E5A2A7>?1蝘鍦<E89D98><E98DA6>?| **Postgres** |
|
||
| **<EFBFBD>鞉𧋦** | **瞼1000+/撟?* (Tair <20>箇<EFBFBD><E7AE87>? | **瞼0** (韏<><E99F8F>憭滨鍂) | **Postgres** |
|
||
| **<EFBFBD>扯<EFBFBD> (TPS)** | **<EFBFBD><EFBFBD><EFBFBD>** (10w+) | **擃?* (5000+) 撖嫣<E69296><E5ABA3>亙<EFBFBD><E4BA99>牐<EFBFBD>甈?AI 靚<>鍂<EFBFBD><E98D82>㦤<EFBFBD>荔<EFBFBD>Postgres <20>扯<EFBFBD>蝏啁趕<E59581>劐<EFBFBD><E58A90>?| **Postgres** |
|
||
| **<EFBFBD><EFBFBD><EFBFBD>鈭厰䔮憸?* | **霂航圾** 霂餃<E99C82><E9A483>賡<EFBFBD>閬<EFBFBD><E996AC>蝏𨅯<E89D8F>餈䈑<E9A488>擃睃僎<E79D83>睲<EFBFBD>Redis銋煺<E98A8B><E785BA>厩<EFBFBD>鈭?| **<EFBFBD>毺㮾** SELECT<43>臬翰<E887AC>扯粉嚗䔶<E59A97><E494B6>𣳇<EFBFBD><F0A3B387><EFBFBD>ode.js<6A>閧瑪蝔衤<E89D94><E8A1A4><EFBFBD><EFBFBD>銝箇𣂎憸<F0A3828E><E686B8>?| **霂航圾瞉<E59CBE><E79E89>** |
|
||
| **蝻枏<E89DBB>皜<EFBFBD><E79A9C>憌𡡞埯** | **摮睃銁** <20><><EFBFBD>皞W枂<EFBCB7><E69E82>閬<EFBFBD><E996AC>蝵容viction蝑𣇉裦嚗䔶<E59A97>敶㯄<E695B6>蝵桐<E89DB5>撖潸稲<E6BDB8>唳旿銝W仃 | **<EFBFBD>舀綉** <20><>鸌<EFBFBD>𣳇膄嚗𡿨IMIT 1000嚗? <20>埝<EFBFBD><E59F9D>𣳇膄嚗峕偶餈靝<E9A488>隡𡁻獈憛𠺶<E6869B>?| **Postgres** |
|
||
| **摮虫<E691AE><E899AB>脩瑪** | **<EFBFBD>∪陪** <20><>閬<EFBFBD>郎銋袠edis<69><73>ullMQ<4D><51>oredis<69><73><EFBFBD>銋<EFBFBD><E98A8B>蝑𣇉裦<F0A38789><E8A3A6><EFBFBD>摮条恣<E69DA1>?| **撟喟<E6929F>** <20>芷<EFBFBD>摮虫<E691AE>pg-boss嚗㇁PI蝐颱撮BullMQ嚗㚁<E59A97><E39A81>嗡<EFBFBD><E597A1>賣糓<E8B3A3><E7B393><EFBFBD><EFBFBD><EFBFBD>ostgres | **Postgres** |
|
||
|
||
### **撣貉<E692A3>霂航圾瞉<E59CBE><E79E89>**
|
||
|
||
#### **霂航圾1嚗䥪ostgres撟嗅<E6929F><E59785>扯<EFBFBD>撌?*
|
||
```
|
||
鈭见<EFBFBD>嚗?- Postgres<65>臬<EFBFBD><E887AC>?銝? QPS嚗<53><E59A97><EFBFBD>閙䰻霂g<E99C82>
|
||
- <20>函<EFBFBD>摰鮋<E691B0>撟嗅<E6929F>: < 50 QPS
|
||
- SELECT<43>臬翰<E887AC>扯粉嚗㇈VCC嚗㚁<E59A97><E39A81>𣳇<EFBFBD>蝡硺<E89DA1>
|
||
- Node.js<6A>閧瑪蝔页<E89D94>1-2銝𣘗PS銝𢠃<E98A9D>嚗劐<E59A97><E58A90><EFBFBD><EFBFBD>銝箇𣂎憸?
|
||
蝏栞捏嚗䥪ostgres銝齿糓<EFBFBD>園<EFBFBD>
|
||
```
|
||
|
||
#### **霂航圾2嚗鋽ELETE隡𡁻<E99AA1>銵券獈憛?*
|
||
```
|
||
鈭见<EFBFBD>嚗?- DELETE<54>航<EFBFBD>蝥折<E89DA5>嚗䔶<E59A97><E494B6>航”<E888AA>?- LIMIT 1000嚗峕神蝘垍漣摰峕<E691B0>嚗ǚ5ms嚗?- <20>滚<EFBFBD><E6BB9A>埝<EFBFBD><E59F9D>𣳇膄嚗<E88684>之<EFBFBD>典<EFBFBD>餈<EFBFBD><E9A488><EFBFBD>唳旿<E594B3>刻粉<E588BB>𡝗𧒄撌脣<E6928C><E884A3>?- <20>喃蝙<E59683>厩妖<E58EA9>页<EFBFBD>瘥誩<E798A5><E8AAA9>?000<30>∴<EFBFBD>1撠𤩺𧒄<F0A4A9BA>舀<EFBFBD><E88880>?銝<>辺
|
||
|
||
蝏栞捏嚗帋<EFBFBD>隡𡁻獈憛?```
|
||
|
||
#### **霂航圾3嚗鑹edis<69><73><EFBFBD><EFBFBD>滢<EFBFBD>銝<EFBFBD>摰𡁜翰**
|
||
```
|
||
鈭见<EFBFBD>嚗?- Redis: 蝵𤑳<E89DB5>撱嗉<E692B1>0.1ms + 霂餃<E99C82>0.05ms = 0.15ms
|
||
- Postgres: 蝵𤑳<E89DB5>撱嗉<E692B1>0.5ms + <20>亥砭1ms = 1.5ms
|
||
- 撌桀<E6928C>: 1.35ms
|
||
- 雿<>糓<EFBFBD>餃<EFBFBD>摨娍𧒄<E5A88D>湛<EFBFBD><E6B99B>思<EFBFBD><E6809D>⊿<EFBFBD>餉<EFBFBD>嚗? 200ms
|
||
- <20>冽<EFBFBD><E586BD>毺䰻撌桀<E6928C>: 0%嚗?.35/200 < 1%嚗?
|
||
蝏栞捏嚗𡁏<EFBFBD>扯<EFBFBD>撌桀<EFBFBD><EFBFBD>函鍂<EFBFBD>瑚<EFBFBD>撉䔶葉<EFBFBD>䭾<EFBFBD><EFBFBD>?```
|
||
|
||
## **5\. <20><>笆<EFBFBD>?撠𤩺𧒄<F0A4A9BA>蹂遙<E8B982>﹦<EFBFBD>萘<EFBFBD><E89098>舫<EFBFBD><E888AB>扯<EFBFBD><E689AF>?*
|
||
|
||
韐函<EFBFBD>嚗䥪ostgres <20>毺<EFBFBD><E6AFBA>賭<EFBFBD>霂?2 撠𤩺𧒄<F0A4A9BA><F0A79284>遙<EFBFBD>∩<EFBFBD>銝剜鱏<E5899C>梹<EFBFBD>
|
||
霂<EFBFBD><EFBFBD>嚗?
|
||
1. **<2A><><EFBFBD><EFBFBD>碶<EFBFBD><E7A2B6>?*嚗帋遙<E5B88B>∩<EFBFBD><E288A9>行<EFBFBD>鈭歹<E988AD>API餈𥪜<E9A488> 200 OK嚗㚁<E59A97><E39A81>喳<EFBFBD><E596B3>亦′<E4BAA6>塩<EFBFBD><E5A1A9>朖雿?SAE <20><>黎銝衤<E98A9D>蝘鍦<E89D98><E98DA6>哨<EFBFBD>隞餃𦛚霈啣<E99C88>靘萘<E99D98><E89098>冽㺭<E586BD>桀<EFBFBD>銝准<E98A9D>?
|
||
2. **撏拇<E6928F><E68B87>W<EFBFBD><EFBCB7>箏<EFBFBD>**嚗?
|
||
* **甇<>虜<EFBFBD><E8999C><EFBFBD>**嚗阳orker <20><><EFBFBD>隞餃𦛚 \-\> <20>扯<EFBFBD> 2 撠𤩺𧒄 \-\> <20>𣂷漱蝏𤘪<E89D8F> \-\> <20><>扇摰峕<E691B0><E5B395>?
|
||
* **撘<>虜<EFBFBD><E8999C><EFBFBD> (SAE 蝻拙捆)**嚗阳orker <20>扯<EFBFBD><E689AF>?1 撠𤩺𧒄鋡恍<E98BA1>瘥?\-\> <20>唳旿摨㯄<E691A8><E3AF84>?4 撠𤩺𧒄<F0A4A9BA>舘<EFBFBD><E88898>?\-\> pg-boss 摰<>擪餈𤤿<E9A488>璉<EFBFBD>瘚见<E7989A>餈<EFBFBD><E9A488> \-\> 撠<>遙<EFBFBD>⊿<EFBFBD><E28ABF>唳<EFBFBD>霈唬蛹 Pending \-\> <20>?Worker 憸<><E686B8><EFBFBD>滩<EFBFBD><E6BBA9>?
|
||
3. **<2A>剔<EFBFBD>蝏凋<E89D8F>**嚗阳orker <20>臬<EFBFBD><E887AC><EFBFBD><EFBFBD>憒<EFBFBD><E68692> 10 <20><><EFBFBD>嚗㗇凒<E39787>唳㺭<E594B3>桀<EFBFBD>銝剔<E98A9D> progress 摮埈挾<E59F88><E68CBE><EFBFBD>霂閙𧒄霂餃<E99C82> progress嚗䔶<E59A97><E494B6>剔<EFBFBD>蝏抒賒<E68A92>扯<EFBFBD><E689AF>?
|
||
**蝏栞捏**嚗𡁻<E59A97><F0A181BB>?pg-boss <20><>香靽⊿<E99DBD><E28ABF>梹<EFBFBD>Dead Letter嚗匧<E59A97><E58CA7>滩<EFBFBD>蝑𣇉裦嚗<E8A3A6>虾<EFBFBD>䭾<EFBFBD>抒<EFBFBD><E68A92>𣬚<EFBFBD><F0A3AC9A>喲<EFBFBD>鈭?Redis嚗<73><E59A97>銝?Redis <20><><EFBFBD>皞W枂憌𡡞埯<F0A1A19E>游之嚗剹<E59A97>?
|
||
## **6\. 摰墧鴌頝舐瑪<E88890>?*
|
||
|
||
### **<2A>嗆挾1嚗帋遙<E5B88B>⊿<EFBFBD><E28ABF>埈㺿<E59F88>𩤃<EFBFBD>Week 1嚗?*
|
||
|
||
#### **Step 1.1嚗𡁜<EFBFBD>鋆<EFBFBD><EFBFBD>韏?*
|
||
```bash
|
||
cd backend
|
||
npm install pg-boss --save
|
||
```
|
||
|
||
#### **Step 1.2嚗𡁜<EFBFBD><EFBFBD>訐gBossQueue<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?*
|
||
```typescript
|
||
// <20><>辣嚗颹ackend/src/common/jobs/PgBossQueue.ts
|
||
|
||
import PgBoss from 'pg-boss';
|
||
import type { Job, JobQueue, JobHandler } from './types';
|
||
import { logger } from '../logging/index';
|
||
import { config } from '../../config/env';
|
||
|
||
export class PgBossQueue implements JobQueue {
|
||
private boss: PgBoss;
|
||
private started = false;
|
||
|
||
constructor() {
|
||
this.boss = new PgBoss({
|
||
connectionString: config.databaseUrl,
|
||
schema: 'job_queue', // <20>祉<EFBFBD>schema嚗䔶<E59A97>瘙⊥<E79899>銝𡁜𦛚銵? max: 5, // 餈墧𦻖瘙惩之撠? // <20>喲睸<E596B2>滨蔭嚗朞挽蝵桅<E89DB5><E6A185><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>銝?撠𤩺𧒄
|
||
// 靽肽<E99DBD>2撠𤩺𧒄隞餃𦛚銝滩◤<E6BBA9>Z粥嚗䔶<E59A97>摰硺<E691B0>撏拇<E6928F><E68B87>舘<EFBFBD><E88898>芸𢆡<E88AB8>W<EFBFBD>
|
||
expireInHours: 4,
|
||
});
|
||
|
||
// <20>穃𨯬<E7A983>躰秤
|
||
this.boss.on('error', error => {
|
||
logger.error('[PgBoss] <20>躰秤', { error });
|
||
});
|
||
}
|
||
|
||
async start(): Promise<void> {
|
||
if (this.started) return;
|
||
|
||
await this.boss.start();
|
||
this.started = true;
|
||
logger.info('[PgBoss] <20>笔<EFBFBD>撌脣鍳<E884A3>?);
|
||
}
|
||
|
||
async push<T = any>(type: string, data: T, options?: any): Promise<Job> {
|
||
await this.start();
|
||
|
||
const jobId = await this.boss.send(type, data, {
|
||
retryLimit: 3,
|
||
retryDelay: 60, // 憭梯揖<E6A2AF>?0蝘㘾<E89D98>霂? expireInHours: 4, // 4撠𤩺𧒄<F0A4A9BA>舘<EFBFBD><E88898>? ...options
|
||
});
|
||
|
||
logger.info('[PgBoss] 隞餃𦛚<E9A483>仿<EFBFBD>', { type, jobId });
|
||
|
||
return {
|
||
id: jobId,
|
||
type,
|
||
data,
|
||
status: 'pending',
|
||
createdAt: new Date(),
|
||
};
|
||
}
|
||
|
||
process<T = any>(type: string, handler: JobHandler<T>): void {
|
||
this.boss.work(type, async (job: any) => {
|
||
logger.info('[PgBoss] 撘<>憪见<E686AA><E8A781><EFBFBD>遙<EFBFBD>?, {
|
||
type,
|
||
jobId: job.id,
|
||
attemptsMade: job.data.__retryCount || 0
|
||
});
|
||
|
||
const startTime = Date.now();
|
||
|
||
try {
|
||
const result = await handler({
|
||
id: job.id,
|
||
type,
|
||
data: job.data as T,
|
||
status: 'processing',
|
||
createdAt: new Date(job.createdon),
|
||
});
|
||
|
||
logger.info('[PgBoss] 隞餃𦛚摰峕<E691B0>', {
|
||
type,
|
||
jobId: job.id,
|
||
duration: `${Date.now() - startTime}ms`
|
||
});
|
||
|
||
return result;
|
||
|
||
} catch (error) {
|
||
logger.error('[PgBoss] 隞餃𦛚憭梯揖', {
|
||
type,
|
||
jobId: job.id,
|
||
error: error instanceof Error ? error.message : 'Unknown'
|
||
});
|
||
throw error; // <20>𥕦枂<F0A595A6>躰秤閫血<E996AB><E8A180>滩<EFBFBD>
|
||
}
|
||
});
|
||
|
||
logger.info('[PgBoss] Worker撌脫釣<E884AB>?, { type });
|
||
}
|
||
|
||
async getJob(id: string): Promise<Job | null> {
|
||
const job = await this.boss.getJobById(id);
|
||
if (!job) return null;
|
||
|
||
return {
|
||
id: job.id,
|
||
type: job.name,
|
||
data: job.data,
|
||
status: this.mapState(job.state),
|
||
createdAt: new Date(job.createdon),
|
||
};
|
||
}
|
||
|
||
async updateProgress(id: string, progress: number, message?: string): Promise<void> {
|
||
// pg-boss<73><73><EFBFBD><EFBFBD>舀<EFBFBD>餈𥕦漲<F0A595A6>湔鰵嚗<E9B0B5>虾<EFBFBD>朞<EFBFBD><E69C9E>湔鰵銝𡁜𦛚銵典<E98AB5><E585B8>? logger.debug('[PgBoss] 餈𥕦漲<F0A595A6>湔鰵', { id, progress, message });
|
||
}
|
||
|
||
async cancelJob(id: string): Promise<boolean> {
|
||
await this.boss.cancel(id);
|
||
return true;
|
||
}
|
||
|
||
async retryJob(id: string): Promise<boolean> {
|
||
await this.boss.resume(id);
|
||
return true;
|
||
}
|
||
|
||
async cleanup(olderThan: number = 86400000): Promise<number> {
|
||
// pg-boss<73>㕑䌊<E39591>冽<EFBFBD><E586BD><EFBFBD>㦤<EFBFBD>? return 0;
|
||
}
|
||
|
||
private mapState(state: string): string {
|
||
switch (state) {
|
||
case 'completed': return 'completed';
|
||
case 'failed': return 'failed';
|
||
case 'active': return 'processing';
|
||
default: return 'pending';
|
||
}
|
||
}
|
||
|
||
async close(): Promise<void> {
|
||
await this.boss.stop();
|
||
logger.info('[PgBoss] <20>笔<EFBFBD>撌脣<E6928C><E884A3>?);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### **Step 1.3嚗𡁏凒<EFBFBD>衷obFactory**
|
||
```typescript
|
||
// <20><>辣嚗颹ackend/src/common/jobs/JobFactory.ts
|
||
|
||
import { JobQueue } from './types';
|
||
import { MemoryQueue } from './MemoryQueue';
|
||
import { PgBossQueue } from './PgBossQueue';
|
||
import { logger } from '../logging/index';
|
||
import { config } from '../../config/env';
|
||
|
||
export class JobFactory {
|
||
private static instance: JobQueue | null = null;
|
||
|
||
static getInstance(): JobQueue {
|
||
if (!this.instance) {
|
||
this.instance = this.createQueue();
|
||
}
|
||
return this.instance;
|
||
}
|
||
|
||
private static createQueue(): JobQueue {
|
||
const queueType = config.queueType || 'pgboss'; // 暺䁅恕雿輻鍂pgboss
|
||
|
||
switch (queueType) {
|
||
case 'pgboss':
|
||
return new PgBossQueue();
|
||
|
||
case 'memory':
|
||
logger.warn('[JobFactory] 雿輻鍂<E8BCBB><E98D82><EFBFBD><EFBFBD>笔<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>𤑳㴓憓<E3B493><E68693>');
|
||
return new MemoryQueue();
|
||
|
||
default:
|
||
logger.warn(`[JobFactory] <20>芰䰻<E88AB0>笔<EFBFBD>蝐餃<E89D90>: ${queueType}嚗䔶蝙<E494B6>私gboss`);
|
||
return new PgBossQueue();
|
||
}
|
||
}
|
||
|
||
static reset(): void {
|
||
this.instance = null;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### **Step 1.4嚗𡁏凒<EFBFBD>啁㴓憓<EFBFBD><EFBFBD><EFBFBD>?*
|
||
```env
|
||
# backend/.env
|
||
QUEUE_TYPE=pgboss
|
||
DATABASE_URL=postgresql://user:password@host:5432/dbname
|
||
```
|
||
|
||
#### **Step 1.5嚗𡁏<EFBFBD>霂𤏪<EFBFBD>2撠𤩺𧒄<EFBFBD>蹂遙<EFBFBD>∴<EFBFBD>**
|
||
```bash
|
||
# <20>𣂷漱1000蝭<30><E89DAD><EFBFBD>桃<EFBFBD><E6A183>劐遙<E58A90>?curl -X POST http://localhost:3001/api/v1/asl/projects/:id/screening
|
||
|
||
# 蝑匧<E89D91>憭<EFBFBD><E686AD><EFBFBD>?0% <20>?<3F>见𢆡<E8A781>𨀣迫<F0A880A3>滚𦛚嚗㇃trl+C嚗?# <20>滚鍳<E6BB9A>滚𦛚
|
||
npm run dev
|
||
|
||
# <20>亦<EFBFBD>隞餃𦛚<E9A483>嗆<EFBFBD>?<3F>?摨磰砲<E7A3B0>芸𢆡<E88AB8>W<EFBFBD>撟嗥誧蝏剖<E89D8F><E58996>?```
|
||
|
||
---
|
||
|
||
### **<2A>嗆挾2嚗𡁶<E59A97>摮䀹㺿<E480B9>𩤃<EFBFBD>Week 2嚗?*
|
||
|
||
#### **Step 2.1嚗𡁏溶<EFBFBD>蘯risma Schema**
|
||
```prisma
|
||
// backend/prisma/schema.prisma
|
||
|
||
model AppCache {
|
||
id Int @id @default(autoincrement())
|
||
key String @unique
|
||
value Json
|
||
expiresAt DateTime
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([expiresAt])
|
||
@@index([key, expiresAt])
|
||
@@map("app_cache")
|
||
}
|
||
```
|
||
|
||
```bash
|
||
# <20><><EFBFBD>餈<EFBFBD>宏
|
||
npx prisma migrate dev --name add_app_cache
|
||
```
|
||
|
||
#### **Step 2.2嚗𡁜<EFBFBD><EFBFBD>訐ostgresCacheAdapter**
|
||
嚗<EFBFBD><EFBFBD>銝𦠜<EFBFBD>"3.2 <20>蹂誨 Redis 蝻枏<E89DBB>"<22>典<EFBFBD>嚗?
|
||
#### **Step 2.3嚗𡁏凒<EFBFBD>蚓acheFactory**
|
||
```typescript
|
||
// <20><>辣嚗颹ackend/src/common/cache/CacheFactory.ts
|
||
|
||
export class CacheFactory {
|
||
static getInstance(): CacheAdapter {
|
||
const cacheType = config.cacheType || 'postgres';
|
||
|
||
switch (cacheType) {
|
||
case 'postgres':
|
||
return new PostgresCacheAdapter();
|
||
case 'memory':
|
||
return new MemoryCacheAdapter();
|
||
default:
|
||
return new PostgresCacheAdapter();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### **Step 2.4嚗𡁜鍳<EFBFBD>典<EFBFBD><EFBFBD>嗆<EFBFBD><EFBFBD>?*
|
||
```typescript
|
||
// <20><>辣嚗颹ackend/src/index.ts
|
||
|
||
import { startCacheCleanupTask } from './common/cache/PostgresCacheAdapter';
|
||
|
||
// <20>典<EFBFBD><E585B8>典鍳<E585B8>冽𧒄
|
||
await app.listen({ port: 3001, host: '0.0.0.0' });
|
||
startCacheCleanupTask(); // <20>臬𢆡蝻枏<E89DBB>皜<EFBFBD><E79A9C>
|
||
```
|
||
|
||
---
|
||
|
||
### **<2A>嗆挾3嚗锭AE<41>函蔡嚗Áeek 3嚗?*
|
||
|
||
1. **<2A>砍𧑐瘚贝<E7989A><E8B49D>朞<EFBFBD>**嚗㇁SL 1000蝭<30><E89DAD><EFBFBD>?+ DC 100隞賜<E99A9E><E8B39C><EFBFBD><EFBFBD>
|
||
2. **<2A>唳旿摨栞<E691A8><E6A09E>仿<EFBFBD>蝵?*嚗𠄎AE<41>臬<EFBFBD><E887AC>㗛<EFBFBD>霈曄蔭DATABASE_URL嚗?3. **<2A>啣漲<E595A3>穃<EFBFBD>**嚗<><E59A97>1銝芸<E98A9D>靘页<E99D98>閫<EFBFBD><E996AB>24撠𤩺𧒄嚗?4. **<2A>券<EFBFBD>銝羓瑪**嚗<><E59A97>摰孵<E691B0>2-3銝芸<E98A9D>靘页<E99D98>
|
||
|
||
## **7\. <20>扯<EFBFBD>颲寧<E9A2B2>銝擧<E98A9D>撅閗楝敺?*
|
||
|
||
### **7.1 <20><>鍂閫<E98D82>芋**
|
||
|
||
| <20><><EFBFBD> | Postgres-Only<6C>寞<EFBFBD>銝𢠃<E98A9D> | <20>函<EFBFBD>敶枏<E695B6><E69E8F>?| 摰匧<E691B0>雿䠷<E99BBF> |
|
||
|------|---------------------|-----------|---------|
|
||
| <20>交暑<E4BAA4>冽<EFBFBD> | 10銝?| 500 | 200<30>?|
|
||
| 撟嗅<E6929F>QPS | 5000 | < 50 | 100<30>?|
|
||
| 蝻枏<E89DBB>摰寥<E691B0> | 10GB | < 100MB | 100<30>?|
|
||
| <20>笔<EFBFBD><E7AC94>𧼮<EFBFBD> | 1000隞餃𦛚/撠𤩺𧒄 | < 50隞餃𦛚/撠𤩺𧒄 | 20<32>?|
|
||
|
||
**蝏栞捏嚗𡁜銁<F0A1819C>舫<EFBFBD>閫<EFBFBD><E996AB><EFBFBD>芣䔉嚗?-3撟湛<E6929F>嚗峕<E59A97>銝滢<E98A9D>頞<EFBFBD>枂餈嗘葵銝𢠃<E98A9D><F0A2A083>?*
|
||
|
||
### **7.2 雿閙𧒄<E99699><F0A79284>閬<EFBFBD>edis嚗?*
|
||
|
||
<EFBFBD>芣<EFBFBD><EFBFBD>其誑銝𧢲<EFBFBD><EFBFBD>萄<EFBFBD><EFBFBD><EFBFBD>𧒄嚗峕<EFBFBD><EFBFBD><EFBFBD>閬<EFBFBD><EFBFBD><EFBFBD><EFBFBD>撘訫<EFBFBD>Redis嚗?
|
||
```
|
||
閫血<EFBFBD><EFBFBD>∩辣嚗㇁NY嚗㚁<EFBFBD>
|
||
<EFBFBD>?<3F>交暑 > 5銝?<3F>?撟嗅<E6929F>QPS > 1000
|
||
<EFBFBD>?Postgres CPU雿輻鍂<E8BCBB><E98D82><EFBFBD>蝏?> 70%
|
||
<EFBFBD>?蝻枏<E89DBB><E69E8F>亥砭撱嗉<E692B1> > 50ms嚗㇊99嚗?<3F>?LLM API<50><49><EFBFBD><EFBFBD>?> 瞼5000嚗<30><E59A97>摮睃𦶢銝剔<E98A9D>雿𠬍<E99BBF>
|
||
|
||
餈<EFBFBD>宏蝑𣇉裦嚗?1. <20><><EFBFBD>蝘腿LM蝻枏<E89DBB><E69E8F>訌edis嚗<73><E59A97>憸𤏸粉嚗?2. 靽脲<E99DBD>隞餃𦛚<E9A483>笔<EFBFBD><E7AC94>沌ostgres嚗<73>撩銝<E692A9><E98A9D>湔<EFBFBD>改<EFBFBD>
|
||
3. 銝𡁜𦛚蝻枏<E89DBB><E69E8F>厰<EFBFBD>餈<EFBFBD>宏
|
||
|
||
<EFBFBD>鞉𧋦嚗?- 餈<>宏撌乩<E6928C><E4B9A9>? 2-3憭?- 餈鞟輕憓𧼮<E68693>: <20>舀𦻖<E88880>梹<EFBFBD>撌脫<E6928C>蝏誯<E89D8F>嚗?```
|
||
|
||
### **7.3 <20>拙<EFBFBD>頝臬<E9A09D>**
|
||
|
||
```
|
||
<EFBFBD>嗆挾1嚗<EFBFBD><EFBFBD><EFBFBD>?5000<30>冽<EFBFBD>嚗? Postgres-Only
|
||
<20>鎿<EFBFBD> <20>笔<EFBFBD>: pg-boss
|
||
<20>鎿<EFBFBD> 蝻枏<E89DBB>: Postgres銵? <20>婙<EFBFBD> <20>鞉𧋦: 瞼0
|
||
|
||
<EFBFBD>嗆挾2嚗?000-5銝<35>鍂<EFBFBD>瘀<EFBFBD>: 瘛瑕<E7989B><E79195>嗆<EFBFBD>
|
||
<20>鎿<EFBFBD> <20>笔<EFBFBD>: pg-boss嚗<73><E59A97><EFBFBD><EFBFBD><EFBFBD>
|
||
<20>鎿<EFBFBD> LLM蝻枏<E89DBB>: Redis嚗<73><E59A97>蝘鳴<E89D98>
|
||
<20>鎿<EFBFBD> 銝𡁜𦛚蝻枏<E89DBB>: Postgres嚗<73><E59A97><EFBFBD><EFBFBD><EFBFBD>
|
||
<20>婙<EFBFBD> <20>鞉𧋦: +瞼1000/撟?
|
||
<EFBFBD>嗆挾3嚗?銝?50銝<30>鍂<EFBFBD>瘀<EFBFBD>: <20>沖edis
|
||
<20>鎿<EFBFBD> <20>笔<EFBFBD>: BullMQ + Redis
|
||
<20>鎿<EFBFBD> 蝻枏<E89DBB>: Redis
|
||
<20>婙<EFBFBD> <20>鞉𧋦: +瞼5000/撟?```
|
||
|
||
---
|
||
|
||
## **8\. FAQ嚗<51>虜閫<E8999C><E996AB><EFBFBD>殷<EFBFBD>**
|
||
|
||
### **Q1: pg-boss隡帋<E99AA1>隡𡁏<E99AA1><F0A1818F>㎜ostgres嚗?*
|
||
|
||
**A:** 銝滢<E98A9D><E6BBA2><EFBFBD>g-boss<73><73>䰻霂a<E99C82><EFBD81>厩揣撘蓥<E69298><E893A5>吔<EFBFBD><E59094>閙活<E99699>亥砭 < 5ms<6D><73>朖雿?00銝杪orker<65>峕𧒄<E5B395>V遙<EFBCB6>∴<EFBFBD>銋笔蘨<E7AC94>?00ms<6D><73><EFBFBD>憭𤥁<E686AD>頧踝<E9A0A7>撖嫣<E69296>5銝𣘗PS<50><53>ostgres<65>亥秩<E4BAA5>臬蕭<E887AC>乓<EFBFBD>?
|
||
### **Q2: 蝻枏<E89DBB>銵其<E98AB5>銝滢<E98A9D><E6BBA2>𣳇<EFBFBD>憓鮋鵭嚗?*
|
||
|
||
**A:** 銝滢<E98A9D><E6BBA2><EFBFBD><EFBFBD><EFBFBD>啣<EFBFBD><E595A3>?+ <20><>鸌皜<E9B88C><E79A9C>嚗諹<E59A97><E8ABB9><EFBFBD>㺭<EFBFBD>桐<EFBFBD>鋡怨䌊<E680A8>冽<EFBFBD><E586BD><EFBFBD><EFBFBD><EFBFBD>朖雿踵<E99BBF>蝘臬<E89D98>嚗峕<E59A97><E5B395><EFBFBD><EFBFBD>1000<30>∠<EFBFBD>皜<EFBFBD><E79A9C><EFBFBD>笔漲頞喃誑摨𥪜笆<F0A5AA9C>?
|
||
### **Q3: 憒<><E68692>Postgres<65><73><EFBFBD><EFBFBD>𦒘<EFBFBD><F0A69298>痹<EFBFBD>**
|
||
|
||
**A:**
|
||
- **<2A>輸<EFBFBD>鈭駵DS**嚗𡁻<E59A97><F0A181BB>舐鍂<E88890><E98D82>䌊<EFBFBD>其蜓隞𤾸<E99A9E><F0A4BEB8>g<EFBFBD><EFBD87><EFBFBD><EFBFBD><EFBFBD>W<EFBFBD> < 30蝘?- **憭<>遢<EFBFBD>W<EFBFBD>**嚗䥪ITR<54>舀<EFBFBD>憭滚<E686AD>隞餅<E99A9E>蝘𡜐<E89D98><F0A19C90>唳旿銝滢腺憭?- **<2A>滨漣蝑𣇉裦**嚗𡁻<E59A97><F0A181BB>堒<EFBFBD>蝻枏<E89DBB><E69E8F>賢銁DB嚗䔶<E59A97>韏瑟<E99F8F>憭㵪<E686AD><E3B5AA>牐<EFBFBD>銝<EFBFBD><E98A9D>湧<EFBFBD><E6B9A7>?
|
||
<EFBFBD>豢<EFBFBD>銋衤<EFBFBD>嚗朙edis<EFBFBD><EFBFBD><EFBFBD>餈㗛<EFBFBD>閬<EFBFBD><EFBFBD>敹<EFBFBD>㺭<EFBFBD>桐<EFBFBD>銝<EFBFBD><EFBFBD>湧䔮憸塩<EFBFBD>?
|
||
### **Q4: 銝箔<E98A9D>銋<EFBFBD><E98A8B><EFBFBD>沖edis嚗<73>㭂霂渲䌊撌望糓鈭穃<E988AD><E7A983><EFBFBD><EFBFBD>**
|
||
|
||
**A:** 鈭穃<E988AD><E7A983>毺<EFBFBD><E6AFBA>詨<EFBFBD><E8A9A8>?*<2A>嗆<EFBFBD><E59786><EFBFBD>蝵?*嚗䔶<E59A97><E494B6>?*敹<>◆<EFBFBD>沖edis**<2A>?
|
||
```
|
||
鈭穃<EFBFBD><EFBFBD>毺<EFBFBD><EFBFBD>祈捶嚗?<3F>?<3F>删𠶖<E588A0><F0A0B696><EFBFBD><EFBFBD>剁<EFBFBD>銝滢<E98A9D>韏𡝗𧋦<F0A19D97>啣<EFBFBD>摮矋<E691AE>
|
||
<EFBFBD>?<3F>嗆<EFBFBD><E59786><EFBFBD>銋<EFBFBD><E98A8B>嚗<EFBFBD>㺭<EFBFBD>桐<EFBFBD>銝W仃嚗?<3F>?瘞游像<E6B8B8>拙<EFBFBD>嚗<EFBFBD><E59A97>摰硺<E691B0><E7A1BA>讛<EFBFBD>嚗?
|
||
Postgres-Only摰<79><E691B0>皛∟雲嚗?<3F>?<3F>嗆<EFBFBD><E59786><EFBFBD><EFBFBD>典銁RDS嚗<53><E59A97>蝵殷<E89DB5>
|
||
<EFBFBD>?隞餃𦛚<E9A483><F0A69B9A><EFBFBD><EFBFBD>吔<EFBFBD>銝滢腺憭梧<E686AD>
|
||
<EFBFBD>?pg-boss<73>舀<EFBFBD>憭𡁜<E686AD>靘页<E99D98>SKIP LOCKED嚗?
|
||
Redis<EFBFBD>芣糓摰䂿緵<EFBFBD>孵<EFBFBD>銋衤<EFBFBD>嚗䔶<EFBFBD><EFBFBD>臬𣈲銝<EFBFBD><EFBFBD>孵<EFBFBD><EFBFBD>?```
|
||
|
||
---
|
||
|
||
## **9\. 蝏栞祗**
|
||
|
||
撖嫣<EFBFBD> 1-2 鈭箄<E988AD>璅∠<E79285>蝎曄<E89D8E><E69B84>𥕢<EFBFBD><F0A595A2>a<EFBFBD>嚗?*<2A><><EFBFBD>舀<EFBFBD><E88880><EFBFBD><EFBFBD>蝻抬<E89DBB>Stack Collapse嚗㗇糓<E39787>滢<EFBFBD><E6BBA2>萄<EFBFBD><E89084><EFBFBD><EFBFBD>雿單<E99BBF>畾?*<2A>?
|
||
<EFBFBD>㗇𥋘 Postgres-Only 銝齿糓<E9BDBF>牐蛹<E78990>睲賑<E79DB2><E8B391><EFBFBD>航氜<E888AA>𠬍<EFBFBD><F0A0AC8D>峕糓<E5B395>牐蛹<E78990>睲賑撖寞<E69296><E5AF9E>舀<EFBFBD><E88880><EFBFBD><EFBFBD>湔楛<E6B994>餌<EFBFBD><E9A48C><EFBFBD>圾嚗?
|
||
- <20>睲賑<E79DB2><E8B391>圾**撟嗅<E6929F><E59785><EFBFBD><EFBFBD>摰噼<E691B0>璅?*嚗<><E59A97><EFBFBD>航<EFBFBD><E888AA>喟<EFBFBD><E5969F>曆<EFBFBD>QPS嚗?- <20>睲賑<E79DB2><E8B391>圾**Postgres<65><73><EFBFBD><EFBFBD>𥡝器<F0A5A19D>?*嚗<><E59A97><EFBFBD>臬㫲鞊∩葉<E288A9>?<3F>?嚗?- <20>睲賑<E79DB2><E8B391>圾**<2A>嗆<EFBFBD><E59786><EFBFBD>瓲敹<E793B2>𤌍<EFBFBD>?*嚗<>迅摰𡁏<E691B0>?> <20>急<EFBFBD>嚗?- <20>睲賑<E79DB2><E8B391>圾**<2A>a<EFBFBD><EFBD81><EFBFBD><EFBFBD>摰噼<E691B0><E599BC>?*嚗<><E59A97>蝏渲<E89D8F><E6B8B2>?= 蝔喳<E89D94><E596B3>改<EFBFBD>
|
||
|
||
<EFBFBD>睲賑<EFBFBD>㗇𥋘<EFBFBD>?*<2A>嗆<EFBFBD><E59786><EFBFBD><EFBFBD>瘣<EFBFBD><E798A3>?*<2A>交揢<E4BAA4>?*餈鞟輕<E99E9F><E8BC95>迅摰𡁏<E691B0>?*嚗𣬚鍂**<2A>∪<EFBFBD><E288AA><EFBFBD>ế<EFBFBD>?*<2A>交揢<E4BAA4>?*銝𡁜𦛚<F0A1819C><F0A69B9A>翰<EFBFBD>蠘翮隞?*<2A>?
|
||
**餈嗘<E9A488><E59798>臬戎<E887AC>𧶏<EFBFBD>餈蹱糓<E8B9B1>箸<EFBFBD><E7AEB8>?* |