porsager / postgres

Postgres.js - The Fastest full featured PostgreSQL client for Node.js, Deno, Bun and CloudFlare
The Unlicense
7.54k stars 273 forks source link

Concurrent calls to notify in transaction results in deadlock, but pg_notify works #966

Open samczsun opened 1 month ago

samczsun commented 1 month ago

PoC

  const sql = postgres({ ssl: "require", max: 10 });

  const promises = [];
  for (let i = 0; i < 10; i++) {
    promises.push((async () => {
      console.log(`beginning thread ${i}`)
      await sql.begin(async (sql) => {
        console.log(`notifying ${i}`)

        await sql.notify('test_channel', i.toString());

        console.log(`done ${i}`)
      })
      console.log(`exiting thread ${i}`)
    })());
  }

  await Promise.all(promises);

  console.log('done promises');

  await sql.end();

Output:

beginning thread 0
beginning thread 1
beginning thread 2
beginning thread 3
beginning thread 4
beginning thread 5
beginning thread 6
beginning thread 7
beginning thread 8
beginning thread 9
notifying 6
notifying 7
notifying 4
notifying 3
notifying 1
notifying 9
notifying 8
notifying 0
notifying 5
notifying 2
^C

Changing max=11 results in this

beginning thread 0
beginning thread 1
beginning thread 2
beginning thread 3
beginning thread 4
beginning thread 5
beginning thread 6
beginning thread 7
beginning thread 8
beginning thread 9
notifying 4
notifying 3
notifying 9
notifying 1
notifying 2
notifying 8
notifying 0
notifying 5
notifying 7
notifying 6
done 4
exiting thread 4
done 3
done 9
done 1
done 2
done 8
exiting thread 1
exiting thread 3
exiting thread 8
done 5
done 7
done 6
exiting thread 2
exiting thread 9
done 0
exiting thread 5
exiting thread 0
exiting thread 6
exiting thread 7
done promises

Interestingly, replacing with await sql`select pg_notify('test_channel', ${i})`; results in no deadlock