|
1 | 1 | use strict;
|
2 | 2 | use warnings;
|
3 | 3 |
|
| 4 | +use Config; |
4 | 5 | use PostgresNode;
|
5 | 6 | use TestLib;
|
6 |
| -use Test::Moretests=> 18; |
| 7 | +use Test::Moretests=> 27; |
| 8 | + |
| 9 | +use constant |
| 10 | +{ |
| 11 | +READ_COMMITTED=> 0, |
| 12 | +REPEATABLE_READ=> 1, |
| 13 | +SERIALIZABLE=> 2, |
| 14 | +}; |
| 15 | + |
| 16 | +my@isolation_level_sql = ('read committed','repeatable read','serializable'); |
| 17 | +my@isolation_level_abbreviations = ('RC','RR','S'); |
7 | 18 |
|
8 | 19 | # Test concurrent deadlock updates in table with different default transaction
|
9 | 20 | # isolation levels.
|
|
18 | 29 | append_to_file($script1,
|
19 | 30 | "\\set delta1 random(-5000, 5000)\n"
|
20 | 31 | ."\\set delta2 random(-5000, 5000)\n"
|
21 |
| - ."BEGIN;" |
22 |
| - ."UPDATE xy SET y = y + :delta1 WHERE x = 1;" |
23 |
| - ."UPDATE xy SET y = y + :delta2 WHERE x = 2;" |
| 32 | + ."BEGIN;\n" |
| 33 | + ."UPDATE xy SET y = y + :delta1 WHERE x = 1;\n" |
| 34 | + ."SELECT pg_sleep(20);\n" |
| 35 | + ."UPDATE xy SET y = y + :delta2 WHERE x = 2;\n" |
24 | 36 | ."END;");
|
25 | 37 |
|
26 | 38 | my$script2 =$node->basedir .'/pgbench_script2';
|
27 | 39 | append_to_file($script2,
|
28 | 40 | "\\set delta1 random(-5000, 5000)\n"
|
29 | 41 | ."\\set delta2 random(-5000, 5000)\n"
|
30 |
| - ."BEGIN;" |
31 |
| - ."UPDATE xy SET y = y + :delta2 WHERE x = 2;" |
32 |
| - ."UPDATE xy SET y = y + :delta1 WHERE x = 1;" |
| 42 | + ."BEGIN;\n" |
| 43 | + ."UPDATE xy SET y = y + :delta2 WHERE x = 2;\n" |
| 44 | + ."UPDATE xy SET y = y + :delta1 WHERE x = 1;\n" |
33 | 45 | ."END;");
|
34 | 46 |
|
35 |
| -# Test deadlock transactions with Read committed default isolation level: |
36 |
| -$node->command_like( |
37 |
| -[qw(pgbench --no-vacuum --client=5 --transactions=10 |
38 |
| - --default-isolation-level=RC --file),$script1,qw(--file),$script2], |
39 |
| -qr{processed: 50/50}, |
40 |
| -'concurrent deadlock update: Read Committed: check processed transactions'); |
41 |
| - |
42 |
| -$node->command_like( |
43 |
| -[qw(pgbench --no-vacuum --client=5 --transactions=10 |
44 |
| - --default-isolation-level=RC --file),$script1,qw(--file),$script2], |
45 |
| -qr{deadlock failures: [1-9]\d*\([1-9]\d*\.\d* %\)}, |
46 |
| -'concurrent deadlock update: Read Committed: check deadlock failures'); |
47 |
| - |
48 |
| -# Test deadlock transactions with Repeatable read default isolation level: |
49 |
| -$node->command_like( |
50 |
| -[qw(pgbench --no-vacuum --client=5 --transactions=10 |
51 |
| - --default-isolation-level=RR --file),$script1,qw(--file),$script2], |
52 |
| -qr{processed: 50/50}, |
53 |
| -'concurrent deadlock update: Repeatable Read: check processed transactions'); |
54 |
| - |
55 |
| -$node->command_like( |
56 |
| -[qw(pgbench --no-vacuum --client=5 --transactions=10 |
57 |
| - --default-isolation-level=RR --file),$script1,qw(--file),$script2], |
58 |
| -qr{deadlock failures: [1-9]\d*\([1-9]\d*\.\d* %\)}, |
59 |
| -'concurrent deadlock update: Repeatable Read: check deadlock failures'); |
60 |
| - |
61 |
| -# Test deadlock transactions with Serializable default isolation level: |
62 |
| -$node->command_like( |
63 |
| -[qw(pgbench --no-vacuum --client=5 --transactions=10 |
64 |
| - --default-isolation-level=S --file),$script1,qw(--file),$script2], |
65 |
| -qr{processed: 50/50}, |
66 |
| -'concurrent deadlock update: Serializable: check processed transactions'); |
67 |
| - |
68 |
| -$node->command_like( |
69 |
| -[qw(pgbench --no-vacuum --client=5 --transactions=10 |
70 |
| - --default-isolation-level=S --file),$script1,qw(--file),$script2], |
71 |
| -qr{deadlock failures: [1-9]\d*\([1-9]\d*\.\d* %\)}, |
72 |
| -'concurrent deadlock update: Serializable: check deadlock failures'); |
| 47 | +subtest_pgbench |
| 48 | +{ |
| 49 | +my ($isolation_level) =@_; |
| 50 | + |
| 51 | +my$isolation_level_sql =$isolation_level_sql[$isolation_level]; |
| 52 | +my$isolation_level_abbreviation = |
| 53 | +$isolation_level_abbreviations[$isolation_level]; |
| 54 | + |
| 55 | +local$ENV{PGPORT} =$node->port; |
| 56 | + |
| 57 | +my ($h1,$in1,$out1,$err1); |
| 58 | +my ($h2,$in2,$out2,$err2); |
| 59 | + |
| 60 | +# Run first pgbench |
| 61 | +my@command1 = ( |
| 62 | +qw(pgbench --no-vacuum --transactions=1 --default-isolation-level), |
| 63 | +$isolation_level_abbreviation, |
| 64 | +"--file", |
| 65 | +$script1); |
| 66 | +print"# Running:" .join("",@command1) ."\n"; |
| 67 | +$h1 = IPC::Run::start \@command1, \$in1, \$out1, \$err1; |
| 68 | + |
| 69 | +# Let pgbench run first update command in the transaction: |
| 70 | +sleep 10; |
| 71 | + |
| 72 | +# Run second pgbench |
| 73 | +my@command2 = ( |
| 74 | +qw(pgbench --no-vacuum --transactions=1 --default-isolation-level), |
| 75 | +$isolation_level_abbreviation, |
| 76 | +"--file", |
| 77 | +$script2); |
| 78 | +print"# Running:" .join("",@command2) ."\n"; |
| 79 | +$h2 = IPC::Run::start \@command2, \$in2, \$out2, \$err2; |
| 80 | + |
| 81 | +# Get all pgbench results |
| 82 | +$h1->pump()untillength$out1; |
| 83 | +$h1->finish(); |
| 84 | + |
| 85 | +$h2->pump()untillength$out2; |
| 86 | +$h2->finish(); |
| 87 | + |
| 88 | +# On Windows, the exit status of the process is returned directly as the |
| 89 | +# process's exit code, while on Unix, it's returned in the high bits |
| 90 | +# of the exit code (see WEXITSTATUS macro in the standard <sys/wait.h> |
| 91 | +# header file). IPC::Run's result function always returns exit code >> 8, |
| 92 | +# assuming the Unix convention, which will always return 0 on Windows as |
| 93 | +# long as the process was not terminated by an exception. To work around |
| 94 | +# that, use $h->full_result on Windows instead. |
| 95 | +my$result1 = |
| 96 | + ($Config{osname}eq"MSWin32") |
| 97 | + ? ($h1->full_results)[0] |
| 98 | + :$h1->result(0); |
| 99 | + |
| 100 | +my$result2 = |
| 101 | + ($Config{osname}eq"MSWin32") |
| 102 | + ? ($h2->full_results)[0] |
| 103 | + :$h2->result(0); |
| 104 | + |
| 105 | +# Check all pgbench results |
| 106 | +ok(!$result1,"@command1 exit code 0"); |
| 107 | +ok(!$result2,"@command2 exit code 0"); |
| 108 | + |
| 109 | +is($err1,'',"@command1 no stderr"); |
| 110 | +is($err2,'',"@command2 no stderr"); |
| 111 | + |
| 112 | +like($out1, |
| 113 | +qr{default transaction isolation level:$isolation_level_sql}, |
| 114 | +"concurrent deadlock update:" |
| 115 | + .$isolation_level_sql |
| 116 | + .": pgbench 1: check default isolation level"); |
| 117 | +like($out2, |
| 118 | +qr{default transaction isolation level:$isolation_level_sql}, |
| 119 | +"concurrent deadlock update:" |
| 120 | + .$isolation_level_sql |
| 121 | + .": pgbench 2: check default isolation level"); |
| 122 | + |
| 123 | +like($out1, |
| 124 | +qr{processed: 1/1}, |
| 125 | +"concurrent deadlock update:" |
| 126 | + .$isolation_level_sql |
| 127 | + .": pgbench 1: check processed transactions"); |
| 128 | +like($out2, |
| 129 | +qr{processed: 1/1}, |
| 130 | +"concurrent deadlock update:" |
| 131 | + .$isolation_level_sql |
| 132 | + .": pgbench 2: check processed transactions"); |
| 133 | + |
| 134 | +# First or second pgbench should get a deadlock error |
| 135 | +like($out1 .$out2, |
| 136 | +qr{deadlock failures: 1\(100\.000 %\)}, |
| 137 | +"concurrent deadlock update:" |
| 138 | + .$isolation_level_sql |
| 139 | + .": check deadlock failures"); |
| 140 | +} |
| 141 | + |
| 142 | +test_pgbench(READ_COMMITTED); |
| 143 | +test_pgbench(REPEATABLE_READ); |
| 144 | +test_pgbench(SERIALIZABLE); |