我有一个SELECT FOR UPDATE带锁的程序。我想同时测试它,以确保锁确实存在。
我正在使用这个:
CREATE TABLE IF NOT EXISTS person (
name varchar primary key
);
INSERT INTO person VALUES ('john');
CREATE TABLE IF NOT EXISTS tickets (
name varchar PRIMARY KEY REFERENCES person,
amount integer NOT NULL
);
CREATE OR REPLACE PROCEDURE sp (_name varchar, _amount integer) AS
$$
BEGIN
-- acquire a lock on person row
PERFORM name FROM person WHERE name = _name FOR UPDATE;
INSERT INTO tickets VALUES(_name, _amount);
END
$$ LANGUAGE plpgsql;
这是我目前可以提供的漂亮转储示例,但它表明必须获取锁才能对sp调用进行排队。
func TestInsert(t *testing.T) {
tx, err := db.Begin() // Read Committed level tx
defer tx.Rollback()
insertPersonFixtures(tx) // Using this tx to fill database with test data needed by testing SP
ready1 := make(chan struct{})
ready2 := make(chan struct{})
done := make(chan struct{})
go func() {
// Must see `prepareSomeData` data in database??
tx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelReadUncommitted})
defer tx.Rollback()
tx.Exec("CALL sp('john', 10)")
ready1 <- struct{}{}
<-ready2
done <- struct{}{}
}()
go func() {
<-ready1
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// Must see `prepareSomeData` data in database??
tx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelReadUncommitted})
defer tx.Rollback()
tx.ExecContext(ctx, "CALL sp(`john`, 20)")
if err == nil {
t.Error("No lock", err)
}
ready2 <- struct{}{}
}()
<-done
}
另外,我希望 2 个 goroutine 可以看到在第一个事务中填充的测试数据,但是sp()因为看不到数据而失败,这很奇怪,因为未提交的读取级别意味着它可以看到脏数据(按insertPersonFixtures)。
@Brits 的一个:
SQL 标准定义了一个附加级别,READ UNCOMMITTED。在 PostgreSQL 中,READ UNCOMMITTED 被视为 READ COMMITTED。
这段代码有什么问题,或者以这种方式测试 RDBMS 锁可能是一种不好的方法?还是我误解了隔离级别?在我的示例中,我希望第二个 goroutine 超时并发出存在锁的信号。
每次测试后有没有一种简单的方法来清理数据库?我不确定为每个表运行 truncate 是否容易。显然,在同等读取已提交和未提交以及缺乏适当的嵌套事务之后,这一切都变得一团糟。
慕桂英546537
相关分类