简体   繁体   English

具有嵌套 connectOrCreate 的 Prisma 多个创建查询抛出唯一约束失败

[英]Prisma multiple create queries with nested connectOrCreate throws unique constraint failed

I'm trying to create a model where there's a many-to-many relation between 2 types, Movie and Genre.我正在尝试创建一个模型,其中电影和流派这两种类型之间存在多对多关系。 I retrieve a list of movies from an external data source asynchronously, create each in my own database if it's not already created, and return the movie.我从外部数据源异步检索电影列表,如果尚未创建,则在我自己的数据库中create每个电影,然后返回电影。

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,
                //...
            },
        });
    }
};

the problem is that there seems to be some sort of inconsistency where if the connection to the external API is slow enough in some runs, everything seems to be working fine;问题是似乎存在某种不一致,如果在某些运行中与外部 API 的连接足够慢,那么一切似乎都运行良好; but also if it's fast enough, it throws the error:但如果它足够快,它也会抛出错误:

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

likely because it is trying to create a genre that's already created instead of connecting it.可能是因为它试图创建一种已经创建的类型,而不是连接它。

As the docs states :正如文档所述

Multiple connectOrCreate queries that run as concurrent transactions can result in a race condition.作为并发事务运行的多个connectOrCreate查询可能会导致竞争条件。 ... ...

So if you request 2 movies of the same genre at the same time and this genre is not yet exist then both queries would try to create this genre and only the first one will succeed.因此,如果您同时请求 2 部相同类型的电影并且该类型尚不存在,则两个查询都会尝试创建该类型,并且只有第一个会成功。

Docs recommends catching for specific error: Docs 建议捕获特定错误:

To work around this scenario, we recommend catching the unique violation exception ( PrismaClientKnownRequestError , error P2002 ) and retrying failed queries.要解决这种情况,我们建议捕获唯一的违规异常( PrismaClientKnownRequestError ,错误P2002 )并重试失败的查询。

You can also create all the genres at first, for example:您也可以先创建所有流派,例如:

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

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

And then create all the movies and connect them to genres, because genres have already been created.然后创建所有电影并将它们与流派联系起来,因为流派已经被创建了。

Also, small nitpick, you function retrieveList is not awaitable because it returns an array of promises, and to actually wait until all the promises are done you need to use Promise.all on it此外,小的 nitpick,你的函数retrieveList是不可等待的,因为它返回一个承诺数组,实际上要等到所有承诺都完成,你需要在它上面使用Promise.all

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

// ...

// You can't just await it ❌
await retrieveList()

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM