Prisma-client-js: "Prisma Query Engine has a panic" on upsert

Created on 10 Jul 2020  路  3Comments  路  Source: prisma/prisma-client-js

Hello! My project is very simple, I'm just learning TypeScript. I have only 1 table to which I'm storing crypto coins from GeckoCoin's API.

Upsert works with one testing call via rest api but fails via other. I'll explain.

Issue Reproduction

  • empty database
  • run server
  • after startup app.listener runs coinService.startupUpdate() which fails
  • you can comment this line (app.tx:85) and try to sync coins via query parameter (ie /get-coins?updateCoins=true) which fails
  • BUT if you run /update-coins which works almost the same, coins are properly stored and returned

Logs

Error: 
Invalid `prisma.coin.upsert()` invocation in
/home/vergil333/NodeJSProjects/GraphQL-Demo/src/coin-repository.ts:59:23


  Started http server on 127.0.0.1:42703

This is a non-recoverable error which probably happens when the Prisma Query Engine has a panic.

Versions

| Name | Version |
|----------|--------------------|
| Node | v10.19.0 |
| OS | debian-openssl-1.1.x|
| Prisma | 2.1.3 |

Description

Started http server on 127.0.0.1:42703

GitHub repo

https://github.com/Vergil333/GraphQL-Demo

bu0-needs-info kinbug

Most helpful comment

Hey,

I checked your code. After transforming few imports and non existant ErrorMsg object in app.ts I was able to run it. Looks like you are not properly waiting over async code. The javascript array doesn't actually pause on the callback function as the internal code won't await on the promise that was returned by that async function. Also, map doesn't modify the source array so Promise.all call is of no use.

So you had this function in your code which is problematic:

async function updateCoinsFromCG(): Promise<CGCoin[]> {
    const coins: CGCoin[] = await cgClient.getAllCoins()
    coins.map(async (coin) => {
        await repository.updateOrCreateCoin(coin)
    })

    return Promise.all(coins)
        .finally(() => repository.disconnect())
}

Instead you should do this:

async function updateCoinsFromCG(): Promise<Coin[]> {
  const coins: CGCoin[] = await cgClient.getAllCoins();

  const coinPromises = coins.map((coin) => {
    return repository.updateOrCreateCoin(coin);
  });

  return Promise.all(coinPromises).finally(() => repository.disconnect());
}

This function that I would map over the coins and turns them into a promise. Since map doesn't modify the original array, we store the mapped result into a coinPromises variable. Now we wait over all of them via Promise.all.

I have changed the return type of the function from CGCoin to Coin so that we can match the type returned by updateOrCreateCoin. Also, I have modified updateOrCreateCoin so that it returns the updated value instead of void like so:

async function updateOrCreateCoin(coin: CGCoin): Promise<Coin> {
  try {
    let result = await prisma.coin.upsert({
      where: { cg_id: coin.id },
      update: {
        cg_id: coin.id,
        name: coin.name,
        symbol: coin.symbol,
      },
      create: {
        cg_id: coin.id,
        name: coin.name,
        symbol: coin.symbol,
      },
    });

    return result;
  } catch (e) {
    console.error("Upsert error: ", e);
    throw e;
  }
}

I have made a PR so that you can see the changes at a glance: https://github.com/Vergil333/GraphQL-Demo/pull/1

Now after these changes there is no panic and I can see all the records in the database:
image
image

All 3 comments

Hey,

I checked your code. After transforming few imports and non existant ErrorMsg object in app.ts I was able to run it. Looks like you are not properly waiting over async code. The javascript array doesn't actually pause on the callback function as the internal code won't await on the promise that was returned by that async function. Also, map doesn't modify the source array so Promise.all call is of no use.

So you had this function in your code which is problematic:

async function updateCoinsFromCG(): Promise<CGCoin[]> {
    const coins: CGCoin[] = await cgClient.getAllCoins()
    coins.map(async (coin) => {
        await repository.updateOrCreateCoin(coin)
    })

    return Promise.all(coins)
        .finally(() => repository.disconnect())
}

Instead you should do this:

async function updateCoinsFromCG(): Promise<Coin[]> {
  const coins: CGCoin[] = await cgClient.getAllCoins();

  const coinPromises = coins.map((coin) => {
    return repository.updateOrCreateCoin(coin);
  });

  return Promise.all(coinPromises).finally(() => repository.disconnect());
}

This function that I would map over the coins and turns them into a promise. Since map doesn't modify the original array, we store the mapped result into a coinPromises variable. Now we wait over all of them via Promise.all.

I have changed the return type of the function from CGCoin to Coin so that we can match the type returned by updateOrCreateCoin. Also, I have modified updateOrCreateCoin so that it returns the updated value instead of void like so:

async function updateOrCreateCoin(coin: CGCoin): Promise<Coin> {
  try {
    let result = await prisma.coin.upsert({
      where: { cg_id: coin.id },
      update: {
        cg_id: coin.id,
        name: coin.name,
        symbol: coin.symbol,
      },
      create: {
        cg_id: coin.id,
        name: coin.name,
        symbol: coin.symbol,
      },
    });

    return result;
  } catch (e) {
    console.error("Upsert error: ", e);
    throw e;
  }
}

I have made a PR so that you can see the changes at a glance: https://github.com/Vergil333/GraphQL-Demo/pull/1

Now after these changes there is no panic and I can see all the records in the database:
image
image

I have also seen this wrong pattern in many other places in your code which you should fix.

@pantharshit00 that's a better answer that I was hoping for. Your explanation to the issue is very helpful and educational. Very much appreciated.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

divyenduz picture divyenduz  路  3Comments

julien1619 picture julien1619  路  3Comments

FluorescentHallucinogen picture FluorescentHallucinogen  路  3Comments

esistgut picture esistgut  路  4Comments

samrith-s picture samrith-s  路  3Comments