-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy path005_recompression_spin_lock_test.pl
More file actions
184 lines (153 loc) · 5.53 KB
/
Copy path005_recompression_spin_lock_test.pl
File metadata and controls
184 lines (153 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# This file and its contents are licensed under the Timescale License.
# Please see the included NOTICE for copyright information and
# LICENSE-TIMESCALE for a copy of the license.
# This TAP test verifies the behavior when recompression is attempted on a chunk
# with unique constraints while concurrent DML is happening
use strict;
use warnings;
use TimescaleNode;
use Data::Dumper;
use Test::More;
# Test setup
my $node = TimescaleNode->create('node');
# Create table with a unique constraint
my $result = $node->safe_psql(
'postgres', q{
CREATE TABLE sensor_data (
time timestamptz not null,
sensor_id integer not null,
cpu double precision null,
temperature double precision null,
UNIQUE(time, sensor_id)
);
}
);
is($result, '', 'create table with unique constraint');
# Create hypertable
$result = $node->safe_psql(
'postgres', q{
SELECT table_name FROM create_hypertable('sensor_data','time', chunk_time_interval => INTERVAL '1 month');
}
);
is($result, 'sensor_data', 'create hypertable');
# Insert data
$result = $node->safe_psql(
'postgres', q{
INSERT INTO sensor_data
SELECT
time + (INTERVAL '1 minute' * random()) AS time,
sensor_id,
random() AS cpu,
random()* 100 AS temperature
FROM
generate_series('2022-01-01', '2022-01-07', INTERVAL '1 hour') AS g1(time),
generate_series(1, 50, 1) AS g2(sensor_id)
ORDER BY time;
}
);
is($result, '', 'insert data');
# Define count query
my $count_query = "SELECT count(*) FROM sensor_data;";
# Count inserted rows
my $num_rows = $node->safe_psql('postgres', $count_query);
is($num_rows, 7250, 'validate inserted rows');
# Enable compression
$result = $node->safe_psql(
'postgres', q{
ALTER TABLE sensor_data SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'sensor_id',
timescaledb.compress_orderby = 'time'
);
}
);
is($result, '', 'enable compression');
# Compress the chunk
my $compress_query =
"SELECT count(*) FROM (SELECT compress_chunk(show_chunks('sensor_data')));";
$result = $node->safe_psql('postgres', $compress_query);
is($result, '1', 'compress chunk');
# Insert more data to make the chunk partial
$result = $node->safe_psql(
'postgres', q{
INSERT INTO sensor_data
SELECT
time + (INTERVAL '1 minute' * random()) AS time,
sensor_id,
random() AS cpu,
random()* 100 AS temperature
FROM
generate_series('2022-01-01', '2022-01--7', INTERVAL '1 hour') AS g1(time),
generate_series(51, 55, 1) AS g2(sensor_id)
ORDER BY time;
}
);
is($result, '', 'insert more data to make the chunk partial');
# Create psql sessions
my $s1 = $node->background_psql('postgres');
my $s2 = $node->background_psql('postgres');
my $s3 = $node->background_psql('postgres');
# Enable segmentwise recompression
$s1->query_safe("SET timescaledb.enable_segmentwise_recompression TO on;");
# Enable waiting to acquire an exclusive lock
$s1->query_safe("SET timescaledb.enable_recompress_waiting TO on;");
# TEST 1:
# Session 1 tries to acquire an exclusive lock at the end of recompression but is blocked due to the inserts by Session 2
# We use session 3 to set up debug waitpoints
# Begin txns in all sessions
$s1->query_safe("BEGIN;");
$s2->query_safe("BEGIN;");
$s3->query_safe("BEGIN;");
# We enable the debug_waitpoint after the latch and release it after s2 aborts
# This allows s1 to successfully acquire the lock the second time around
$s3->query_safe(
"SELECT debug_waitpoint_enable('chunk_recompress_after_latch');");
# Get lock data
$result = $node->safe_psql('postgres',
"SELECT relation::regclass::text, mode FROM pg_locks WHERE relation::regclass::text LIKE '%hyper_1_%chunk' and granted;"
);
is($result, '', "verify no locks exist on the chunk");
# Session 2: Insert rows into the chunk
# This blocks until session 2 releases its locks
$s2->query_until(
'', q{
INSERT INTO sensor_data VALUES
('2022-01-05 12:00:00', 100, 0.5, 25.5),
('2022-01-05 13:00:00', 101, 0.6, 26.5);
});
# We have to use 'query_until('', ...)' so that the test immediately fires the next query
$s1->query_until(
'', q{
SELECT compress_chunk(show_chunks('sensor_data'));
});
# Session 2 immediately aborts, releasing the RowExclusiveLock on the table
$s2->query_safe("ABORT");
# Release the debug waitpoint so that recompression succeeds
$s3->query_safe(
"SELECT debug_waitpoint_release('chunk_recompress_after_latch');");
# Session 1 re-acquires the lock asynchronously after the waitpoint is
# released, so poll until it shows up before asserting.
$node->poll_query_until('postgres',
"SELECT count(*) > 0 FROM pg_locks WHERE relation::regclass::text LIKE '%hyper_1_%chunk' AND granted AND mode = 'ExclusiveLock';"
) or die "timed out waiting for ExclusiveLock on uncompressed chunk";
# Verify ExclusiveLock on uncompressed chunk
$result = $node->safe_psql('postgres',
"SELECT relation::regclass::text FROM pg_locks WHERE relation::regclass::text LIKE '%hyper_1_%chunk' AND granted AND mode = 'ExclusiveLock';"
);
is( $result,
'_timescaledb_internal._hyper_1_1_chunk',
"verify ExclusiveLock on uncompressed chunk");
# Verify AccessShareLock on compressed chunk
$result = $node->safe_psql('postgres',
"SELECT relation::regclass::text FROM pg_locks WHERE relation::regclass::text LIKE '%hyper_1_%chunk' AND granted AND mode = 'AccessShareLock'"
);
is( $result,
"_timescaledb_internal._hyper_1_1_chunk",
"verify AccessShareLock on internal compressed chunk");
# Clean up
$s1->query_safe("ROLLBACK;");
$s3->query_safe("ROLLBACK;");
$s3->quit();
$s2->quit();
$s1->quit();
done_testing();