public function provideTestHighContention()
{
$cases = array_map(function (array $mutexFactory) {
$file = tmpfile();
fputs($file, pack("i", 0));
fflush($file);
return [function ($increment) use($file) {
fseek($file, 0);
$data = fread($file, 4);
$counter = unpack("i", $data)[1];
$counter += $increment;
fseek($file, 0);
fwrite($file, pack("i", $counter));
fflush($file);
return $counter;
}, $mutexFactory[0]];
}, $this->provideMutexFactories());
$addPDO = function ($dsn, $user, $vendor) use(&$cases) {
$pdo = $this->getPDO($dsn, $user);
$pdo->beginTransaction();
$options = ["mysql" => "engine=InnoDB"];
$option = isset($options[$vendor]) ? $options[$vendor] : "";
$pdo->exec("CREATE TABLE IF NOT EXISTS counter(id INT PRIMARY KEY, counter INT) {$option}");
$pdo->exec("DELETE FROM counter");
$pdo->exec("INSERT INTO counter VALUES (1, 0)");
$pdo->commit();
$this->pdo = null;
$cases[$vendor] = [function ($increment) use($dsn, $user) {
// This prevents using a closed connection from a child.
if ($increment == 0) {
$this->pdo = null;
}
$pdo = $this->getPDO($dsn, $user);
$id = 1;
$select = $pdo->prepare("SELECT counter FROM counter WHERE id = ? FOR UPDATE");
$select->execute([$id]);
$counter = $select->fetchColumn();
$counter += $increment;
$pdo->prepare("UPDATE counter SET counter = ? WHERE id = ?")->execute([$counter, $id]);
return $counter;
}, function ($timeout = 3) use($dsn, $user) {
$this->pdo = null;
$pdo = $this->getPDO($dsn, $user);
return new TransactionalMutex($pdo, $timeout);
}];
};
if (getenv("MYSQL_DSN")) {
$dsn = getenv("MYSQL_DSN");
$user = getenv("MYSQL_USER");
$addPDO($dsn, $user, "mysql");
}
if (getenv("PGSQL_DSN")) {
$dsn = getenv("PGSQL_DSN");
$user = getenv("PGSQL_USER");
$addPDO($dsn, $user, "postgres");
}
return $cases;
}