Prisma 多重创建查询嵌套 connectOrCreate 抛出唯一约束失败

Prisma multiple create queries with nested connectOrCreate throws unique constraint failed

我正在尝试创建一个模型,其中电影和流派这两种类型之间存在多对多关系。 我从外部数据源异步检索电影列表,create 每个电影都在我自己的数据库中(如果尚未创建),return 电影。

const retrieveList = async () => {
   const results = await API.getMovies();
   return results.map((item) => getMovieItem(item.id));
}

const getMovieItem = async (movieId) => {
    // see if is already in database
    const movie = await prisma.movie.findUnique({
        where: { tmdbId: movieId },
    });
    if (movie) {
        return movie;
    } else {
        // create and return if doesn't exist
        const details = await API.getDetails(movieId);
        const genresData = details.genres.map((genre) => ({
            create: {
                name: genre.name,
            },
            where: {
                name: genre.name
            },
        }));
        return prisma.movie.create({
            data: {
                title: details.title,
                description: details.overview,
                genres: {
                    connectOrCreate: genresData,
                },
            },
            select: {
                tmdbId: true,
                id: true,
                title: true,
                description: true,
                //...
            },
        });
    }
};

问题是似乎存在某种不一致,如果在某些运行中与外部 API 的连接足够慢,一切似乎都运行良好;但如果它足够快,它也会抛出错误:

Invalid `prisma.movie.create()` invocation:
  Unique constraint failed on the fields: (`name`)"

可能是因为它正在尝试创建一个已经创建的 流派,而不是连接它。

作为the docs states:

Multiple connectOrCreate queries that run as concurrent transactions can result in a race condition. ...

因此,如果您同时请求同一类型的 2 部电影,而该类型尚不存在,那么两个查询都会尝试创建该类型,只有第一个会成功。

文档建议捕获特定错误:

To work around this scenario, we recommend catching the unique violation exception (PrismaClientKnownRequestError, error P2002) and retrying failed queries.

也可以先创建所有流派,例如:

// Some abstract function which extracts all the genres
const genres = gatherGenres(results);

await prisma.genres.createMany({
    data: genres,
    // Do nothing on duplicates
    skipDuplicates: true,
});

然后创建所有电影并将它们与流派联系起来,因为流派已经创建。

此外,小吹毛求疵,你的函数 retrieveList 是不可等待的,因为它 returns 一个 promises 数组,要真正等到所有 promises 完成你需要使用 Promise.all 在上面

const retrieveList = async () => {
   const results = await API.getMovies();
   return results.map((item) => getMovieItem(item.id));
}

// ...

// You can just await it
await retrieveList()

// Need to use Promise.all
await Promise.all(retrieveList())