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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy