mangoweb / pg-deadlock-playground

v0.1.1 2017-02-27 16:34 UTC

This package is auto-updated.

Last update: 2024-08-29 04:31:16 UTC


README

一个简单的工具,用于与多个并发 PostgreSQL 事务进行交互,并测试它们在什么情况下可能导致死锁。验证器类可以自动测试给定场景的所有可能的顺序。

安装

composer require mangoweb/pg-deadlock-playground

文档

创建场景

场景是一个步骤的有序序列。每个步骤都定义为一个连接 ID 和查询的元组。

$scenario = new Mangoweb\PgDeadlockPlayground\Scenario();
$scenario->addStep(0, 'SELECT 123');     // first execute SELECT 123 on connection #0
$scenario->addStep(1, 'SELECT 456');     // then execute SELECT 456 on connection #1
$scenario->addStep(1, 'SELECT 789');     // then execute SELECT 789 on connection #1
$scenario->addStep(2, 'SELECT \'abc\''); // then execute SELECT 'abc' on connection #2
$scenario->addStep(0, 'SELECT NOW()');   // then execute SELECT NOW on connection #0

在任何时刻,您都可以使用 $scenario->dump() 调用转储场景。在这种情况下,它将打印

SELECT 123
               SELECT 456
               SELECT 789
                            SELECT 'abc'
SELECT NOW()

或者,您也可以使用 Scenario::fromArray 创建相同的场景

$scenario = Mangoweb\PgDeadlockPlayground\Scenario::fromArray([
    ['SELECT 123',   NULL,        NULL            ],
    [NULL,          'SELECT 456', NULL            ],
    [NULL,          'SELECT 789', NULL            ],
    [NULL,           NULL,        'SELECT \'abc\''],
    ['SELECT NOW()', NULL,        NULL            ],
]);

生成所有场景步骤顺序

要获取给定场景的所有可能的步骤顺序,请调用 $scenario->getAllOrderings()。例如,以下代码

$scenario = Mangoweb\PgDeadlockPlayground\Scenario::fromArray([
	['SELECT 123',   NULL       ],
	[NULL,          'SELECT 456'],
	[NULL,          'SELECT 789'],
]);

foreach ($scenario->getAllOrderings() as $scenarioVariant) {
    $scenarioVariant->dump();
}

将输出

SELECT 123
             SELECT 456
             SELECT 789
-----------------------
             SELECT 456
SELECT 123
             SELECT 789
-----------------------
             SELECT 456
             SELECT 789
SELECT 123

执行场景

要执行场景,您需要一个 ScenarioExecutor 实例。

$executor = Mangoweb\PgDeadlockPlayground\ScenarioExecutor::create([
    'dbname' => 'deadlock_playground',
    'user' => 'postgres',
    'password' => '',
]);

调用 $executor->execute($scenario) 将始终返回一个 ScenarioExecutionResult 实例。您可以通过调用 $result->dump() 来检查结果。

$result = $executor->execute($scenario);
$result->dump();

理解结果

$result->dump() 的输出类似于 $scenario->dump(),但每个步骤都带有重要的标签。

例子

用法

$config = [
    'host' => '127.0.0.1',
    'dbname' => 'deadlock_playground',
    'user' => 'postgres',
    'password' => '',
];

$initQueries = [
    'DROP TABLE IF EXISTS users',
    'CREATE TABLE users (id INTEGER NOT NULL, name TEXT NOT NULL)',
    'INSERT INTO users VALUES (1, \'Logan\')'
];

$executor = Mangoweb\PgDeadlockPlayground\ScenarioExecutor::create($config, $initQueries);
$verifier = new Mangoweb\PgDeadlockPlayground\ScenarioExpectationVerifier($executor);
$verifier->setVerbose();

$verifier->expectAlwaysOk(
    Scenario::fromArray([
        ['START TRANSACTION ISOLATION LEVEL REPEATABLE READ',   NULL],
        ['LOCK users IN SHARE MODE',                            NULL],
        ['SELECT * FROM users WHERE id = 1 FOR UPDATE',         NULL],
        ['COMMIT',                                              NULL],
        [NULL,                                                  'START TRANSACTION ISOLATION LEVEL REPEATABLE READ'],
        [NULL,                                                  'UPDATE users SET name = \'John\' WHERE id = 1'],
        [NULL,                                                  'COMMIT'],
    ])
);

输出

...
SUCCESS: completed without error
in C:\Projects\deadlock-playground\examples\readme.php:25

   [OK]         START TRANSACTION ISOLATION LEVEL REPEATABLE READ
   [OK]         LOCK users IN SHARE MODE
   [OK]         SELECT * FROM users WHERE id = 1 FOR UPDATE
                                                                    [OK]         START TRANSACTION ISOLATION LEVEL REPEATABLE READ
   [OK]         COMMIT
                                                                    [OK]         UPDATE users SET name = 'John' WHERE id = 1
                                                                    [OK]         COMMIT
----------------------------------------------------------------------------------------------------------------------------------

SUCCESS: completed without error
in C:\Projects\deadlock-playground\examples\readme.php:25

   [OK]         START TRANSACTION ISOLATION LEVEL REPEATABLE READ
   [OK]         LOCK users IN SHARE MODE
   [OK]         SELECT * FROM users WHERE id = 1 FOR UPDATE
                                                                    [OK]         START TRANSACTION ISOLATION LEVEL REPEATABLE READ
                                                                    [WAITING...] UPDATE users SET name = 'John' WHERE id = 1
   [OK]         COMMIT
                                                                    [...SUCCESS]
                                                                    [OK]         COMMIT
...