如何在TypeScript/JavaScript中测试并发数据库交互及幂等性代码?
TypeScript并发与幂等性测试实现方案
核心实现思路
1. 模拟CyclicBarrier同步机制
JavaScript/TypeScript基于单线程事件循环,原生没有多线程屏障,但可以用Promise结合共享状态实现类似功能:
class CyclicBarrier { private count: number; private parties: number; private resolveList: (() => void)[] = []; constructor(parties: number) { this.parties = parties; this.count = parties; } await(): Promise<void> { this.count--; if (this.count === 0) { this.count = this.parties; this.resolveList.forEach(resolve => resolve()); this.resolveList = []; return Promise.resolve(); } return new Promise(resolve => this.resolveList.push(resolve)); } }
使用示例:
async function testConcurrentUpdate() { const barrier = new CyclicBarrier(2); const task1 = async () => { // 前置准备:获取资源初始状态 await barrier.await(); // 等待另一个任务就绪 // 执行数据库更新 await updateResource('resource-id', { value: 1 }); }; const task2 = async () => { // 同样的前置准备 await barrier.await(); // 执行数据库更新 await updateResource('resource-id', { value: 1 }); }; // 同时启动两个任务 await Promise.all([task1(), task2()]); // 验证幂等性结果 const finalState = await getResource('resource-id'); expect(finalState.value).toBe(1); // 预期幂等操作仅生效一次 }
2. 用Worker Threads实现真实并行
如果需要模拟Node.js环境下的多线程并发(比如数据库连接竞争),可以使用worker_threads模块:
- 主线程创建多个Worker,通过消息传递实现同步
- 所有Worker准备就绪后,同时触发数据库操作
主线程代码:
import { Worker } from 'worker_threads'; async function testWithWorkers() { const workerCount = 3; const workers = []; const readyPromises: Promise<void>[] = []; for (let i = 0; i < workerCount; i++) { const worker = new Worker('./worker-script.ts'); workers.push(worker); readyPromises.push(new Promise(resolve => { worker.on('message', (msg) => { if (msg.type === 'ready') resolve(); }); })); } // 等待所有Worker准备完成 await Promise.all(readyPromises); // 同步发送开始指令 workers.forEach(worker => worker.postMessage({ type: 'start' })); // 等待所有Worker执行结束 const finishPromises = workers.map(worker => new Promise(resolve => worker.on('message', msg => msg.type === 'finish' && resolve())) ); await Promise.all(finishPromises); // 验证最终状态 const finalState = await getResource('resource-id'); expect(finalState.version).toBe(1); // 假设乐观锁版本仅递增一次 }
Worker线程代码:
import { parentPort } from 'worker_threads'; import { updateResource } from './db-utils'; parentPort?.on('message', async (msg) => { if (msg.type === 'start') { await updateResource('resource-id', { value: 1 }); parentPort?.postMessage({ type: 'finish' }); } }); // 通知主线程已准备就绪 parentPort?.postMessage({ type: 'ready' });
3. 提升测试有效性的技巧
- 重复执行测试:由于事件循环的不确定性,单次测试可能无法触发竞争,可循环执行10-100次
- 增加并发量:用10+个任务/Worker提高竞争概率
- 插入随机延迟:在关键步骤(如获取资源后、更新前)加入
await new Promise(resolve => setTimeout(resolve, Math.random() * 50)),模拟真实网络延迟
参考资源
- 《Node.js设计模式》:深入讲解Node.js并发模型与异步模式,包含并发测试实践
- Node.js官方文档:
worker_threads模块详细说明,理解多线程并行实现细节 - 社区实践:搜索"TypeScript并发测试" "幂等性验证"相关案例,参考一线开发者的落地方案
内容的提问来源于stack exchange,提问作者Augusto
相关产品推荐
相关产品推荐

