cockroachdb / docs

CockroachDB user documentation
https://cockroachlabs.com/docs
Creative Commons Attribution 4.0 International
190 stars 459 forks source link

Add Perl "Hello World" example code #6697

Open rmloveland opened 4 years ago

rmloveland commented 4 years ago

Richard Loveland (rmloveland) commented:

We have had a user request for a Perl driver.

AFAICT from some light usage, DBI (with the [DBD::Pg](https://metacpan.org/pod/DBD::Pg] backend] works fine. At least I haven't encountered any problems yet. E.g., prepared statements with placeholders work.

Estimated scope of work:

Jira Issue: DOC-450

rmloveland commented 3 years ago

had this basic retry loop in my back pocket, figure I'll post it here in case it's useful

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw/ say /;
use DBI;
use Data::Dumper;
use Try::Tiny;

my $db_config = { AutoCommit => -1, pg_errorlevel => 2 };

my $dbh = DBI->connect( "dbi:Pg:dbname=bank;host=localhost;port=26257",
    'root', '', $db_config );

# We will manage the transaction lifecycle in our code.
$dbh->{AutoCommit} = undef;
$dbh->{RaiseError} = 1;

END {
    $dbh->disconnect;
}

sub create_accounts {
    my $dbh = shift;

    my @stmts = (
q[CREATE TABLE IF NOT EXISTS accounts (id INT PRIMARY KEY, balance INT)],
        q[UPSERT INTO accounts (id, balance) VALUES (1, 1000), (2, 250)],
    );

    $dbh->do($_) for @stmts;
    $dbh->commit;
}

sub print_balances {
    my $dbh  = shift;
    my @rows = $dbh->selectrow_array(q[SELECT id, balance FROM accounts]);
    $dbh->commit;
    say qq[Balances at], localtime(time);
    say for @rows;
}

sub delete_accounts {
    my $dbh = shift;
    $dbh->do(q[DELETE FROM bank.accounts]);
    $dbh->commit;
}

sub run_transaction {
    my ( $dbh, $sub ) = @_;

    my $retries     = 0;
    my $max_retries = 3;

  RETRY: while (1) {
        $retries++;
        if ( $retries == $max_retries ) {
            die qq[Transaction did not succeed after $retries retries\n];
        }
        try {
            $sub->($dbh);
        }
        catch {
            say qq[Had an error of type: ], Dumper $dbh->state;

            # Capture the PG error code
            my $SQL_STATE = $dbh->state;
            if ( $SQL_STATE eq q[40001] ) {
                eval { $dbh->rollback };
                my $sleep_secs = int( 2**$retries );
                say qq[Sleeping for $sleep_secs seconds ...];
                sleep($sleep_secs);
                next RETRY;
            }
            else {
                # This is not a transaction retry error, so your
                # application will need to deal with it separately.
            }
        }
    }
}

sub test_retry_loop {
    my $dbh = shift;

    my @stmts =
      ( q[SELECT now()], q[SELECT crdb_internal.force_retry('1s'::INTERVAL)], );

    for (@stmts) {
        say qq[About to run SQL: '$_'];
        $dbh->do($_);
    }
}

sub transfer_funds { }

sub main {
    my $dbh = shift;

    my $stmt   = qq[SELECT now()];
    my @answer = $dbh->selectrow_array($stmt);
    say Dumper @answer;

    # Test retry loop
    say qq[Testing retry loop ...];
    run_transaction( $dbh, \&test_retry_loop );
}

main($dbh);