无法使用线程将数据插入到带有DBIish的PostgreSQL中。出了什么问题?

时间:2017-10-27 11:29:31

标签: perl6

编辑:这是由莫里茨解决的。我在行上的代码中添加了一个注释错误。

我的应用程序是与游戏客户端交谈的Web服务器。服务器是多线程的,Postgres允许。在将客户端数据加载到数据库中时,我注意到并行请求因几个不同的错误而失败,这些错误对我来说都没有意义。

这个简短的测试用例将嵌套的哈希转储到数据库中。在没有start的情况下运行时,它可以完美运行。使用线程运行时,它几乎总是会出现以下一个或多个错误:

  

DBDish :: Pg:错误:(7)在方法准备中   d:\ rakudo \共享\ perl6 \网站\来源\ BAD7C1548F63C7AA7BC86BEDDA0F7BD185E141AD   (DBDish :: Pg :: Connection)在testcase.p6第62行的块中的第48行   在sub-add-enum-mappings at testcase.p6第59行in block at   testcase.p6第91行

     

DBDish :: Pg:错误:错误:准备好的声明   “pg_3448_16”已经存在(7)在方法准备中   d:\ rakudo \共享\ perl6 \网站\来源\ BAD7C1548F63C7AA7BC86BEDDA0F7BD185E141AD   (DBDish :: Pg :: Connection)在testcase.p6第62行的块中的第46行   在sub-add-enum-mappings at testcase.p6第59行in block at   testcase.p6第91行

     

DBDish :: Pg:错误:参数数量错误   方法执行:得到1,期望0(-1)在方法enter-execute at   d:\ rakudo \共享\ perl6 \网站\ \源65FFB78EFA3030486D1C4D339882A410E3C94AD2   (DBDish :: StatementHandle)方法中的第40行执行at   d:\ rakudo \共享\ perl6 \网站\来源\ B3190B6E6B1AA764F7521B490408245094C6AA87   (DBDish :: Pg :: StatementHandle)子add-enum-mappings中的第52行   在testcase.p6第90行的块中的testcase.p6第54行

     

消息类型0x31在空闲时从服务器到达   消息类型0x5a在空闲时从服务器到达   消息类型0x74在空闲时从服务器到达   消息类型0x6e在空闲时从服务器到达   消息类型0x5a在空闲时从服务器到达

这是代码。 (如果你选择运行它,请记住设置正确的密码。它创建/操作一个名为“enummappings”的表,但不执行任何其他操作。)肉在add-enum-mappings()。其他一切都只是设置。哦,dbh()为每个线程创建一个单独的数据库连接。根据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 个答案:

答案 0 :(得分:4)

我怀疑你的问题在这里:

my $dbh := %connections<$*THREAD.id>;

%hash<...>语法仅适用于文字。你真的需要写%connections{$*THREAD.id}

如果你的错误到位,你只有一个在所有线程之间共享的数据库连接,我想这就是DBIish(或底层的postgresql C客户端库)不满意的。