编辑:这是由莫里茨解决的 . 我在行上的代码中添加了一个注释错误 .
我的应用程序是一个与游戏客户端交谈的Web服务器 . 服务器是多线程的,Postgres允许 . 在将客户端数据加载到数据库中时,我注意到并行请求因几个不同的错误而失败,这些错误对我来说都没有意义 .
这个简短的测试用例将嵌套的哈希转储到数据库中 . 在没有 start
的情况下运行时,它可以完美运行 . 使用线程运行时,它几乎总是会出现以下一个或多个错误:
DBDish :: Pg:错误:(7)方法准备在D:\ rakudo \ share \ perl6 \ site \ sources \ BAD7C1548F63C7AA7BC86BEDDA0F7BD185E141AD(DBDish :: Pg :: Connection)第48行,在testcase.p6第62行的子块中在testcase.p6第59行的testcase.p6第59行的add-enum-mappings在DBDish :: Pg:错误:错误:准备语句“pg_3448_16”已经存在(7)在方法准备D:\ rakudo \ share \ perl6 \ site \ sources \ BAD7C1548F63C7AA7BC86BEDDA0F7BD185E141AD(DBDish :: Pg :: Connection)第46行在testcase.p6第62行中的sub add-enum-mappings在testcase.p6第59行在blockcase中的testcase.p6第91行DBDish :: Pg :错误:方法执行的参数数量错误:得到1,预期0(-1)方法在方法中输入 - 执行D:\ rakudo \ share \ perl6 \ site \ sources \ 65FFB78EFA3030486D1C4D339882A410E3C94AD2(DBDish :: StatementHandle)第40行方法执行D:\ rakudo \ share \ perl6 \ site \ sources \ B3190B6E6B1AA764F7521B490408245094C6AA87(DBDish :: Pg :: StatementHandle)第52行,在sub-add-enum-mappings at testcase.p6第54行的bloc中执行k在testcase.p6第90行消息类型0x31从服务器到达而空闲消息类型0x5a从服务器到达而空闲消息类型0x74从服务器到达而空闲消息类型0x6e从服务器到达而空闲消息类型0x5a从服务器到达时空闲
这是代码 . (如果你选择运行它,请记住设置正确的密码 . 它创建/操作一个名为"enummappings"的表,但不执行任何其他操作 . )肉在 add-enum-mappings()
中 . 其他一切都只是设置 . 哦, dbh()
为每个线程创建一个单独的DB连接 . 根据PostgreSQL文档,这是必要的 .
#!/usr/bin/env perl6
use DBIish;
use Log::Async;
my Lock $db-lock;
my Lock $deletion-lock;
my Lock $insertion-lock;
INIT {
logger.send-to($*ERR);
$db-lock .= new;
$deletion-lock .= new;
$insertion-lock .= new;
}
# Get a per-thread database connection.
sub dbh() {
state %connections;
my $dbh := %connections<$*THREAD.id>; # THIS IS WRONG. Should be %connections{$*THREAD.id}.
$db-lock.protect: {
if !$dbh.defined {
$dbh = DBIish.connect('Pg', :host<127.0.0.1>, :port(5432), :database<postgres>,
:user<postgres>, :password<PASSWORD>);
}
};
return $dbh;
}
sub create-table() {
my $name = 'enummappings';
my $column-spec =
'enumname TEXT NOT NULL, name TEXT NOT NULL, value INTEGER NOT NULL, UNIQUE(enumname, name)';
my $version = 1;
my $sth = dbh.prepare("CREATE TABLE IF NOT EXISTS $name ($column-spec);");
$sth.execute;
# And add the version number to a version table:
dbh.execute:
"CREATE TABLE IF NOT EXISTS tableversions (name TEXT NOT NULL UNIQUE, version INTEGER NOT NULL);";
$sth = dbh.prepare:
'INSERT INTO tableversions (name, version) VALUES (?, ?)
ON CONFLICT (name)
DO
UPDATE SET version = ?;';
$sth.execute($name, $version, $version);
}
sub add-enum-mappings($enumname, @names, @values --> Hash) {
$deletion-lock.protect: {
my $sth = dbh.prepare('DELETE FROM enummappings WHERE enumname = ?;');
$sth.execute($enumname);
};
my @rows = (^@names).map: -> $i {$enumname, @names[$i], @values[$i]};
info "Inserting @rows.elems() rows...";
$insertion-lock.protect: {
my $sth = dbh.prepare('INSERT INTO enummappings (enumname,name,value) VALUES '~
('(?,?,?)' xx @rows.elems).join(',') ~ ';');
$sth.execute(@rows>>.list.flat);
};
return %(status => 'okay');
}
# Create a bunch of long enums with random names, keys, and values.
sub create-enums(--> Hash[Hash]) {
my @letters = ('a'..'z', 'A'..'Z').flat;
my Hash %enums = ();
for ^36 {
my $key = @letters.pick(10).join;
for ^45 {
my $sub-key = @letters.pick(24).join;
%enums{$key}{$sub-key} = (0..10).pick;
}
}
return %enums;
}
sub MAIN() {
create-table;
await do for create-enums.kv -> $enum-name, %enum {
start {
add-enum-mappings($enum-name, %enum.keys, %enum.values);
CATCH { default { note "Got error adding enum: " ~ .gist; } }
};
}
}
我在Windows 10上,配有8核计算机 . 我知道我可以单线程插入数据,但如果游戏一次获得一百个连接怎么办?我需要解决这个问题 .
1 回答
我怀疑你的问题在这里:
%hash<...>
语法仅适用于文字 . 你真的需要写%connections{$*THREAD.id}
.有了你的错误,你只有一个在所有线程之间共享的数据库连接,我想这就是DBIish(或底层的postgresql C客户端库)不满意的地方 .