为什么在使用 pg-promise 的事务中不能解锁咨询锁?
Why can't advisory locks be unlocked while in a transaction with pg-promise?
我 运行 浏览了以下代码并尝试对其进行调试,但我不明白为什么在这种情况下无法解锁锁:
const db = pgp(opts)
await db.one('SELECT pg_try_advisory_lock(1) AS lock') // returns true, and I can see the lock in the DB
// no await
db.tx(async t => {
const fingerprintIds = fingerprints.map(item => item.id)
sql = `UPDATE fingerprint SET blah=1 WHERE id IN ($(fingerprintIds:list)) RETURNING id`
const updatedFingerprintIds = await db.query(sql, { fingerprintIds }) // yes, it is not using the transaction object
// some other database calls
})
result = await db.one('SELECT pg_advisory_unlock(1) AS lock')
// result.lock === false, database logs the error: 'WARNING: you don't own a lock of type ExclusiveLock'
// lock still stays in the database (until the session ends)
当然,当我在 db.tx
调用前面添加 await
并在 t运行saction 回调中使用 t
时,它的工作方式如下预期。
发生这种情况是否是因为调用 unlock
时 pg-promise 库处于 t运行sactional 状态?因为当我调用这一系列查询时,锁会按预期解锁:
SELECT pg_try_advisory_lock(1) AS lock;
BEGIN;
SELECT * FROM table;
SELECT pg_advisory_unlock(1) AS lock;
这些锁定操作是当前连接会话独有的,因此它们必须在同一个事务块中执行:
await db.tx(async t => {
await t.func('pg_try_advisory_lock', [1]);
// execute all queries here, against 't' context;
// ...
await t.func('pg_advisory_unlock', [1]);
});
- 我们在这里使用方法func只是为了缩短语法。
然而,如果交易失败,那将不会执行解锁,所以如果这是一个问题,那么您将需要手动捕获交易中的错误:
await db.tx(async t => {
await t.func('pg_try_advisory_lock', [1]);
let result, err;
try {
// execute all queries here, against 't' context;
// and set the 'result'...
} catch(e) {
err = e;
}
await t.func('pg_advisory_unlock', [1]);
if(e) {
throw e;
}
return result;
});
更新
I’m using an advisory lock because there are a lot of other things (non-idempotent, non-database) that happen outside of this one database transaction, and since there are multiple instances of the app running, the lock is to prevent other instances from doing the same work at the same time
我不确定这种类型的锁如何帮助跨多个数据库甚至多个应用程序,因为这些锁只能在同一会话中使用。
但是如果您确实需要在事务之外执行这些锁,并且针对同一个连接,那么您将需要手动管理连接:
// Somewhere in the beginning of your chain logic,
// you allocate a manually-managed connection:
const dbm = await db.connect();
// Then your lock happens:
await dbm.func('pg_try_advisory_lock', [1]);
// Then you transaction happens...
await dbm.tx(async t => {
// transaction queries
});
// Then your unlock query:
await dbm.func('pg_advisory_unlock', [1]);
// Then you release the connection:
dbm.done();
你只需要注意这一点,不要泄漏连接 ;)
- 参见方法 connect。
我 运行 浏览了以下代码并尝试对其进行调试,但我不明白为什么在这种情况下无法解锁锁:
const db = pgp(opts)
await db.one('SELECT pg_try_advisory_lock(1) AS lock') // returns true, and I can see the lock in the DB
// no await
db.tx(async t => {
const fingerprintIds = fingerprints.map(item => item.id)
sql = `UPDATE fingerprint SET blah=1 WHERE id IN ($(fingerprintIds:list)) RETURNING id`
const updatedFingerprintIds = await db.query(sql, { fingerprintIds }) // yes, it is not using the transaction object
// some other database calls
})
result = await db.one('SELECT pg_advisory_unlock(1) AS lock')
// result.lock === false, database logs the error: 'WARNING: you don't own a lock of type ExclusiveLock'
// lock still stays in the database (until the session ends)
当然,当我在 db.tx
调用前面添加 await
并在 t运行saction 回调中使用 t
时,它的工作方式如下预期。
发生这种情况是否是因为调用 unlock
时 pg-promise 库处于 t运行sactional 状态?因为当我调用这一系列查询时,锁会按预期解锁:
SELECT pg_try_advisory_lock(1) AS lock;
BEGIN;
SELECT * FROM table;
SELECT pg_advisory_unlock(1) AS lock;
这些锁定操作是当前连接会话独有的,因此它们必须在同一个事务块中执行:
await db.tx(async t => {
await t.func('pg_try_advisory_lock', [1]);
// execute all queries here, against 't' context;
// ...
await t.func('pg_advisory_unlock', [1]);
});
- 我们在这里使用方法func只是为了缩短语法。
然而,如果交易失败,那将不会执行解锁,所以如果这是一个问题,那么您将需要手动捕获交易中的错误:
await db.tx(async t => {
await t.func('pg_try_advisory_lock', [1]);
let result, err;
try {
// execute all queries here, against 't' context;
// and set the 'result'...
} catch(e) {
err = e;
}
await t.func('pg_advisory_unlock', [1]);
if(e) {
throw e;
}
return result;
});
更新
I’m using an advisory lock because there are a lot of other things (non-idempotent, non-database) that happen outside of this one database transaction, and since there are multiple instances of the app running, the lock is to prevent other instances from doing the same work at the same time
我不确定这种类型的锁如何帮助跨多个数据库甚至多个应用程序,因为这些锁只能在同一会话中使用。
但是如果您确实需要在事务之外执行这些锁,并且针对同一个连接,那么您将需要手动管理连接:
// Somewhere in the beginning of your chain logic,
// you allocate a manually-managed connection:
const dbm = await db.connect();
// Then your lock happens:
await dbm.func('pg_try_advisory_lock', [1]);
// Then you transaction happens...
await dbm.tx(async t => {
// transaction queries
});
// Then your unlock query:
await dbm.func('pg_advisory_unlock', [1]);
// Then you release the connection:
dbm.done();
你只需要注意这一点,不要泄漏连接 ;)
- 参见方法 connect。