我的一个领域存在唯一约束问题。我正在向数据库添加记录,以便能够通过测试检查我的代码是否按预期工作。表字段之一是从外部提供的唯一编号(它与同一数据库中的其他表无关),我需要为每个测试生成这个唯一编号,但我遇到了唯一约束问题。我有以下功能:
export const findMinUniqueUserId = async (): Promise<number> => {
const subscriptions = await prisma.$queryRaw<Subscription[]>(`
SELECT "userId"
FROM public."Subscriptions"
ORDER BY "userId" DESC
LIMIT 1
`);
const firstFreeUserId = (subscriptions[0]?.userId || 0) + 1;
return firstFreeUserId;
};
返回第一个最小免费“userId”字段。我还有以下测试:
describe("Test 1", () => {
it("should do something", async () => {
const draftSub = {
userId: await findMinUniqueUserId()
...some other fields
}
await prisma.subscription.create({
data: draftSub
})
...some other test stuff
})
})
第二个:
describe("Test 2", () => {
it("should do something", async () => {
const draftSub = {
userId: await findMinUniqueUserId()
...some other fields
}
await prisma.subscription.create({
data: draftSub
})
...some other test stuff
})
})
有时我会收到一个错误:
Unique constraint failed on the fields: (`userId`)
我听说每个测试套件(描述块)都在单独的工作线程上工作,我试图准备某种单例类,这可以帮助我,但我认为类的每个实例都在单独的工作线程中创建,因此生成userId
不是唯一的。
这就是我在单例类中尝试的:
export class UserIdManager {
private static instance: UserIdManager
private static userIdShiftBeforeDatabaseCall = 0
private static minFreeUserIdAfterDatabaseCall = 0
private constructor() {
return;
}
private static async init() {
this.minFreeUserIdAfterDatabaseCall = await findMinUniqueUserId();
}
public static async reserveMinFreeUserId() {
let minFreeUserId = UserIdManager.userIdShiftBeforeDatabaseCall;
UserIdManager.userIdShiftBeforeDatabaseCall++;
if (!UserIdManager.instance) {
UserIdManager.instance = new UserIdManager();
await this.init();
}
minFreeUserId += UserIdManager.minFreeUserIdAfterDatabaseCall;
return minFreeUserId;
}
}
但我意识到它对多线程没有帮助。我用过这个,但结果相同:
....
const draftSub = {
userId: await UserIdManager.reserveMinFreeUserId()
...some other fields
}
....
因此,问题是如何为每个测试生成唯一编号。当我将 --runInBand 选项传递给jest
一切正常时,但需要更多时间。
您使用的是分配唯一值的典型MAX()+1方法。不幸的是,这是一个虚拟的保证,您会为您的独特价值获得重复的价值。这是Postgres的多版本并发控制 ( MVCC ) 特性的结果。在 MVCC 数据库中,在第一个会话提交之前,另一个会话无法看到一个会话采取的操作。因此,当多个会话访问max()+1 时,它们每个都会得到相同的结果。第一个提交成功,第二个失败。解决方案是创建一个序列并让 Postgres 分配唯一值,无论有多少会话同时访问序列,它都不会分配相同的值两次。然而成本是你的价值观将包含差距- 接受它,克服它,然后继续前进。您可以通过将您的用户 ID 定义为生成的身份(Postgres10 或更高版本)或为旧版本的序列号来生成序列。
create table subscriptions ( id generated always as identity ...) -- for versions Postgres 10 or later
or
create table subscriptions ( id serial ...) -- for versions prior to Postgers 10
使用其中任何一个就可以摆脱您的 findMinUniqueUserId 函数。您可能还想研究插入...返回...功能
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句