Skip to content

Commit 466a086

Browse files
committed
Test for a write cycles
1 parent 6fb8e41 commit 466a086

File tree

1 file changed

+114
-45
lines changed

1 file changed

+114
-45
lines changed
Lines changed: 114 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
###############################################################################
22
# Test of proper transaction isolation.
3+
# Based on Martin Kleppmann tests, https://github.com/ept/hermitage
34
###############################################################################
45

56
use strict;
67
use warnings;
78
use PostgresNode;
89
use TestLib;
9-
use Test::More tests => 1;
10-
use DBI();
11-
use DBD::Pg();
10+
use Test::More tests => 2;
11+
use DBI;
12+
use DBD::Pg ':async';
1213

1314
sub query_row
1415
{
@@ -23,8 +24,16 @@ sub query_row
2324
sub query_exec
2425
{
2526
my ($dbi, $sql) = @_;
26-
print "query_exec('$sql')\n";
2727
my $rv = $dbi->do($sql) || die;
28+
print "query_exec('$sql')\n";
29+
return $rv;
30+
}
31+
32+
sub query_exec_async
33+
{
34+
my ($dbi, $sql) = @_;
35+
my $rv = $dbi->do($sql, {pg_async => PG_ASYNC}) || die;
36+
print "query_exec('$sql')\n";
2837
return $rv;
2938
}
3039

@@ -50,33 +59,33 @@ sub PostgresNode::psql_fails {
5059
my $node1 = get_new_node("node1");
5160
$node1->init;
5261
$node1->append_conf('postgresql.conf', qq(
53-
max_prepared_transactions = 10
54-
shared_preload_libraries = 'pg_tsdtm'
62+
max_prepared_transactions = 10
63+
shared_preload_libraries = 'pg_tsdtm'
5564
));
5665
$node1->start;
5766

5867
# Setup second node
5968
my $node2 = get_new_node("node2");
6069
$node2->init;
6170
$node2->append_conf('postgresql.conf', qq(
62-
max_prepared_transactions = 10
63-
shared_preload_libraries = 'pg_tsdtm'
71+
max_prepared_transactions = 10
72+
shared_preload_libraries = 'pg_tsdtm'
6473
));
6574
$node2->start;
6675

67-
$node1->psql('postgres', "create extension pg_tsdtm;");
68-
$node1->psql('postgres', "create table t(u int primary key, v int)");
69-
$node1->psql('postgres', "insert into t (select generate_series(0, 9), 0)");
76+
$node1->psql('postgres', "create extension pg_tsdtm");
77+
$node1->psql('postgres', "create table t(id int primary key, v int)");
78+
$node1->psql('postgres', "insert into t values(1, 10)");
7079

71-
$node2->psql('postgres', "create extension pg_tsdtm;");
72-
$node2->psql('postgres', "create table t(u int primary key, v int)");
73-
$node2->psql('postgres', "insert into t (select generate_series(0, 9), 0)");
80+
$node2->psql('postgres', "create extension pg_tsdtm");
81+
$node2->psql('postgres', "create table t(id int primary key, v int)");
82+
$node2->psql('postgres', "insert into t values(2, 20)");
7483

7584
# we need two connections to each node (run two simultameous global tx)
76-
my $conn11 = DBI->connect('DBI:Pg:' . $node1->connstr('postgres'));
77-
my $conn21 = DBI->connect('DBI:Pg:' . $node2->connstr('postgres'));
78-
my $conn12 = DBI->connect('DBI:Pg:' . $node1->connstr('postgres'));
79-
my $conn22 = DBI->connect('DBI:Pg:' . $node2->connstr('postgres'));
85+
my $conn1a = DBI->connect('DBI:Pg:' . $node1->connstr('postgres'));
86+
my $conn2a = DBI->connect('DBI:Pg:' . $node2->connstr('postgres'));
87+
my $conn1b = DBI->connect('DBI:Pg:' . $node1->connstr('postgres'));
88+
my $conn2b = DBI->connect('DBI:Pg:' . $node2->connstr('postgres'));
8089

8190
sub count_total
8291
{
@@ -100,34 +109,94 @@ sub count_total
100109
return $tot;
101110
}
102111

112+
sub start_global
113+
{
114+
my ($gtid, $c1, $c2) = @_;
115+
116+
query_exec($c1, "begin transaction");
117+
query_exec($c2, "begin transaction");
118+
my $snapshot = query_row($c1, "select dtm_extend('$gtid')");
119+
query_exec($c2, "select dtm_access($snapshot, '$gtid')");
120+
}
121+
122+
sub commit_global
123+
{
124+
my ($gtid, $c1, $c2) = @_;
125+
126+
query_exec($c1, "prepare transaction '$gtid'");
127+
query_exec($c2, "prepare transaction '$gtid'");
128+
query_exec($c1, "select dtm_begin_prepare('$gtid')");
129+
query_exec($c2, "select dtm_begin_prepare('$gtid')");
130+
my $csn = query_row($c1, "select dtm_prepare('$gtid', 0)");
131+
query_exec($c2, "select dtm_prepare('$gtid', $csn)");
132+
query_exec($c1, "select dtm_end_prepare('$gtid', $csn)");
133+
query_exec($c2, "select dtm_end_prepare('$gtid', $csn)");
134+
query_exec($c1, "commit prepared '$gtid'");
135+
query_exec($c2, "commit prepared '$gtid'");
136+
}
137+
103138
###############################################################################
104-
# Sanity check on dirty reads
139+
# Sanity check for dirty reads
140+
###############################################################################
141+
142+
start_global("gtx1", $conn1a, $conn2a);
143+
144+
query_exec($conn1a, "update t set v = v - 10 where id=1");
145+
146+
my $intermediate_total = count_total($conn1b, $conn2b);
147+
148+
query_exec($conn2a, "update t set v = v + 10 where id=2");
149+
150+
commit_global("gtx1", $conn1a, $conn2a);
151+
152+
is($intermediate_total, 30, "Check for absence of dirty reads");
153+
105154
###############################################################################
155+
# G0
156+
###############################################################################
157+
158+
my $fail = 0;
159+
$node1->psql('postgres', "update t set v = 10 where id = 2");
160+
$node2->psql('postgres', "update t set v = 20 where id = 2");
161+
162+
start_global("gtx2a", $conn1a, $conn2a);
163+
start_global("gtx2b", $conn1b, $conn2b);
164+
165+
query_exec($conn1a, "update t set v = 11 where id = 1");
166+
query_exec_async($conn1b, "update t set v = 12 where id = 1");
167+
168+
# last update should be locked
169+
$fail = 1 if $conn1b->pg_ready != 0;
170+
171+
query_exec($conn2a, "update t set v = 21 where id = 2");
172+
commit_global("gtx2a", $conn1a, $conn2a);
173+
174+
# here transaction can continue
175+
$conn1b->pg_result;
176+
177+
my $v1 = query_row($conn1a, "select v from t where id = 1");
178+
my $v2 = query_row($conn2a, "select v from t where id = 2");
179+
180+
# we shouldn't see second's tx data
181+
$fail = 1 if $v1 != 11 or $v2 != 21;
182+
183+
query_exec($conn2b, "update t set v = 22 where id = 2");
184+
commit_global("gtx2b", $conn1b, $conn2b);
185+
186+
$v1 = query_row($conn1a, "select v from t where id = 1");
187+
$v2 = query_row($conn2a, "select v from t where id = 2");
188+
189+
$fail = 1 if $v1 != 12 or $v2 != 22;
190+
191+
is($fail, 0, "Global transactions prevents Write Cycles (G0)");
192+
193+
194+
195+
196+
197+
198+
199+
200+
106201

107-
my $gtid1 = "gtx1";
108-
109-
# start global tx
110-
query_exec($conn11, "begin transaction");
111-
query_exec($conn21, "begin transaction");
112-
my $snapshot = query_row($conn11, "select dtm_extend('$gtid1')");
113-
query_exec($conn21, "select dtm_access($snapshot, '$gtid1')");
114-
115-
# transfer some amount of integers to different node
116-
query_exec($conn11, "update t set v = v - 10 where u=1");
117-
my $intermediate_total = count_total($conn12, $conn22);
118-
query_exec($conn21, "update t set v = v + 10 where u=2");
119-
120-
# commit our global tx
121-
query_exec($conn11, "prepare transaction '$gtid1'");
122-
query_exec($conn21, "prepare transaction '$gtid1'");
123-
query_exec($conn11, "select dtm_begin_prepare('$gtid1')");
124-
query_exec($conn21, "select dtm_begin_prepare('$gtid1')");
125-
my $csn = query_row($conn11, "select dtm_prepare('$gtid1', 0)");
126-
query_exec($conn21, "select dtm_prepare('$gtid1', $csn)");
127-
query_exec($conn11, "select dtm_end_prepare('$gtid1', $csn)");
128-
query_exec($conn21, "select dtm_end_prepare('$gtid1', $csn)");
129-
query_exec($conn11, "commit prepared '$gtid1'");
130-
query_exec($conn21, "commit prepared '$gtid1'");
131-
132-
is($intermediate_total, 0, "Check for absence of dirty reads");
133202

0 commit comments

Comments
 (0)