1
0:0:0,06 --> 0:0:0,59999996
Nik: Hello, hello.
2
0:0:0,59999996 --> 0:0:1,92
This is Postgres.FM.
3
0:0:1,92 --> 0:0:3,84
My name is Nik, PostgresAI.
4
0:0:3,84 --> 0:0:7,3199997
And as usual with me, Michael pgMustard.
5
0:0:7,68 --> 0:0:8,5
Hi, Michael.
6
0:0:9,48 --> 0:0:10,42
Michael: Hello, Nik.
7
0:0:10,9 --> 0:0:13,82
Nik: I proposed to discuss hot_standby_feedback.
8
0:0:14,599999 --> 0:0:18,04
And that's interesting because
I have 4 customers with this problem
9
0:0:18,04 --> 0:0:22,26
right now, different on different
platforms, right?
10
0:0:22,26 --> 0:0:22,66
Yeah.
11
0:0:22,66 --> 0:0:25,56
On Supabase on RDS and something
else.
12
0:0:25,56 --> 0:0:30,72
It's very interesting how problems
sometimes are very silent
13
0:0:30,86 --> 0:0:33,98
until suddenly they come from multiple
directions.
14
0:0:34,04 --> 0:0:34,54
Right.
15
0:0:34,64 --> 0:0:37,94
I don't know why it's happening,
right, but it's just happening.
16
0:0:38,739998 --> 0:0:41,86
Michael: All 4, are they all suffering
from the same direction?
17
0:0:42,559998 --> 0:0:43,059998
Yeah.
18
0:0:43,22 --> 0:0:44,02
Okay, interesting.
19
0:0:45,06 --> 0:0:48,4
Nik: So all of them are complaining
about read replicas lagging.
20
0:0:50,58 --> 0:0:55,38
And it's all because platforms,
most platforms keep hot_standby_feedback
21
0:0:55,38 --> 0:0:59,48
off by default, like Postgres
itself has it off by default.
22
0:1:1,56 --> 0:1:8,38
While my honest opinion, like strong
opinion, I will say, is
23
0:1:8,38 --> 0:1:10,88
that you cannot have read replicas
with off.
24
0:1:11,4 --> 0:1:18,28
For general OLTP case with many
users, And I say this after a
25
0:1:18,28 --> 0:1:19,26
lot of experience.
26
0:1:19,94 --> 0:1:26,0
I built 3 social networks with
each 1 of them achieved many million
27
0:1:26,0 --> 0:1:29,88
users and 1 plus DAU daily active
users.
28
0:1:30,56 --> 0:1:32,84
And we needed to scale reads of
course, right?
29
0:1:32,84 --> 0:1:34,58
So we needed read replicas.
30
0:1:35,58 --> 0:1:37,24
1st 1 I built in 2006.
31
0:1:38,92 --> 0:1:42,84
And also I helped many companies
who also needed to scale reads.
32
0:1:43,52 --> 0:1:46,5
My honest opinion, you cannot have
read replica with hot_standby_feedback
33
0:1:46,5 --> 0:1:49,58
off, just because
it will start lagging.
34
0:1:50,22 --> 0:1:51,6
But this is how it is.
35
0:1:52,36 --> 0:1:56,14
All people need to rediscover it
because defaults are...
36
0:1:56,4 --> 0:1:58,14
This is our favorite topic, right?
37
0:1:58,14 --> 0:2:1,9
Defaults are terrible in many aspects.
38
0:2:3,1 --> 0:2:6,46
Michael: Well, yeah, I'm actually
not sure I fully agree on this
39
0:2:6,46 --> 0:2:6,96
1 yet.
40
0:2:6,96 --> 0:2:7,8
Maybe I will by the end.
41
0:2:7,8 --> 0:2:8,76
Nik: You are not alone.
42
0:2:9,96 --> 0:2:11,44
Michael: Yeah, I'm not sure.
43
0:2:11,94 --> 0:2:16,9
It's a really difficult 1, I think,
this 1 in terms of like risks
44
0:2:16,92 --> 0:2:20,44
versus, yeah, when things bite
you.
45
0:2:20,44 --> 0:2:22,54
But let's, let's, maybe let's go
back to basics.
46
0:2:22,54 --> 0:2:23,42
Like what's the…
47
0:2:23,42 --> 0:2:24,38
Nik: Yeah, yeah, yeah.
48
0:2:24,38 --> 0:2:28,94
So we should discuss, and I just
threw it in the, at the very
49
0:2:28,94 --> 0:2:30,6
beginning to set my goal.
50
0:2:30,6 --> 0:2:35,58
I'm going to discuss it in detail
and explain my way of thinking.
51
0:2:36,1 --> 0:2:39,92
But just separately, I'm just wondering,
are there people who
52
0:2:39,92 --> 0:2:46,0
really scaled reads on really large
web or mobile apps and had
53
0:2:46,0 --> 0:2:49,64
it off because I just don't understand
how it's, how it can be
54
0:2:49,64 --> 0:2:52,28
possible unless you, you are okay.
55
0:2:52,28 --> 0:2:54,86
Your application is okay with lagging
replicas.
56
0:2:54,96 --> 0:3:0,32
Sometimes it's so if you serve
traffic to some users and some
57
0:3:0,32 --> 0:3:3,9
stale information, like lagging
some seconds or maybe minutes
58
0:3:3,9 --> 0:3:4,62
is fine.
59
0:3:5,02 --> 0:3:9,12
I can imagine that you basically
show some news for example or
60
0:3:9,12 --> 0:3:10,52
blog posts or something.
61
0:3:10,68 --> 0:3:15,58
But in general, modern applications
like social media applications,
62
0:3:15,82 --> 0:3:20,24
e-commerce and so on Where you
want to scale reads to asynchronous
63
0:3:20,38 --> 0:3:23,3
replicas, of course we talk about
asynchronous replicas here.
64
0:3:24,12 --> 0:3:27,42
Then we do need hot_standby_feedback
to be on.
65
0:3:28,52 --> 0:3:30,4
Anyway, let's start from the basics.
66
0:3:30,44 --> 0:3:34,62
We had several episodes where we
covered basics already.
67
0:3:34,64 --> 0:3:36,56
I just wanted to have a quick recap.
68
0:3:37,2 --> 0:3:40,3
1st of all, my favorite thing,
if some people don't realize,
69
0:3:40,38 --> 0:3:42,88
there are hidden columns, xmin,
xmax.
70
0:3:43,38 --> 0:3:47,52
xmin is a transaction ID which
gave birth to this tuple.
71
0:3:47,54 --> 0:3:49,4
Tuple is a row version, right?
72
0:3:49,74 --> 0:3:53,76
And there is a concept of xmin horizon,
and we had a separate
73
0:3:53,76 --> 0:4:0,22
episode about it, which defines
global for whole that database.
74
0:4:0,28 --> 0:4:6,74
Global horizon, which says this
is the oldest transaction ID that
75
0:4:6,74 --> 0:4:11,18
defines data snapshot, which is
still needed to some transactions,
76
0:4:12,26 --> 0:4:13,04
some clients.
77
0:4:13,1 --> 0:4:13,6
Right.
78
0:4:14,06 --> 0:4:17,62
And it means, And this is global,
a single value basically.
79
0:4:17,74 --> 0:4:21,54
There are many values coming from
different sources, but horizon
80
0:4:21,66 --> 0:4:24,26
is absolute minimum of all of them.
81
0:4:25,52 --> 0:4:30,06
And it defines the garbage collection
behavior, vacuum behavior,
82
0:4:30,06 --> 0:4:30,56
right?
83
0:4:30,78 --> 0:4:36,98
Because when DELETE or UPDATE or
canceled INSERT happens, dead
84
0:4:36,98 --> 0:4:38,52
tuples are produced, right?
85
0:4:38,52 --> 0:4:41,18
And the job is not done fully.
86
0:4:41,2 --> 0:4:43,76
The remaining of the job is garbage
collection.
87
0:4:44,06 --> 0:4:46,08
Dead tuple must be deleted later.
88
0:4:46,86 --> 0:4:52,84
And if this dead tuple belongs
to a transaction, which is newer
89
0:4:53,16 --> 0:4:55,38
than our xmin horizon, Vacuum cannot
delete it.
90
0:4:55,38 --> 0:4:56,48
And this is simple logic.
91
0:4:56,48 --> 0:4:57,22
It's global.
92
0:4:57,44 --> 0:4:59,88
Some people think it's maybe per
table, but it's not.
93
0:4:59,88 --> 0:5:0,56
It's global.
94
0:5:1,4 --> 0:5:1,9
Michael: Yeah.
95
0:5:2,04 --> 0:5:6,1
And this is all very simple on
a single node.
96
0:5:6,1 --> 0:5:10,24
Just if you're only worried about
having a primary database or
97
0:5:10,24 --> 0:5:16,26
maybe only an HA replica, no, it's
nice and simple, right?
98
0:5:16,26 --> 0:5:19,68
Like we just hold on to the very
versions that might be needed
99
0:5:19,68 --> 0:5:20,58
by the oldest.
100
0:5:22,24 --> 0:5:27,48
Query or whatever the process might
need it and then once that's
101
0:5:27,48 --> 0:5:27,98
finished.
102
0:5:28,48 --> 0:5:31,72
We do the cleanup or vacuum does
it right
103
0:5:31,72 --> 0:5:33,84
Nik: And here we need to dive into
some details.
104
0:5:33,84 --> 0:5:37,4
So yeah, let's 1st think about
only the primary and single node
105
0:5:37,44 --> 0:5:40,1
situation where usually people
start.
106
0:5:40,52 --> 0:5:45,24
You have, there are 5 reasons,
5 sources and our monitoring covers
107
0:5:45,24 --> 0:5:45,74
them.
108
0:5:45,78 --> 0:5:48,36
And we discussed it in the xmin horizon
episode.
109
0:5:48,94 --> 0:5:51,68
5 reasons to block xmin horizon.
110
0:5:52,2 --> 0:5:56,08
Normally if all transactions are
fast, they finish promptly,
111
0:5:57,56 --> 0:6:1,88
no replication is used, neither
logical nor physical, and you
112
0:6:1,88 --> 0:6:4,14
don't use prepared transactions.
113
0:6:5,64 --> 0:6:10,46
In this case, xmin horizon flows
naturally, and you can calculate
114
0:6:10,68 --> 0:6:14,54
age of xmin horizon, meaning that
how many transaction IDs, real
115
0:6:14,54 --> 0:6:19,2
transaction IDs passed since this
horizon and ideally it's very
116
0:6:19,2 --> 0:6:19,54
low.
117
0:6:19,54 --> 0:6:21,36
Say thousands is quite low.
118
0:6:21,88 --> 0:6:23,32
10, 000 is quite low.
119
0:6:23,32 --> 0:6:25,06
1 million it's already noticeable.
120
0:6:25,14 --> 0:6:25,64
Right.
121
0:6:26,38 --> 0:6:29,8
And we know, we all know that 1
billion is already basically
122
0:6:29,8 --> 0:6:35,82
0.5 of capacity we have because
if the whole capacity is 2.1
123
0:6:35,98 --> 0:6:37,2
billion roughly, right?
124
0:6:37,7 --> 0:6:45,14
We have 4-byte integer transaction
IDs, and 0.5 of that space
125
0:6:45,56 --> 0:6:50,92
is our future, 0.5 of it is our
past, and we need freezing vacuum.
126
0:6:51,04 --> 0:6:58,04
1 of its jobs is to freeze tuples
saying that don't look at this
127
0:6:58,04 --> 0:7:1,74
xmin, it looks like from future,
but it's from the past, right?
128
0:7:2,22 --> 0:7:9,02
And if we don't freeze during 2.1,
transaction ID, if our xmin horizon
129
0:7:9,84 --> 0:7:13,22
is that old, age of it is that
old.
130
0:7:13,22 --> 0:7:15,32
We can use function age, actually,
right?
131
0:7:15,58 --> 0:7:19,06
In this case, it means that we're
going to be in big trouble
132
0:7:19,06 --> 0:7:22,48
and database will be not accessible
because transaction ID wraparound
133
0:7:22,72 --> 0:7:23,5
will happen.
134
0:7:24,24 --> 0:7:27,8
This is the biggest danger actually
of xmin horizon being blocked
135
0:7:27,8 --> 0:7:28,88
for a long time.
136
0:7:29,44 --> 0:7:33,48
And In the case of single node
of the primary, everything is
137
0:7:33,48 --> 0:7:39,18
relatively simple, but I noticed
many people don't realize nuances
138
0:7:39,44 --> 0:7:39,94
here.
139
0:7:40,24 --> 0:7:45,48
Remember this year I started saying
that the warning, let's avoid
140
0:7:45,48 --> 0:7:48,84
long running transactions is quite
inaccurate.
141
0:7:49,64 --> 0:7:50,14
Right.
142
0:7:50,14 --> 0:7:52,44
Remember this, we're talking about
this.
143
0:7:52,44 --> 0:7:56,1
And I noticed actually, even AI
is making a lot of mistakes.
144
0:7:56,12 --> 0:8:2,1
For example, with GPT-6 Astra,
I decided to renew graphics in
145
0:8:2,1 --> 0:8:6,3
PGSimCity and also make it
more entertainable and have some
146
0:8:6,56 --> 0:8:10,14
bad scenarios to be reproduced
and so you could see what happens.
147
0:8:10,6 --> 0:8:14,54
And it wrote a very simple thing
and I see it quite often.
148
0:8:15,24 --> 0:8:20,54
It said somebody typed BEGIN and
left transaction open for a
149
0:8:20,54 --> 0:8:22,16
day and we're in trouble.
150
0:8:22,2 --> 0:8:25,46
But it's not simple BEGIN is not
a trouble.
151
0:8:25,68 --> 0:8:29,82
Because by default we have read
committed transaction isolation.
152
0:8:30,4 --> 0:8:35,84
Which means that after each statement
snapshot is free to go.
153
0:8:35,88 --> 0:8:38,1
Basically we don't block xmin for
ever.
154
0:8:38,1 --> 0:8:41,68
So if we think about transaction
at default transaction isolation
155
0:8:41,68 --> 0:8:46,28
level, if it consists of multiple
statements, while each statement
156
0:8:46,28 --> 0:8:49,98
lasts, of course we need that snapshot
and we block xmin horizon.
157
0:8:50,2 --> 0:8:51,8
We need to work with that data.
158
0:8:51,88 --> 0:8:53,96
Some SELECT is running on some
huge table.
159
0:8:53,96 --> 0:8:57,04
Yeah, it lasts for long and we
need that data.
160
0:8:57,04 --> 0:9:1,98
But once this SELECT finishes,
Even if transaction is still open,
161
0:9:2,9 --> 0:9:4,4
that snapshot is not needed.
162
0:9:4,4 --> 0:9:7,54
And basically we allow xmin horizon
to shift.
163
0:9:9,06 --> 0:9:13,38
And Simple BEGIN doesn't hold any
problem, any snapshot at all,
164
0:9:13,38 --> 0:9:13,88
right?
165
0:9:15,28 --> 0:9:17,02
At default isolation level.
166
0:9:18,22 --> 0:9:22,2
So yeah, it's very inaccurate and
that's why saying, oh, long
167
0:9:22,2 --> 0:9:24,34
running transactions are dangerous.
168
0:9:24,78 --> 0:9:26,54
It's too rough.
169
0:9:26,76 --> 0:9:31,2
It's not a precise statement because
not every long running transaction
170
0:9:31,34 --> 0:9:31,84
is.
171
0:9:31,84 --> 0:9:32,34
Yes.
172
0:9:33,72 --> 0:9:37,54
Michael: Some are of course, but
not quite all right.
173
0:9:37,54 --> 0:9:39,52
There are lots of cases where they're
not.
174
0:9:40,28 --> 0:9:40,52
Yeah, sure.
175
0:9:40,52 --> 0:9:41,02
Nik: Right.
176
0:9:41,14 --> 0:9:44,04
Of course, we don't talk about
locks here because there are 2
177
0:9:44,04 --> 0:9:49,78
topics, locks and xmin horizon
blockage, 2 dangers of long-running
178
0:9:49,78 --> 0:9:50,28
transactions.
179
0:9:50,5 --> 0:9:56,42
So we need to distinguish read
committed transactions and transactions
180
0:9:56,54 --> 0:10:1,86
running at higher isolation level,
repeatable read and serializable.
181
0:10:3,18 --> 0:10:6,68
And repeatable read is pretty common
because this is how pg_dump
182
0:10:6,68 --> 0:10:7,18
works.
183
0:10:7,9 --> 0:10:11,94
So every time you run pg_dump, even
if it's not doing, of course
184
0:10:11,94 --> 0:10:14,96
it's doing something always, right,
but it captures snapshot
185
0:10:14,96 --> 0:10:18,56
in the very beginning of transaction,
And even if you dump a
186
0:10:18,56 --> 0:10:22,26
lot of tables, you select from
them, the pg_dump selects from
187
0:10:22,26 --> 0:10:25,12
them, it shifts to the next table.
188
0:10:25,52 --> 0:10:29,6
Snapshot is still the same as it
was defined at the very beginning
189
0:10:29,6 --> 0:10:30,36
of transaction.
190
0:10:30,64 --> 0:10:34,64
It means if you start a transaction
at repeatable read, that's
191
0:10:34,64 --> 0:10:36,26
enough to be harmful.
192
0:10:36,98 --> 0:10:40,3
You just say START TRANSACTION
or BEGIN at repeatable isolation
193
0:10:40,38 --> 0:10:40,76
level.
194
0:10:40,76 --> 0:10:42,14
I always forget syntax.
195
0:10:43,52 --> 0:10:46,74
You don't need even to run any
queries inside it.
196
0:10:46,74 --> 0:10:48,02
It's already harmful.
197
0:10:48,74 --> 0:10:49,44
It holds.
198
0:10:49,54 --> 0:10:52,54
Michael: You say harmful, it starts
to hold the xmin horizon,
199
0:10:52,54 --> 0:10:53,04
right?
200
0:10:53,68 --> 0:10:55,12
It affects, yeah.
201
0:10:55,32 --> 0:10:57,74
Short holds are not expected, right?
202
0:10:58,18 --> 0:11:0,68
The reason it's being held is for
a good reason.
203
0:11:1,08 --> 0:11:6,88
If you're doing a dump, you want
probably everything, not just
204
0:11:6,88 --> 0:11:10,24
consistency, but you want the data
at that point in time, and
205
0:11:10,24 --> 0:11:14,12
if it's changed, then you want,
like you still need those, you
206
0:11:14,12 --> 0:11:16,12
can't have those rows being cleaned
up.
207
0:11:16,16 --> 0:11:18,06
Nik: So you don't need to pay…
IM – This is how consistency is
208
0:11:18,06 --> 0:11:18,94
defined, right?
209
0:11:18,94 --> 0:11:19,36
TG –
210
0:11:19,36 --> 0:11:20,15
Michael: Yeah, yeah.
211
0:11:20,15 --> 0:11:20,36
IM
212
0:11:20,36 --> 0:11:23,36
Nik: – Because if you read from
2 tables from different points
213
0:11:23,36 --> 0:11:25,36
of time, this is inconsistent dump.
214
0:11:25,46 --> 0:11:26,82
So we don't want that.
215
0:11:27,1 --> 0:11:29,12
That's why we need a repeatable read.
216
0:11:29,34 --> 0:11:30,64
Michael: But you say it's harmful.
217
0:11:30,66 --> 0:11:34,74
It just means we're holding on
to a bit of extra data maybe bloating
218
0:11:34,74 --> 0:11:39,44
some indexes a bit like there's
a an amount of that we should
219
0:11:39,44 --> 0:11:42,18
always expect when we're running
Postgres right like it there's
220
0:11:42,18 --> 0:11:45,48
a certain amount of that will always
be natural we try and minimize
221
0:11:45,48 --> 0:11:47,68
it and we try not to let it get
excessive.
222
0:11:49,46 --> 0:11:54,52
But then if that goes on for too
long, it gets into that boundary
223
0:11:54,52 --> 0:11:57,76
where it is maybe excessive or
causing other issues.
224
0:11:57,88 --> 0:11:58,98
Is that what we're trying to say?
225
0:11:58,98 --> 0:12:3,84
So like harmful, yes, but like
it, it scales with the amount
226
0:12:3,84 --> 0:12:8,86
of time or the maybe like time
multiplied by the amount of churn
227
0:12:8,86 --> 0:12:9,52
in that time.
228
0:12:9,52 --> 0:12:9,84
Nik: Yeah.
229
0:12:9,84 --> 0:12:9,96
Yeah.
230
0:12:9,96 --> 0:12:10,24
Yeah.
231
0:12:10,24 --> 0:12:10,74
Database.
232
0:12:11,74 --> 0:12:16,52
I usually say you need like, it's
really hard to understand age
233
0:12:16,52 --> 0:12:20,4
and number of transactions, although
it's more representative
234
0:12:20,66 --> 0:12:22,16
metric in this context.
235
0:12:22,66 --> 0:12:26,16
If you say like long transaction
running like 3 hours during
236
0:12:26,16 --> 0:12:31,02
busy time on like at high traffic
is not the same as during weekend
237
0:12:31,02 --> 0:12:31,96
with low traffic, right?
238
0:12:31,96 --> 0:12:35,42
Because it might be even nothing
happened during 3 hours at weekend.
239
0:12:35,42 --> 0:12:40,04
Sometimes we see some applications
with very acute spikes of
240
0:12:40,04 --> 0:12:40,98
traffic, right?
241
0:12:41,12 --> 0:12:46,06
But, but so it's, there is no like
very clear connection between
242
0:12:46,06 --> 0:12:50,36
time and I, I name it XID consumption
or XID growth rate.
243
0:12:50,74 --> 0:12:55,9
So how fast real transaction ID
grows because this is what defines
244
0:12:56,1 --> 0:12:57,82
this xmin horizon age.
245
0:12:57,94 --> 0:13:0,56
And 1 of the contributors by the
way is savepoints, sub-transactions.
246
0:13:0,86 --> 0:13:5,28
If you use them a lot in 1 transaction,
you consume many real
247
0:13:5,28 --> 0:13:9,14
transaction IDs, because every
savepoint gets allocation of
248
0:13:9,14 --> 0:13:10,62
XID, real XID.
249
0:13:11,0 --> 0:13:15,8
So this is 1 of the dangers I identified,
that savepoints lead
250
0:13:15,8 --> 0:13:17,62
to higher consumption of XIDs.
251
0:13:18,68 --> 0:13:22,66
So yeah, and yeah, if it, of course,
like if we block a vacuum,
252
0:13:22,66 --> 0:13:26,82
it also, we need to be very accurate
with language here because
253
0:13:27,18 --> 0:13:30,52
I realized also when we say vacuum
is blocked, some people think
254
0:13:30,7 --> 0:13:32,28
it's blocked, it like fails.
255
0:13:32,78 --> 0:13:36,92
I even saw, so I have a new tool
to be released soon called PGBS
256
0:13:37,02 --> 0:13:41,5
detector, which is going to, and
we started using it internally
257
0:13:41,54 --> 0:13:46,48
to identify bullshit in our write-ups
and I saw RDS article from
258
0:13:46,48 --> 0:13:50,12
2019 and this was the only article
talking about hot_standby_feedback
259
0:13:50,12 --> 0:13:51,44
from RDS.
260
0:13:52,24 --> 0:13:53,08
Nothing else.
261
0:13:53,48 --> 0:13:56,92
So I tried to understand why they
keep default off and what's
262
0:13:56,92 --> 0:13:58,38
the reasoning behind it.
263
0:13:58,52 --> 0:14:4,14
So I saw that in that article,
this like very confusing statement,
264
0:14:4,28 --> 0:14:9,9
like vacuum is blocked and then
error like canceled statement,
265
0:14:10,2 --> 0:14:11,98
like it was vacuum statement canceled.
266
0:14:11,98 --> 0:14:17,38
No, when we say xmin horizon blockage
leads to affects vacuum
267
0:14:17,38 --> 0:14:22,48
negatively, It's not like vacuum
fails or cannot work at all.
268
0:14:22,48 --> 0:14:23,44
It works partially.
269
0:14:23,76 --> 0:14:29,24
It cleans up the dead tuples which
became dead before xmin horizon.
270
0:14:30,06 --> 0:14:34,24
And it just keeps and reports it
in the logs, tuples which it
271
0:14:34,24 --> 0:14:37,96
cannot clean as dead, already dead
but cannot be cleaned up yet.
272
0:14:37,96 --> 0:14:38,8
I don't remember.
273
0:14:38,8 --> 0:14:40,24
It cannot be removed, cannot be
deleted.
274
0:14:40,24 --> 0:14:41,34
I don't remember exactly.
275
0:14:42,04 --> 0:14:45,06
But this is what's useful to see
because you can see for each
276
0:14:45,06 --> 0:14:49,64
table, a vacuum visited, autovacuum
visited, you can see what
277
0:14:49,64 --> 0:14:51,74
is the damage for this.
278
0:14:52,08 --> 0:14:56,52
You can also run VACUUM VERBOSE
and VERBOSE will report such
279
0:14:56,52 --> 0:15:0,18
tuples which are dead, but cannot
be deleted because it's xmin horizon.
280
0:15:0,28 --> 0:15:2,72
It actually also reports xmin horizon.
281
0:15:3,34 --> 0:15:7,44
It just prints the transaction
ID and what's the age of it, which
282
0:15:7,44 --> 0:15:8,74
is very useful as well.
283
0:15:8,74 --> 0:15:11,4
So that's why autovacuum logging
should be enabled.
284
0:15:12,38 --> 0:15:16,22
And maybe not 0, which means all
occurrences of autovacuum,
285
0:15:16,32 --> 0:15:21,06
but maybe at least above a few
seconds, 10 seconds or 1 second,
286
0:15:21,34 --> 0:15:26,92
if you're concerned about some
very often executions, which are
287
0:15:26,92 --> 0:15:27,74
very fast.
288
0:15:27,8 --> 0:15:31,3
So anyway, autovacuum logs are
quite useful in this area.
289
0:15:31,88 --> 0:15:32,72
What's next?
290
0:15:33,26 --> 0:15:36,1
Michael: I think we've not really
even discussed what hot_stand
291
0:15:36,22 --> 0:15:37,58
by_feedback does.
292
0:15:37,68 --> 0:15:43,32
We've talked about the retention
of dead tuples while like something's
293
0:15:43,32 --> 0:15:48,26
happening, but hot_standby_feedback
comes in because it's something
294
0:15:48,26 --> 0:15:49,9
happening not on the primary, right?
295
0:15:49,9 --> 0:15:50,28
Like it's
296
0:15:50,28 --> 0:15:52,12
Nik: something happening on your
replica.
297
0:15:52,12 --> 0:15:53,54
Yeah, it's conflicts.
298
0:15:53,8 --> 0:15:57,54
So if you think how replication
works, physical replication,
299
0:15:57,56 --> 0:16:0,06
it replays WAL like it was recovery.
300
0:16:0,06 --> 0:16:3,48
It was built on top of recovery
from crashes, right?
301
0:16:3,82 --> 0:16:6,96
And the hot_standby_feedback is
just constantly replaying WAL
302
0:16:6,96 --> 0:16:8,3
like it was a recovery.
303
0:16:8,88 --> 0:16:13,34
That's why a function to check
is it primary or standby is called
304
0:16:13,34 --> 0:16:15,36
pg_is_in_recovery, which is confusing.
305
0:16:15,54 --> 0:16:16,04
Right?
306
0:16:16,68 --> 0:16:17,54
And it replays.
307
0:16:17,54 --> 0:16:18,16
WAL, good.
308
0:16:18,16 --> 0:16:22,58
But what if we have some query
ongoing, selecting from a huge
309
0:16:22,58 --> 0:16:24,06
table, for example, reporting,
right?
310
0:16:24,06 --> 0:16:24,96
It's pretty common.
311
0:16:24,96 --> 0:16:28,48
Let's offload heavy reporting queries
to read replicas.
312
0:16:29,24 --> 0:16:34,52
If it's running there and WAL has
instruction to clean up dead
313
0:16:34,66 --> 0:16:37,5
tuples which on the primary already
dead but we still reading
314
0:16:37,5 --> 0:16:38,0
them.
315
0:16:38,72 --> 0:16:42,76
This is 1 of conflicts and this
is snapshot conflict of replication
316
0:16:42,78 --> 0:16:47,54
conflicts with a query or query
or transaction conflicts with
317
0:16:47,54 --> 0:16:50,26
this WAL data right It cannot be
replayed.
318
0:16:50,94 --> 0:16:56,4
So depending on the type of physical
standby, 1 of 2 settings
319
0:16:56,68 --> 0:16:59,78
are used to decide what to do.
320
0:16:59,86 --> 0:17:1,72
So what to do is obvious.
321
0:17:2,08 --> 0:17:3,54
Replication is paused.
322
0:17:4,2 --> 0:17:5,26
It cannot be replayed.
323
0:17:7,42 --> 0:17:8,6
This is default option.
324
0:17:9,82 --> 0:17:13,04
So we wait until the end of this
transaction.
325
0:17:14,1 --> 0:17:15,78
Michael: And then we continue replaying.
326
0:17:17,22 --> 0:17:19,88
Nik: We wait until xmin horizon
shifts.
327
0:17:20,58 --> 0:17:22,54
If it shifts, we can replay it.
328
0:17:23,36 --> 0:17:26,86
So you can see it manually in pg_stat_
activity backend_xmin
329
0:17:27,26 --> 0:17:27,76
column.
330
0:17:28,14 --> 0:17:35,44
And also it's, yeah, So backend_
xmin is for each backend, you
331
0:17:35,44 --> 0:17:41,12
can see this local horizon for
each backend and minimum of them
332
0:17:41,12 --> 0:17:43,16
is our horizon for this node.
333
0:17:44,02 --> 0:17:46,74
It waits, it waits not forever.
334
0:17:47,62 --> 0:17:53,82
There is a setting I believe it's
called max_standby_streaming_
335
0:17:54,48 --> 0:17:54,98
delay.
336
0:17:55,86 --> 0:17:56,36
Yes,
337
0:17:56,74 --> 0:17:59,54
Michael: there's max_standby_streaming_
delay and max_standby_
338
0:17:59,54 --> 0:18:0,54
archive_delay.
339
0:18:0,72 --> 0:18:2,84
Nik: I will never remember all
GUCs.
340
0:18:2,84 --> 0:18:3,34
Yeah.
341
0:18:3,46 --> 0:18:6,76
So yeah, there are 2, 1 for streaming,
1 for WAL replay.
342
0:18:6,86 --> 0:18:10,74
Replicas which are consuming WALs
from archive, like S3 archive
343
0:18:10,74 --> 0:18:13,4
or something, according to restore_
command.
344
0:18:13,58 --> 0:18:17,44
And, but more common because they
have smaller lags is streaming
345
0:18:17,44 --> 0:18:22,36
replication, which connects to
the primary, unless we use cascade
346
0:18:22,36 --> 0:18:26,6
replication, and just streams the
WALs and applies them.
347
0:18:26,82 --> 0:18:29,98
And this is when we use max_standby_
streaming_delay.
348
0:18:30,86 --> 0:18:32,72
By default, I think it's 30 seconds.
349
0:18:33,54 --> 0:18:35,42
It waits a maximum of 30 seconds.
350
0:18:36,22 --> 0:18:42,04
After this, Postgres says replication
is more important and just
351
0:18:42,04 --> 0:18:43,72
cancels the query.
352
0:18:44,54 --> 0:18:47,76
And the 1st thing that happens,
people don't like queries being
353
0:18:47,76 --> 0:18:48,48
canceled, right?
354
0:18:48,48 --> 0:18:51,24
Or important reports.
355
0:18:51,6 --> 0:18:53,94
Michael: Especially sometimes long
ones, like if it's a long
356
0:18:53,94 --> 0:18:57,44
reporting query, you've already
waited 10 minutes for it.
357
0:18:57,44 --> 0:18:57,66
Nik: Right.
358
0:18:57,66 --> 0:19:1,26
And that's very visible because
reporting usually is close to
359
0:19:1,26 --> 0:19:3,74
decision makers, So they start
complaining very fast.
360
0:19:3,74 --> 0:19:5,64
Like, why report was not delivered?
361
0:19:5,64 --> 0:19:7,68
Okay, we have conflicts.
362
0:19:7,7 --> 0:19:8,2
Canceled.
363
0:19:8,46 --> 0:19:13,08
Okay, let's increase this to 3
minutes, 30 minutes, 3 hours a
364
0:19:13,08 --> 0:19:13,58
day.
365
0:19:14,44 --> 0:19:15,34
And it's okay.
366
0:19:15,9 --> 0:19:16,4
Yeah.
367
0:19:16,94 --> 0:19:18,96
At extreme, you can set minus 1.
368
0:19:19,62 --> 0:19:23,98
And so replication will wait until,
369
0:19:24,28 --> 0:19:24,78
Michael: yeah.
370
0:19:25,32 --> 0:19:26,04
So what happens now?
371
0:19:26,04 --> 0:19:28,38
And this whole time, I think it's
really important to stress,
372
0:19:28,38 --> 0:19:33,28
this whole time anything newer
than that conflict is not getting
373
0:19:33,28 --> 0:19:35,4
replayed on the replica.
374
0:19:35,46 --> 0:19:39,96
So no, any new inserts, any updates,
any deletes, nothing's coming
375
0:19:39,96 --> 0:19:40,46
through
376
0:19:40,96 --> 0:19:42,44
Nik: until that query finishes.
377
0:19:42,44 --> 0:19:43,055
Yeah, it's global.
378
0:19:43,055 --> 0:19:43,84
Horizon was global.
379
0:19:43,94 --> 0:19:46,42
Replication is just single process
actually.
380
0:19:46,72 --> 0:19:49,58
You can see a startup process in
top or ps.
381
0:19:50,02 --> 0:19:51,94
And this is what applies changes.
382
0:19:52,2 --> 0:19:55,76
By the way, changes might be received
already because the replication
383
0:19:55,76 --> 0:19:56,5
has stages.
384
0:19:56,68 --> 0:20:0,06
They can be flushed to local pg_wal
directory, but they just
385
0:20:0,06 --> 0:20:3,82
cannot be applied because somebody
needs very old, all the data,
386
0:20:4,0 --> 0:20:6,86
which we already want to clean
up.
387
0:20:7,8 --> 0:20:8,3
Right.
388
0:20:8,8 --> 0:20:11,92
So what happens next?
389
0:20:12,5 --> 0:20:13,54
Reports work.
390
0:20:13,62 --> 0:20:17,64
For example, we set 3 hours, All
reports last no more than 1
391
0:20:17,64 --> 0:20:18,12
hour.
392
0:20:18,12 --> 0:20:18,98
We are good.
393
0:20:19,74 --> 0:20:25,06
But then, usually what happens,
some users started to complain.
394
0:20:25,24 --> 0:20:26,1
Stale data.
395
0:20:27,74 --> 0:20:30,96
And if your load balancer is not
smart enough, If you have a
396
0:20:30,96 --> 0:20:35,44
smart load balancer, by the way,
you mentioned Postgres 19 has
397
0:20:35,44 --> 0:20:37,24
a wait for LSN feature, right?
398
0:20:37,24 --> 0:20:39,3
We probably should talk about it
separately.
399
0:20:39,44 --> 0:20:41,66
I have a lot to talk about this.
400
0:20:41,76 --> 0:20:45,42
But smart load balancing understands
that some replica is lagging
401
0:20:45,9 --> 0:20:47,28
and stops using it.
402
0:20:48,7 --> 0:20:52,44
Just because we don't want to deliver
stale data to our clients.
403
0:20:52,66 --> 0:20:57,1
But if you have pretty basic load
balancing and some either queries
404
0:20:57,34 --> 0:21:2,72
or transactions went to read replica,
It takes some time because
405
0:21:3,48 --> 0:21:7,08
usually who suffers is not decision
makers like management of
406
0:21:7,08 --> 0:21:9,72
a company wanting reports, but
some users.
407
0:21:9,88 --> 0:21:14,64
And they also usually not every
user is willing to waste energy
408
0:21:14,64 --> 0:21:16,16
to report problems, right?
409
0:21:16,16 --> 0:21:18,3
It takes some time usually, that's
a problem.
410
0:21:18,82 --> 0:21:21,66
Michael: It takes time and also
you have to rule out other issues
411
0:21:21,66 --> 0:21:22,2
1st, right?
412
0:21:22,2 --> 0:21:24,44
Like you have to make sure it's
not on your side.
413
0:21:24,44 --> 0:21:27,58
Like there's just because something
seems like it's not working
414
0:21:27,64 --> 0:21:30,36
doesn't you don't automatically
assume it's a service.
415
0:21:30,9 --> 0:21:33,7
Nik: Yeah And what I'm describing
is a very common situation,
416
0:21:34,24 --> 0:21:39,66
which I went through in 2006 or
7 and just thought it's solved.
417
0:21:39,8 --> 0:21:43,58
But due to defaults, especially
defaults on managed Postgres
418
0:21:43,58 --> 0:21:48,2
platforms, which keep hot_standby_feedback
off, This is what's happening.
419
0:21:48,48 --> 0:21:54,4
So people increase max_standby_
streaming_delay and then they
420
0:21:54,4 --> 0:21:58,22
just say, okay, we have replication
lags.
421
0:21:58,22 --> 0:22:1,34
And they start thinking why, because
it's not obvious why.
422
0:22:1,56 --> 0:22:6,26
It's not really not obvious, because
yeah, it's just lags and
423
0:22:6,26 --> 0:22:6,94
so on.
424
0:22:7,66 --> 0:22:12,52
Michael: But so should we switch,
should we move to then why
425
0:22:12,52 --> 0:22:16,08
we now have hot_standby_feedback
and what it does?
426
0:22:16,1 --> 0:22:20,2
Nik: So hot_standby_feedback reports
xmin horizon observed on
427
0:22:20,2 --> 0:22:21,38
replica to the primary.
428
0:22:21,82 --> 0:22:27,02
So the primary can involve it in
calculation of the final xmin horizon
429
0:22:27,44 --> 0:22:32,12
used by Vacuum, deciding what can
be cleaned safely, what still
430
0:22:32,12 --> 0:22:33,22
cannot be cleaned.
431
0:22:33,68 --> 0:22:40,58
So if you had 1 hour query on the
primary, which led to bloat
432
0:22:41,2 --> 0:22:45,02
sometimes, right, because xmin
horizon again, global, it
433
0:22:45,02 --> 0:22:45,74
was bloat.
434
0:22:45,86 --> 0:22:50,4
And then you offloaded to read
replica without hot_standby_feedback,
435
0:22:50,4 --> 0:22:54,3
you have lags up to 1 hour because
your queries are 1 hour.
436
0:22:54,52 --> 0:22:56,02
And it's terrible lag, right?
437
0:22:57,04 --> 0:23:2,8
Or when you switch to hot_standby_feedback
to on, you have the problem
438
0:23:2,8 --> 0:23:6,14
like the horizon being reported
and you have the similar situation
439
0:23:6,22 --> 0:23:8,8
as it was executed on the primary
itself.
440
0:23:9,34 --> 0:23:10,02
Matthew 14.
441
0:23:11,12 --> 0:23:14,82
Michael: And I think the critical
thing to mention is the chance
442
0:23:14,82 --> 0:23:18,56
of you then getting conflicts are
massively reduced because the
443
0:23:18,56 --> 0:23:23,8
primary won't have cleaned up those
dead tuples and therefore
444
0:23:24,18 --> 0:23:28,94
won't send those conflicts through
to the replica until it's
445
0:23:28,94 --> 0:23:30,32
finished what it was doing.
446
0:23:30,42 --> 0:23:33,92
So it avoids that case we were
just talking about in most cases.
447
0:23:34,02 --> 0:23:35,54
Nik: That's why lag doesn't happen.
448
0:23:35,54 --> 0:23:37,18
So conflict leads to lag.
449
0:23:37,54 --> 0:23:39,38
Conflict leads to 2 things.
450
0:23:39,38 --> 0:23:43,08
1st, lag of replication, and when
it's too much lag, then cancelling
451
0:23:43,22 --> 0:23:44,62
the source of the problem.
452
0:23:44,64 --> 0:23:45,14
Query.
453
0:23:45,72 --> 0:23:46,22
Right?
454
0:23:46,7 --> 0:23:49,32
So, don't block xmin horizon Progress.
455
0:23:49,66 --> 0:23:51,2
So, you are right.
456
0:23:51,22 --> 0:23:55,64
I like that you used reduce number
of conflicts because it doesn't.
457
0:23:55,64 --> 0:23:56,14
Yeah.
458
0:23:56,82 --> 0:24:0,02
For example, we talked about the
conflict when we need to clean
459
0:24:0,02 --> 0:24:4,46
up, but tuples still needed there,
Like this is a snapshot conflict,
460
0:24:4,46 --> 0:24:6,36
but there might be also a conflict.
461
0:24:6,38 --> 0:24:9,44
When we change schema, we need
ACCESS EXCLUSIVE lock.
462
0:24:9,52 --> 0:24:13,46
And the mistake is to keep this
ACCESS EXCLUSIVE lock for long.
463
0:24:15,06 --> 0:24:18,52
And on the primary, it leads to
many problems.
464
0:24:18,52 --> 0:24:21,02
We know like some selects even
will wait.
465
0:24:21,02 --> 0:24:23,34
It's ACCESS EXCLUSIVE lock, blocks
even selects.
466
0:24:23,68 --> 0:24:24,48
Schema change.
467
0:24:24,48 --> 0:24:28,52
So if you open transaction, edit
a column very briefly, and then
468
0:24:28,52 --> 0:24:30,26
start to read something from somewhere.
469
0:24:30,48 --> 0:24:32,68
You keep the lock until very end
of transactions.
470
0:24:32,68 --> 0:24:35,96
We discussed many times lock cannot
be released midway.
471
0:24:36,02 --> 0:24:37,54
It's released only at the end.
472
0:24:37,54 --> 0:24:42,38
If you do something else, this
lock is held and nobody can work
473
0:24:42,38 --> 0:24:43,3
with that table.
474
0:24:43,32 --> 0:24:46,74
But on the replica also, if you
keep this lock on the primary.
475
0:24:47,08 --> 0:24:52,44
WAL comes saying lock and again,
lag will happen.
476
0:24:52,44 --> 0:24:54,94
And hot_standby_feedback doesn't
solve this at all.
477
0:24:55,76 --> 0:24:56,66
This should be clear.
478
0:24:56,66 --> 0:24:58,48
So yeah, you're very good warning.
479
0:24:58,48 --> 0:24:59,06
I liked it.
480
0:24:59,06 --> 0:25:0,52
It's not solving fully.
481
0:25:0,8 --> 0:25:3,7
Even more, I learned it only recently.
482
0:25:4,64 --> 0:25:9,64
So if you usually use standard
replicas, these days I used streaming
483
0:25:9,64 --> 0:25:11,62
replication plus replication slots.
484
0:25:11,68 --> 0:25:15,36
Replication slots were created
later than streaming replication.
485
0:25:15,36 --> 0:25:16,9
It's like additional level.
486
0:25:17,36 --> 0:25:21,7
And you can use streaming replication
without slots.
487
0:25:21,7 --> 0:25:26,26
Usually people think replication
slots are protection from WALs
488
0:25:26,58 --> 0:25:28,16
being deleted from the primary.
489
0:25:28,44 --> 0:25:31,78
The replica lags too much, it cannot
converge anymore, right?
490
0:25:31,92 --> 0:25:35,38
It's solved usually if you have
good backups in S3, object storage,
491
0:25:35,38 --> 0:25:39,02
you can configure restore_command
on the replica and it will
492
0:25:39,02 --> 0:25:40,84
just take WALs from there, no
problem.
493
0:25:40,84 --> 0:25:42,64
So slots can be like mitigated.
494
0:25:42,9 --> 0:25:45,26
Also slots are convenient for observability.
495
0:25:45,72 --> 0:25:49,12
But what I also learned, imagine
we have streaming replication
496
0:25:49,12 --> 0:25:49,96
without slot.
497
0:25:50,28 --> 0:25:55,24
Slots have xmin horizon reporting
right in pg_replication_slots,
498
0:25:55,24 --> 0:25:57,52
you can see it, xmin, column.
499
0:25:57,98 --> 0:26:1,1
But if you don't have slot, streaming
replication, It works all
500
0:26:1,1 --> 0:26:1,6
good.
501
0:26:1,72 --> 0:26:5,1
But then some network issue, replica
disconnected.
502
0:26:5,46 --> 0:26:8,68
On the primary, for example, vacuum
deleted tuples, reconnection
503
0:26:8,82 --> 0:26:12,48
happened, vacuum already deleted
tuples and WAL is delivered,
504
0:26:12,54 --> 0:26:13,68
tuples are deleted.
505
0:26:14,16 --> 0:26:18,28
And despite hot_standby_feedback
being on, we have situation like
506
0:26:18,28 --> 0:26:19,26
it was off.
507
0:26:20,28 --> 0:26:23,1
This was an interesting finding
for me last week when I dived
508
0:26:23,1 --> 0:26:28,14
into this topic deeper, using our
new tool for experiments, reproduced
509
0:26:28,14 --> 0:26:30,56
a lot of failure scenarios, including
this 1.
510
0:26:30,72 --> 0:26:31,76
So it's interesting.
511
0:26:32,08 --> 0:26:35,28
It means that slots are useful
in many different cases also,
512
0:26:35,28 --> 0:26:36,84
like in sensors, right?
513
0:26:37,54 --> 0:26:42,34
So yeah, hot_standby_feedback
on drastically reduce the chance
514
0:26:42,34 --> 0:26:44,56
that you will have lag on standby.
515
0:26:45,06 --> 0:26:48,68
And this is what we want for the
replicas because we want them
516
0:26:48,68 --> 0:26:51,16
to be up to date as much as possible.
517
0:26:51,98 --> 0:26:53,9
Michael: But it also does 1 other
thing that I think is quite
518
0:26:53,9 --> 0:26:54,4
important.
519
0:26:54,94 --> 0:26:58,4
It should also drastically reduce
the number of cancellations
520
0:26:58,68 --> 0:27:1,12
you get if you have these long
running queries.
521
0:27:1,4 --> 0:27:4,58
Let's say longer than 30 seconds
or whatever you set that timing
522
0:27:4,64 --> 0:27:9,44
to, it should reduce those getting
cancelled because the conflicts
523
0:27:9,44 --> 0:27:10,32
aren't going through.
524
0:27:10,32 --> 0:27:11,76
Because you're not getting conflicts
in the
525
0:27:11,76 --> 0:27:12,7
Nik: 1st place, right?
526
0:27:13,5 --> 0:27:13,9742
Right.
527
0:27:13,9742 --> 0:27:18,74
Cancellation is a result of delay,
which is a result of conflict.
528
0:27:18,8 --> 0:27:23,7
So conflict, delay or replication
lag, and then cancellation
529
0:27:23,82 --> 0:27:25,26
when it's too high.
530
0:27:25,96 --> 0:27:27,6
Value of lag is too high.
531
0:27:28,34 --> 0:27:33,14
So that's why I think read replicas
should have hot_standby_feedback
532
0:27:33,14 --> 0:27:37,52
on and reasoning that it will be
worse because they will lead
533
0:27:37,52 --> 0:27:38,04
to bloat.
534
0:27:38,04 --> 0:27:40,6
This is just how Postgres works.
535
0:27:40,6 --> 0:27:44,24
It was usually people start with
all queries going to primary,
536
0:27:44,24 --> 0:27:46,16
then they offload queries to replica.
537
0:27:46,16 --> 0:27:49,28
It's just a mistake to expect that
those queries won't affect
538
0:27:49,28 --> 0:27:50,14
vacuum behavior.
539
0:27:51,14 --> 0:27:51,88
That's it.
540
0:27:51,9 --> 0:27:54,26
Of course, you can live with off.
541
0:27:55,16 --> 0:27:56,54
Actually, 2 points here.
542
0:27:56,54 --> 0:27:58,12
1st point is quite interesting.
543
0:27:59,06 --> 0:28:7,06
So we want to minimize damage of
long-running transactions which
544
0:28:7,06 --> 0:28:10,8
block xmin horizon progress, and
how can we do it?
545
0:28:10,8 --> 0:28:16,56
We can set transaction_timeout,
both on primary and all read
546
0:28:16,56 --> 0:28:17,06
replicas.
547
0:28:17,32 --> 0:28:20,96
For example, if we set it to 3
hours, that's it.
548
0:28:21,1 --> 0:28:22,7
No more 3 hours of damage.
549
0:28:22,74 --> 0:28:25,02
But as we discussed, damage is
relative.
550
0:28:25,14 --> 0:28:28,76
3 hours, we don't know how many
transactions happened, right?
551
0:28:29,2 --> 0:28:33,34
I think actually it would make
sense, it might make sense to
552
0:28:33,34 --> 0:28:35,2
have all timeouts.
553
0:28:35,46 --> 0:28:38,4
We have transaction_timeout, we
have statement_timeout, and
554
0:28:38,4 --> 0:28:39,9
idle_in_transaction_session_timeout.
555
0:28:40,64 --> 0:28:43,22
transaction_timeout limits whole
transaction, which is good.
556
0:28:43,26 --> 0:28:47,86
But I think it might make sense
either to have all these 3 measured
557
0:28:47,9 --> 0:28:52,32
in not in milliseconds or seconds,
but in a transaction count.
558
0:28:52,86 --> 0:28:56,5
Imagine transaction_timeout, but
count of transactions, right?
559
0:28:56,74 --> 0:29:1,72
Or adjust hot_standby_feedback so
it would be not like on and
560
0:29:1,72 --> 0:29:5,72
off, but some threshold after which
we cancel.
561
0:29:6,28 --> 0:29:9,14
So there is definitely room for
improvement here.
562
0:29:9,14 --> 0:29:13,92
And if you look, my AI told me
other database systems ship settings
563
0:29:14,22 --> 0:29:14,94
for replicas.
564
0:29:15,54 --> 0:29:18,96
Postgres ships 2 extremes, on and
off.
565
0:29:20,14 --> 0:29:23,86
But with transaction_timeout, at
least measured in seconds, right?
566
0:29:23,86 --> 0:29:27,62
It's indirect, but it's already
good enough in many cases, which
567
0:29:27,62 --> 0:29:29,42
was implemented with Postgres 17.
568
0:29:30,14 --> 0:29:32,22
So on old Postgres it's not available.
569
0:29:32,78 --> 0:29:33,9
But it's good enough.
570
0:29:33,9 --> 0:29:37,62
My recommendation, on and 3 hours
transaction_timeout.
571
0:29:39,0 --> 0:29:42,42
So nobody could leave transaction
open which blocks xmin horizon
572
0:29:42,5 --> 0:29:44,08
progress for a very long time.
573
0:29:44,16 --> 0:29:46,96
But you need to do it on the primary
as well, right?
574
0:29:46,96 --> 0:29:49,14
Because who knows what happens
there.
575
0:29:49,28 --> 0:29:50,88
It's not no difference here.
576
0:29:51,74 --> 0:29:57,22
Michael: I think the big difference
is people not offloading
577
0:29:57,36 --> 0:30:0,06
just like OLTP traffic to a replica.
578
0:30:0,06 --> 0:30:3,82
I think it's people think that
I've got a replica, I can send
579
0:30:3,82 --> 0:30:7,2
my reporting or analytics queries
there or I can give a data
580
0:30:7,2 --> 0:30:11,08
team access to that and it doesn't
matter, it protects the primary.
581
0:30:11,08 --> 0:30:16,32
I think the key learning from this
setting is That's not true.
582
0:30:17,54 --> 0:30:22,02
You have to be, it can still affect
the primary and therefore
583
0:30:22,02 --> 0:30:25,7
you have to factor that in on almost
like an architectural level.
584
0:30:25,76 --> 0:30:28,78
Should you even be running those
reporting queries there if it
585
0:30:28,78 --> 0:30:32,84
on an OLTP, like an important OLTP
system or should you not do
586
0:30:32,84 --> 0:30:33,34
that?
587
0:30:33,4 --> 0:30:35,7
I think it raises those important
questions.
588
0:30:36,42 --> 0:30:36,92
Nik: Yeah.
589
0:30:37,66 --> 0:30:41,68
For reporting replicas, maybe you
should keep it off, but the
590
0:30:41,68 --> 0:30:45,46
consequences are very often replication
lags.
591
0:30:45,86 --> 0:30:49,24
And I even say that it makes like
basically this replication
592
0:30:49,28 --> 0:30:54,92
replica, like single user, like
you take around very long query,
593
0:30:54,92 --> 0:30:57,8
all other users like suffer and
cannot use it anymore.
594
0:30:57,8 --> 0:30:59,64
Like they say, oh, it's very old
data.
595
0:30:59,64 --> 0:31:0,8
We need fresh data.
596
0:31:1,28 --> 0:31:6,26
So like you become very like expensive
user, but maybe it's fine
597
0:31:6,26 --> 0:31:10,64
in some cases, like maybe it's
better to do that and then catch
598
0:31:10,64 --> 0:31:11,14
up.
599
0:31:11,64 --> 0:31:13,3
I don't know, but it's quite expensive.
600
0:31:13,94 --> 0:31:17,78
It's quite expensive to have a
whole node for reports, which,
601
0:31:17,84 --> 0:31:19,84
and this node is lagging and catches
up.
602
0:31:19,84 --> 0:31:20,42
I don't know.
603
0:31:20,42 --> 0:31:23,68
Michael: Like it's, but some, some
people do like shipping it
604
0:31:23,68 --> 0:31:24,5
to like ClickHouse.
605
0:31:24,6 --> 0:31:26,46
We did whole episodes on this,
didn't we?
606
0:31:26,46 --> 0:31:28,76
And I think there's some interesting
alternatives.
607
0:31:29,34 --> 0:31:31,8
Nik: But this is not really a replica,
it's an analytical replica,
608
0:31:31,8 --> 0:31:31,96
right?
609
0:31:31,96 --> 0:31:36,94
Or if you have different storage,
there are some, not Postgres,
610
0:31:36,94 --> 0:31:40,02
but some alternative to Postgres,
which stores data differently
611
0:31:40,12 --> 0:31:42,14
and executes queries much faster.
612
0:31:42,66 --> 0:31:46,56
In this case, maybe hot_standby_feedback
on is still good because
613
0:31:46,56 --> 0:31:48,82
negative effect to vacuum will
be low, right?
614
0:31:49,24 --> 0:31:52,08
I can imagine some cases where
off is good.
615
0:31:52,08 --> 0:31:54,06
For example, if it's a delayed
replica.
616
0:31:55,2 --> 0:31:59,54
Some people keep 8 hours, 12 hours
delayed replica, which replays
617
0:31:59,54 --> 0:32:4,84
WAL with delay 10, 12 hours to
be able to very quickly restore
618
0:32:5,38 --> 0:32:7,86
point-in-time recovery, to perform
point-in-time recovery.
619
0:32:8,14 --> 0:32:11,78
I think this recipe is quite outdated
because now we have snapshots
620
0:32:11,82 --> 0:32:15,66
and if even with lazy load, it's
quite fast to provision multi-terabyte
621
0:32:15,88 --> 0:32:18,06
databases from cloud snapshots.
622
0:32:18,9 --> 0:32:22,36
But if you use like delayed replica,
of course you don't want
623
0:32:22,36 --> 0:32:23,16
hot_standby_feedback.
624
0:32:23,16 --> 0:32:25,68
I actually think it's not possible
to use it there because hot_stand
625
0:32:25,68 --> 0:32:28,3
by_feedback works only with streaming
replication, right?
626
0:32:28,925 --> 0:32:36,5
So with archive replicas which
work using restore_command, replaying
627
0:32:36,5 --> 0:32:38,5
WALs from archive or from some place.
628
0:32:39,24 --> 0:32:41,18
hot_standby_feedback cannot be applied
there.
629
0:32:41,76 --> 0:32:45,48
And you cannot see them in replication
slots, so these replicas
630
0:32:45,48 --> 0:32:46,02
are invisible.
631
0:32:46,02 --> 0:32:48,54
And this is how delayed replicas
should work.
632
0:32:48,62 --> 0:32:51,88
And in this case, you'll be dealing
with the same mechanics,
633
0:32:52,0 --> 0:32:59,35
but defined by max_standby, not
streaming, but what the other
634
0:32:59,35 --> 0:32:59,84
alternative?
635
0:32:59,84 --> 0:33:0,34
Shipping.
636
0:33:1,08 --> 0:33:5,46
Archive delay, max_standby_archive_
delay.
637
0:33:5,46 --> 0:33:5,96
Right.
638
0:33:6,46 --> 0:33:10,3
So I think this is, this covers
quite well this topic.
639
0:33:10,4 --> 0:33:14,88
I think Postgres has opportunity
to, to improve things drastically,
640
0:33:15,04 --> 0:33:16,68
like for better control.
641
0:33:17,38 --> 0:33:22,34
I wish like I had a capability
to define the damage, not in seconds,
642
0:33:22,34 --> 0:33:28,4
but precise, like maximum, like
100000 transaction IDs, yeah.
643
0:33:29,18 --> 0:33:34,28
And if it happens, if somebody
exceeds it, I want to cancel that
644
0:33:34,28 --> 0:33:34,78
1.
645
0:33:34,92 --> 0:33:38,66
And for reporting, the most standard
approach for large databases,
646
0:33:38,86 --> 0:33:43,1
I agree with you, it remains another
database system and you
647
0:33:43,1 --> 0:33:47,46
need to create some pipeline, like
a logical replication pipeline
648
0:33:47,56 --> 0:33:51,36
to ship data to ClickHouse or anything
else like Snowflake.
649
0:33:51,82 --> 0:33:52,32
Michael: Yeah.
650
0:33:52,42 --> 0:33:52,66
Yeah.
651
0:33:52,66 --> 0:33:53,86
We could monitor for that.
652
0:33:53,86 --> 0:33:55,22
Like you can do that.
653
0:33:55,32 --> 0:33:56,98
You can build that yourself, right?
654
0:33:56,98 --> 0:34:2,5
If you monitor for things holding
back the xmin horizon and you
655
0:34:2,5 --> 0:34:5,78
can check the age in like transaction
IDs.
656
0:34:5,86 --> 0:34:9,92
So that is that's already possible
today right but we just have
657
0:34:9,92 --> 0:34:12,74
to do it ourselves rather than
a configuration parameter.
658
0:34:14,24 --> 0:34:19,86
Nik: Yeah so yeah you are talking
about some automation which
659
0:34:19,86 --> 0:34:23,14
will be outside Postgres but it
will demonstrate all 5 reasons
660
0:34:23,14 --> 0:34:25,96
for xmin horizon being blocked,
identify them and then cancel
661
0:34:25,96 --> 0:34:26,46
them.
662
0:34:26,54 --> 0:34:27,14
Then 5.
663
0:34:27,74 --> 0:34:28,24
Michael: Yeah.
664
0:34:28,66 --> 0:34:29,16
Nik: Yeah.
665
0:34:29,2 --> 0:34:32,88
You like you're talking about self-driving
ideas, but it shouldn't
666
0:34:32,88 --> 0:34:34,12
be in Postgres in my opinion.
667
0:34:34,12 --> 0:34:35,78
This is, this is the wrong example.
668
0:34:36,02 --> 0:34:40,68
This logic is too basic, too fundamental
to be, it's possible
669
0:34:40,68 --> 0:34:41,78
to implement outside.
670
0:34:42,34 --> 0:34:42,84
Definitely.
671
0:34:42,9 --> 0:34:46,56
I remember actually many times
I implemented like something in
672
0:34:46,56 --> 0:34:50,02
pg_cron or regular cron, like
canceling long running transactions,
673
0:34:50,02 --> 0:34:51,68
which are like offensive.
674
0:34:52,72 --> 0:34:54,72
Looking at backend_xmin
and so on.
675
0:34:54,72 --> 0:34:56,02
Yeah, we did it actually.
676
0:34:56,14 --> 0:34:59,0
And this is like duct taping, right?
677
0:34:59,64 --> 0:35:0,46
Michael: Yeah, sure.
678
0:35:0,92 --> 0:35:5,58
Nik: This is not, but I'm talking
like, I just feel, I feel the
679
0:35:5,58 --> 0:35:10,02
need in Postgres itself could be
improved, maybe some listener
680
0:35:10,9 --> 0:35:12,28
should propose some patches.
681
0:35:12,88 --> 0:35:15,8
Michael: I think it would be tough
to, I think this is a really
682
0:35:15,8 --> 0:35:18,14
tough 1 because it's a genuine
trade off.
683
0:35:18,24 --> 0:35:22,72
I don't feel like there's a right
solution and it's going to
684
0:35:22,72 --> 0:35:27,84
depend on what you're using your
replica for and maybe there's
685
0:35:27,84 --> 0:35:32,9
like a case that 95% of people
want and like therefore we should
686
0:35:32,9 --> 0:35:33,98
just change it.
687
0:35:34,04 --> 0:35:37,8
But it feels to me like some people
do prefer the trade-off of
688
0:35:37,8 --> 0:35:40,38
having it on and some people prefer
the trade-off of having it
689
0:35:40,38 --> 0:35:40,88
off.
690
0:35:41,18 --> 0:35:45,36
And it's maybe tricky, gonna be
hard to find something everybody's
691
0:35:45,48 --> 0:35:46,56
actually happy with.
692
0:35:47,12 --> 0:35:51,04
Nik: Yeah, actually, I think for,
yeah, this is, yeah, I think
693
0:35:51,04 --> 0:35:54,18
to leave the choice on the user
who provisions read replica,
694
0:35:54,24 --> 0:35:55,52
it is also a good idea.
695
0:35:57,72 --> 0:36:1,16
By the way, I've read, I saw many
hackers proposed to switch
696
0:36:1,16 --> 0:36:3,3
default in Postgres itself to on.
697
0:36:3,64 --> 0:36:7,0
There was such opinion, very strong
1, multiple, very well-known
698
0:36:7,02 --> 0:36:7,86
hackers proposed it.
699
0:36:7,86 --> 0:36:11,32
But then it was a discussion, it
was opposition to it.
700
0:36:11,32 --> 0:36:15,06
And by the way, at that time,
transaction_timeout didn't exist.
701
0:36:15,06 --> 0:36:17,06
So maybe now it should be reconsidered.
702
0:36:17,44 --> 0:36:17,9
Yeah.
703
0:36:17,9 --> 0:36:19,18
It was before Postgres 17.
704
0:36:19,82 --> 0:36:24,78
And the reason I remember, which
like hits in my mind brightly,
705
0:36:25,08 --> 0:36:30,22
that it was named that problems
with lags and conflicts canceled,
706
0:36:30,66 --> 0:36:33,62
canceled like lags and canceled
queries on replica.
707
0:36:34,06 --> 0:36:34,56
Yes.
708
0:36:34,94 --> 0:36:36,24
Very obvious to you.
709
0:36:36,26 --> 0:36:39,1
And it's better to discover what's
happening, understand and
710
0:36:39,1 --> 0:36:43,32
then make decision based on your
situation and experience rather
711
0:36:43,32 --> 0:36:47,7
than you don't notice or bloat.
712
0:36:48,32 --> 0:36:48,84
Bloat on
713
0:36:48,84 --> 0:36:50,88
Michael: the primary building up
slowly.
714
0:36:51,04 --> 0:36:54,02
Nik: But at the same time, the
same problem exists on the primary
715
0:36:54,02 --> 0:36:54,52
itself.
716
0:36:54,64 --> 0:36:56,14
Absolutely the same, right?
717
0:36:57,18 --> 0:36:59,42
Michael: And I think you can make
the opposite argument.
718
0:37:0,92 --> 0:37:2,88
Bloat is quieter.
719
0:37:3,22 --> 0:37:5,6
The reason you don't notice bloat
is because maybe it's not as
720
0:37:5,6 --> 0:37:5,92
bad.
721
0:37:5,92 --> 0:37:7,86
Nik: Yeah, we should control bloat
anyway.
722
0:37:8,08 --> 0:37:10,1
We should control bloat anyway,
right?
723
0:37:10,2 --> 0:37:13,48
And on the primary it happens exactly
in the same mechanism,
724
0:37:13,52 --> 0:37:17,6
so why should we make different
approach, apply different approach
725
0:37:17,6 --> 0:37:18,66
to replica.
726
0:37:18,74 --> 0:37:20,9
And bloat control is a whole topic
anyway, right?
727
0:37:20,9 --> 0:37:23,72
If you want to improve that experience,
it should be done on
728
0:37:23,72 --> 0:37:24,7
the primary 1st.
729
0:37:25,42 --> 0:37:29,14
Michael: And if we care about that
from a Postgres tuning perspective,
730
0:37:29,2 --> 0:37:32,36
we should probably be changing
the defaults of the various auto
731
0:37:32,36 --> 0:37:33,62
vacuum settings 1st.
732
0:37:33,64 --> 0:37:37,04
Nik: You know what, I like how
I'm pulling you into discussion
733
0:37:37,12 --> 0:37:37,74
and hackers.
734
0:37:37,84 --> 0:37:40,46
You should think and write your
opinion next time.
735
0:37:41,6 --> 0:37:43,24
Michael: Yeah, I'll try and get
braver.
736
0:37:44,2 --> 0:37:46,66
Nik: Yeah, it's a rabbit hole,
I know.
737
0:37:48,28 --> 0:37:48,78
Yeah.
738
0:37:49,7 --> 0:37:51,74
Michael: Anyway, this was great
and helpful.
739
0:37:51,74 --> 0:37:56,5
And I think I agree with you that
it feels like the vast majority
740
0:37:56,82 --> 0:38:1,02
of OLTP replicas where you're just
offloading a huge number of
741
0:38:1,02 --> 0:38:4,7
really quick queries, hot_stand
by_feedback On makes so much more
742
0:38:4,7 --> 0:38:6,22
sense to me than Off.
743
0:38:6,62 --> 0:38:10,28
But I can't agree that like all
read replicas should have it
744
0:38:10,28 --> 0:38:10,46
off.
745
0:38:10,46 --> 0:38:14,06
Like I do think there are these
people using it, probably still
746
0:38:14,06 --> 0:38:18,16
it's a good architectural choice
as these analytics replicas
747
0:38:18,48 --> 0:38:21,0
and I think you're right that it
makes sense to keep those off
748
0:38:21,0 --> 0:38:23,86
and it's nice that you can do it
on a per replica basis like
749
0:38:23,86 --> 0:38:26,48
it doesn't have to be the same
for all of your replicas which
750
0:38:26,48 --> 0:38:27,14
is cool.
751
0:38:27,44 --> 0:38:27,94
Yeah.
752
0:38:28,7 --> 0:38:29,2
Good.
753
0:38:29,38 --> 0:38:29,88
Awesome.
754
0:38:29,96 --> 0:38:30,78
Anything else?
755
0:38:31,1 --> 0:38:31,6
Nik: No.
756
0:38:31,88 --> 0:38:35,94
Just don't let your replica to
lag too often.
757
0:38:36,22 --> 0:38:37,04
Michael: Nice 1, Nik.
758
0:38:37,04 --> 0:38:37,9
Thanks so much.
0:0:0,06 --> 0:0:0,59999996
Nik: Hello, hello.
2
0:0:0,59999996 --> 0:0:1,92
This is Postgres.FM.
3
0:0:1,92 --> 0:0:3,84
My name is Nik, PostgresAI.
4
0:0:3,84 --> 0:0:7,3199997
And as usual with me, Michael pgMustard.
5
0:0:7,68 --> 0:0:8,5
Hi, Michael.
6
0:0:9,48 --> 0:0:10,42
Michael: Hello, Nik.
7
0:0:10,9 --> 0:0:13,82
Nik: I proposed to discuss hot_standby_feedback.
8
0:0:14,599999 --> 0:0:18,04
And that's interesting because
I have 4 customers with this problem
9
0:0:18,04 --> 0:0:22,26
right now, different on different
platforms, right?
10
0:0:22,26 --> 0:0:22,66
Yeah.
11
0:0:22,66 --> 0:0:25,56
On Supabase on RDS and something
else.
12
0:0:25,56 --> 0:0:30,72
It's very interesting how problems
sometimes are very silent
13
0:0:30,86 --> 0:0:33,98
until suddenly they come from multiple
directions.
14
0:0:34,04 --> 0:0:34,54
Right.
15
0:0:34,64 --> 0:0:37,94
I don't know why it's happening,
right, but it's just happening.
16
0:0:38,739998 --> 0:0:41,86
Michael: All 4, are they all suffering
from the same direction?
17
0:0:42,559998 --> 0:0:43,059998
Yeah.
18
0:0:43,22 --> 0:0:44,02
Okay, interesting.
19
0:0:45,06 --> 0:0:48,4
Nik: So all of them are complaining
about read replicas lagging.
20
0:0:50,58 --> 0:0:55,38
And it's all because platforms,
most platforms keep hot_standby_feedback
21
0:0:55,38 --> 0:0:59,48
off by default, like Postgres
itself has it off by default.
22
0:1:1,56 --> 0:1:8,38
While my honest opinion, like strong
opinion, I will say, is
23
0:1:8,38 --> 0:1:10,88
that you cannot have read replicas
with off.
24
0:1:11,4 --> 0:1:18,28
For general OLTP case with many
users, And I say this after a
25
0:1:18,28 --> 0:1:19,26
lot of experience.
26
0:1:19,94 --> 0:1:26,0
I built 3 social networks with
each 1 of them achieved many million
27
0:1:26,0 --> 0:1:29,88
users and 1 plus DAU daily active
users.
28
0:1:30,56 --> 0:1:32,84
And we needed to scale reads of
course, right?
29
0:1:32,84 --> 0:1:34,58
So we needed read replicas.
30
0:1:35,58 --> 0:1:37,24
1st 1 I built in 2006.
31
0:1:38,92 --> 0:1:42,84
And also I helped many companies
who also needed to scale reads.
32
0:1:43,52 --> 0:1:46,5
My honest opinion, you cannot have
read replica with hot_standby_feedback
33
0:1:46,5 --> 0:1:49,58
off, just because
it will start lagging.
34
0:1:50,22 --> 0:1:51,6
But this is how it is.
35
0:1:52,36 --> 0:1:56,14
All people need to rediscover it
because defaults are...
36
0:1:56,4 --> 0:1:58,14
This is our favorite topic, right?
37
0:1:58,14 --> 0:2:1,9
Defaults are terrible in many aspects.
38
0:2:3,1 --> 0:2:6,46
Michael: Well, yeah, I'm actually
not sure I fully agree on this
39
0:2:6,46 --> 0:2:6,96
1 yet.
40
0:2:6,96 --> 0:2:7,8
Maybe I will by the end.
41
0:2:7,8 --> 0:2:8,76
Nik: You are not alone.
42
0:2:9,96 --> 0:2:11,44
Michael: Yeah, I'm not sure.
43
0:2:11,94 --> 0:2:16,9
It's a really difficult 1, I think,
this 1 in terms of like risks
44
0:2:16,92 --> 0:2:20,44
versus, yeah, when things bite
you.
45
0:2:20,44 --> 0:2:22,54
But let's, let's, maybe let's go
back to basics.
46
0:2:22,54 --> 0:2:23,42
Like what's the…
47
0:2:23,42 --> 0:2:24,38
Nik: Yeah, yeah, yeah.
48
0:2:24,38 --> 0:2:28,94
So we should discuss, and I just
threw it in the, at the very
49
0:2:28,94 --> 0:2:30,6
beginning to set my goal.
50
0:2:30,6 --> 0:2:35,58
I'm going to discuss it in detail
and explain my way of thinking.
51
0:2:36,1 --> 0:2:39,92
But just separately, I'm just wondering,
are there people who
52
0:2:39,92 --> 0:2:46,0
really scaled reads on really large
web or mobile apps and had
53
0:2:46,0 --> 0:2:49,64
it off because I just don't understand
how it's, how it can be
54
0:2:49,64 --> 0:2:52,28
possible unless you, you are okay.
55
0:2:52,28 --> 0:2:54,86
Your application is okay with lagging
replicas.
56
0:2:54,96 --> 0:3:0,32
Sometimes it's so if you serve
traffic to some users and some
57
0:3:0,32 --> 0:3:3,9
stale information, like lagging
some seconds or maybe minutes
58
0:3:3,9 --> 0:3:4,62
is fine.
59
0:3:5,02 --> 0:3:9,12
I can imagine that you basically
show some news for example or
60
0:3:9,12 --> 0:3:10,52
blog posts or something.
61
0:3:10,68 --> 0:3:15,58
But in general, modern applications
like social media applications,
62
0:3:15,82 --> 0:3:20,24
e-commerce and so on Where you
want to scale reads to asynchronous
63
0:3:20,38 --> 0:3:23,3
replicas, of course we talk about
asynchronous replicas here.
64
0:3:24,12 --> 0:3:27,42
Then we do need hot_standby_feedback
to be on.
65
0:3:28,52 --> 0:3:30,4
Anyway, let's start from the basics.
66
0:3:30,44 --> 0:3:34,62
We had several episodes where we
covered basics already.
67
0:3:34,64 --> 0:3:36,56
I just wanted to have a quick recap.
68
0:3:37,2 --> 0:3:40,3
1st of all, my favorite thing,
if some people don't realize,
69
0:3:40,38 --> 0:3:42,88
there are hidden columns, xmin,
xmax.
70
0:3:43,38 --> 0:3:47,52
xmin is a transaction ID which
gave birth to this tuple.
71
0:3:47,54 --> 0:3:49,4
Tuple is a row version, right?
72
0:3:49,74 --> 0:3:53,76
And there is a concept of xmin horizon,
and we had a separate
73
0:3:53,76 --> 0:4:0,22
episode about it, which defines
global for whole that database.
74
0:4:0,28 --> 0:4:6,74
Global horizon, which says this
is the oldest transaction ID that
75
0:4:6,74 --> 0:4:11,18
defines data snapshot, which is
still needed to some transactions,
76
0:4:12,26 --> 0:4:13,04
some clients.
77
0:4:13,1 --> 0:4:13,6
Right.
78
0:4:14,06 --> 0:4:17,62
And it means, And this is global,
a single value basically.
79
0:4:17,74 --> 0:4:21,54
There are many values coming from
different sources, but horizon
80
0:4:21,66 --> 0:4:24,26
is absolute minimum of all of them.
81
0:4:25,52 --> 0:4:30,06
And it defines the garbage collection
behavior, vacuum behavior,
82
0:4:30,06 --> 0:4:30,56
right?
83
0:4:30,78 --> 0:4:36,98
Because when DELETE or UPDATE or
canceled INSERT happens, dead
84
0:4:36,98 --> 0:4:38,52
tuples are produced, right?
85
0:4:38,52 --> 0:4:41,18
And the job is not done fully.
86
0:4:41,2 --> 0:4:43,76
The remaining of the job is garbage
collection.
87
0:4:44,06 --> 0:4:46,08
Dead tuple must be deleted later.
88
0:4:46,86 --> 0:4:52,84
And if this dead tuple belongs
to a transaction, which is newer
89
0:4:53,16 --> 0:4:55,38
than our xmin horizon, Vacuum cannot
delete it.
90
0:4:55,38 --> 0:4:56,48
And this is simple logic.
91
0:4:56,48 --> 0:4:57,22
It's global.
92
0:4:57,44 --> 0:4:59,88
Some people think it's maybe per
table, but it's not.
93
0:4:59,88 --> 0:5:0,56
It's global.
94
0:5:1,4 --> 0:5:1,9
Michael: Yeah.
95
0:5:2,04 --> 0:5:6,1
And this is all very simple on
a single node.
96
0:5:6,1 --> 0:5:10,24
Just if you're only worried about
having a primary database or
97
0:5:10,24 --> 0:5:16,26
maybe only an HA replica, no, it's
nice and simple, right?
98
0:5:16,26 --> 0:5:19,68
Like we just hold on to the very
versions that might be needed
99
0:5:19,68 --> 0:5:20,58
by the oldest.
100
0:5:22,24 --> 0:5:27,48
Query or whatever the process might
need it and then once that's
101
0:5:27,48 --> 0:5:27,98
finished.
102
0:5:28,48 --> 0:5:31,72
We do the cleanup or vacuum does
it right
103
0:5:31,72 --> 0:5:33,84
Nik: And here we need to dive into
some details.
104
0:5:33,84 --> 0:5:37,4
So yeah, let's 1st think about
only the primary and single node
105
0:5:37,44 --> 0:5:40,1
situation where usually people
start.
106
0:5:40,52 --> 0:5:45,24
You have, there are 5 reasons,
5 sources and our monitoring covers
107
0:5:45,24 --> 0:5:45,74
them.
108
0:5:45,78 --> 0:5:48,36
And we discussed it in the xmin horizon
episode.
109
0:5:48,94 --> 0:5:51,68
5 reasons to block xmin horizon.
110
0:5:52,2 --> 0:5:56,08
Normally if all transactions are
fast, they finish promptly,
111
0:5:57,56 --> 0:6:1,88
no replication is used, neither
logical nor physical, and you
112
0:6:1,88 --> 0:6:4,14
don't use prepared transactions.
113
0:6:5,64 --> 0:6:10,46
In this case, xmin horizon flows
naturally, and you can calculate
114
0:6:10,68 --> 0:6:14,54
age of xmin horizon, meaning that
how many transaction IDs, real
115
0:6:14,54 --> 0:6:19,2
transaction IDs passed since this
horizon and ideally it's very
116
0:6:19,2 --> 0:6:19,54
low.
117
0:6:19,54 --> 0:6:21,36
Say thousands is quite low.
118
0:6:21,88 --> 0:6:23,32
10, 000 is quite low.
119
0:6:23,32 --> 0:6:25,06
1 million it's already noticeable.
120
0:6:25,14 --> 0:6:25,64
Right.
121
0:6:26,38 --> 0:6:29,8
And we know, we all know that 1
billion is already basically
122
0:6:29,8 --> 0:6:35,82
0.5 of capacity we have because
if the whole capacity is 2.1
123
0:6:35,98 --> 0:6:37,2
billion roughly, right?
124
0:6:37,7 --> 0:6:45,14
We have 4-byte integer transaction
IDs, and 0.5 of that space
125
0:6:45,56 --> 0:6:50,92
is our future, 0.5 of it is our
past, and we need freezing vacuum.
126
0:6:51,04 --> 0:6:58,04
1 of its jobs is to freeze tuples
saying that don't look at this
127
0:6:58,04 --> 0:7:1,74
xmin, it looks like from future,
but it's from the past, right?
128
0:7:2,22 --> 0:7:9,02
And if we don't freeze during 2.1,
transaction ID, if our xmin horizon
129
0:7:9,84 --> 0:7:13,22
is that old, age of it is that
old.
130
0:7:13,22 --> 0:7:15,32
We can use function age, actually,
right?
131
0:7:15,58 --> 0:7:19,06
In this case, it means that we're
going to be in big trouble
132
0:7:19,06 --> 0:7:22,48
and database will be not accessible
because transaction ID wraparound
133
0:7:22,72 --> 0:7:23,5
will happen.
134
0:7:24,24 --> 0:7:27,8
This is the biggest danger actually
of xmin horizon being blocked
135
0:7:27,8 --> 0:7:28,88
for a long time.
136
0:7:29,44 --> 0:7:33,48
And In the case of single node
of the primary, everything is
137
0:7:33,48 --> 0:7:39,18
relatively simple, but I noticed
many people don't realize nuances
138
0:7:39,44 --> 0:7:39,94
here.
139
0:7:40,24 --> 0:7:45,48
Remember this year I started saying
that the warning, let's avoid
140
0:7:45,48 --> 0:7:48,84
long running transactions is quite
inaccurate.
141
0:7:49,64 --> 0:7:50,14
Right.
142
0:7:50,14 --> 0:7:52,44
Remember this, we're talking about
this.
143
0:7:52,44 --> 0:7:56,1
And I noticed actually, even AI
is making a lot of mistakes.
144
0:7:56,12 --> 0:8:2,1
For example, with GPT-6 Astra,
I decided to renew graphics in
145
0:8:2,1 --> 0:8:6,3
PGSimCity and also make it
more entertainable and have some
146
0:8:6,56 --> 0:8:10,14
bad scenarios to be reproduced
and so you could see what happens.
147
0:8:10,6 --> 0:8:14,54
And it wrote a very simple thing
and I see it quite often.
148
0:8:15,24 --> 0:8:20,54
It said somebody typed BEGIN and
left transaction open for a
149
0:8:20,54 --> 0:8:22,16
day and we're in trouble.
150
0:8:22,2 --> 0:8:25,46
But it's not simple BEGIN is not
a trouble.
151
0:8:25,68 --> 0:8:29,82
Because by default we have read
committed transaction isolation.
152
0:8:30,4 --> 0:8:35,84
Which means that after each statement
snapshot is free to go.
153
0:8:35,88 --> 0:8:38,1
Basically we don't block xmin for
ever.
154
0:8:38,1 --> 0:8:41,68
So if we think about transaction
at default transaction isolation
155
0:8:41,68 --> 0:8:46,28
level, if it consists of multiple
statements, while each statement
156
0:8:46,28 --> 0:8:49,98
lasts, of course we need that snapshot
and we block xmin horizon.
157
0:8:50,2 --> 0:8:51,8
We need to work with that data.
158
0:8:51,88 --> 0:8:53,96
Some SELECT is running on some
huge table.
159
0:8:53,96 --> 0:8:57,04
Yeah, it lasts for long and we
need that data.
160
0:8:57,04 --> 0:9:1,98
But once this SELECT finishes,
Even if transaction is still open,
161
0:9:2,9 --> 0:9:4,4
that snapshot is not needed.
162
0:9:4,4 --> 0:9:7,54
And basically we allow xmin horizon
to shift.
163
0:9:9,06 --> 0:9:13,38
And Simple BEGIN doesn't hold any
problem, any snapshot at all,
164
0:9:13,38 --> 0:9:13,88
right?
165
0:9:15,28 --> 0:9:17,02
At default isolation level.
166
0:9:18,22 --> 0:9:22,2
So yeah, it's very inaccurate and
that's why saying, oh, long
167
0:9:22,2 --> 0:9:24,34
running transactions are dangerous.
168
0:9:24,78 --> 0:9:26,54
It's too rough.
169
0:9:26,76 --> 0:9:31,2
It's not a precise statement because
not every long running transaction
170
0:9:31,34 --> 0:9:31,84
is.
171
0:9:31,84 --> 0:9:32,34
Yes.
172
0:9:33,72 --> 0:9:37,54
Michael: Some are of course, but
not quite all right.
173
0:9:37,54 --> 0:9:39,52
There are lots of cases where they're
not.
174
0:9:40,28 --> 0:9:40,52
Yeah, sure.
175
0:9:40,52 --> 0:9:41,02
Nik: Right.
176
0:9:41,14 --> 0:9:44,04
Of course, we don't talk about
locks here because there are 2
177
0:9:44,04 --> 0:9:49,78
topics, locks and xmin horizon
blockage, 2 dangers of long-running
178
0:9:49,78 --> 0:9:50,28
transactions.
179
0:9:50,5 --> 0:9:56,42
So we need to distinguish read
committed transactions and transactions
180
0:9:56,54 --> 0:10:1,86
running at higher isolation level,
repeatable read and serializable.
181
0:10:3,18 --> 0:10:6,68
And repeatable read is pretty common
because this is how pg_dump
182
0:10:6,68 --> 0:10:7,18
works.
183
0:10:7,9 --> 0:10:11,94
So every time you run pg_dump, even
if it's not doing, of course
184
0:10:11,94 --> 0:10:14,96
it's doing something always, right,
but it captures snapshot
185
0:10:14,96 --> 0:10:18,56
in the very beginning of transaction,
And even if you dump a
186
0:10:18,56 --> 0:10:22,26
lot of tables, you select from
them, the pg_dump selects from
187
0:10:22,26 --> 0:10:25,12
them, it shifts to the next table.
188
0:10:25,52 --> 0:10:29,6
Snapshot is still the same as it
was defined at the very beginning
189
0:10:29,6 --> 0:10:30,36
of transaction.
190
0:10:30,64 --> 0:10:34,64
It means if you start a transaction
at repeatable read, that's
191
0:10:34,64 --> 0:10:36,26
enough to be harmful.
192
0:10:36,98 --> 0:10:40,3
You just say START TRANSACTION
or BEGIN at repeatable isolation
193
0:10:40,38 --> 0:10:40,76
level.
194
0:10:40,76 --> 0:10:42,14
I always forget syntax.
195
0:10:43,52 --> 0:10:46,74
You don't need even to run any
queries inside it.
196
0:10:46,74 --> 0:10:48,02
It's already harmful.
197
0:10:48,74 --> 0:10:49,44
It holds.
198
0:10:49,54 --> 0:10:52,54
Michael: You say harmful, it starts
to hold the xmin horizon,
199
0:10:52,54 --> 0:10:53,04
right?
200
0:10:53,68 --> 0:10:55,12
It affects, yeah.
201
0:10:55,32 --> 0:10:57,74
Short holds are not expected, right?
202
0:10:58,18 --> 0:11:0,68
The reason it's being held is for
a good reason.
203
0:11:1,08 --> 0:11:6,88
If you're doing a dump, you want
probably everything, not just
204
0:11:6,88 --> 0:11:10,24
consistency, but you want the data
at that point in time, and
205
0:11:10,24 --> 0:11:14,12
if it's changed, then you want,
like you still need those, you
206
0:11:14,12 --> 0:11:16,12
can't have those rows being cleaned
up.
207
0:11:16,16 --> 0:11:18,06
Nik: So you don't need to pay…
IM – This is how consistency is
208
0:11:18,06 --> 0:11:18,94
defined, right?
209
0:11:18,94 --> 0:11:19,36
TG –
210
0:11:19,36 --> 0:11:20,15
Michael: Yeah, yeah.
211
0:11:20,15 --> 0:11:20,36
IM
212
0:11:20,36 --> 0:11:23,36
Nik: – Because if you read from
2 tables from different points
213
0:11:23,36 --> 0:11:25,36
of time, this is inconsistent dump.
214
0:11:25,46 --> 0:11:26,82
So we don't want that.
215
0:11:27,1 --> 0:11:29,12
That's why we need a repeatable read.
216
0:11:29,34 --> 0:11:30,64
Michael: But you say it's harmful.
217
0:11:30,66 --> 0:11:34,74
It just means we're holding on
to a bit of extra data maybe bloating
218
0:11:34,74 --> 0:11:39,44
some indexes a bit like there's
a an amount of that we should
219
0:11:39,44 --> 0:11:42,18
always expect when we're running
Postgres right like it there's
220
0:11:42,18 --> 0:11:45,48
a certain amount of that will always
be natural we try and minimize
221
0:11:45,48 --> 0:11:47,68
it and we try not to let it get
excessive.
222
0:11:49,46 --> 0:11:54,52
But then if that goes on for too
long, it gets into that boundary
223
0:11:54,52 --> 0:11:57,76
where it is maybe excessive or
causing other issues.
224
0:11:57,88 --> 0:11:58,98
Is that what we're trying to say?
225
0:11:58,98 --> 0:12:3,84
So like harmful, yes, but like
it, it scales with the amount
226
0:12:3,84 --> 0:12:8,86
of time or the maybe like time
multiplied by the amount of churn
227
0:12:8,86 --> 0:12:9,52
in that time.
228
0:12:9,52 --> 0:12:9,84
Nik: Yeah.
229
0:12:9,84 --> 0:12:9,96
Yeah.
230
0:12:9,96 --> 0:12:10,24
Yeah.
231
0:12:10,24 --> 0:12:10,74
Database.
232
0:12:11,74 --> 0:12:16,52
I usually say you need like, it's
really hard to understand age
233
0:12:16,52 --> 0:12:20,4
and number of transactions, although
it's more representative
234
0:12:20,66 --> 0:12:22,16
metric in this context.
235
0:12:22,66 --> 0:12:26,16
If you say like long transaction
running like 3 hours during
236
0:12:26,16 --> 0:12:31,02
busy time on like at high traffic
is not the same as during weekend
237
0:12:31,02 --> 0:12:31,96
with low traffic, right?
238
0:12:31,96 --> 0:12:35,42
Because it might be even nothing
happened during 3 hours at weekend.
239
0:12:35,42 --> 0:12:40,04
Sometimes we see some applications
with very acute spikes of
240
0:12:40,04 --> 0:12:40,98
traffic, right?
241
0:12:41,12 --> 0:12:46,06
But, but so it's, there is no like
very clear connection between
242
0:12:46,06 --> 0:12:50,36
time and I, I name it XID consumption
or XID growth rate.
243
0:12:50,74 --> 0:12:55,9
So how fast real transaction ID
grows because this is what defines
244
0:12:56,1 --> 0:12:57,82
this xmin horizon age.
245
0:12:57,94 --> 0:13:0,56
And 1 of the contributors by the
way is savepoints, sub-transactions.
246
0:13:0,86 --> 0:13:5,28
If you use them a lot in 1 transaction,
you consume many real
247
0:13:5,28 --> 0:13:9,14
transaction IDs, because every
savepoint gets allocation of
248
0:13:9,14 --> 0:13:10,62
XID, real XID.
249
0:13:11,0 --> 0:13:15,8
So this is 1 of the dangers I identified,
that savepoints lead
250
0:13:15,8 --> 0:13:17,62
to higher consumption of XIDs.
251
0:13:18,68 --> 0:13:22,66
So yeah, and yeah, if it, of course,
like if we block a vacuum,
252
0:13:22,66 --> 0:13:26,82
it also, we need to be very accurate
with language here because
253
0:13:27,18 --> 0:13:30,52
I realized also when we say vacuum
is blocked, some people think
254
0:13:30,7 --> 0:13:32,28
it's blocked, it like fails.
255
0:13:32,78 --> 0:13:36,92
I even saw, so I have a new tool
to be released soon called PGBS
256
0:13:37,02 --> 0:13:41,5
detector, which is going to, and
we started using it internally
257
0:13:41,54 --> 0:13:46,48
to identify bullshit in our write-ups
and I saw RDS article from
258
0:13:46,48 --> 0:13:50,12
2019 and this was the only article
talking about hot_standby_feedback
259
0:13:50,12 --> 0:13:51,44
from RDS.
260
0:13:52,24 --> 0:13:53,08
Nothing else.
261
0:13:53,48 --> 0:13:56,92
So I tried to understand why they
keep default off and what's
262
0:13:56,92 --> 0:13:58,38
the reasoning behind it.
263
0:13:58,52 --> 0:14:4,14
So I saw that in that article,
this like very confusing statement,
264
0:14:4,28 --> 0:14:9,9
like vacuum is blocked and then
error like canceled statement,
265
0:14:10,2 --> 0:14:11,98
like it was vacuum statement canceled.
266
0:14:11,98 --> 0:14:17,38
No, when we say xmin horizon blockage
leads to affects vacuum
267
0:14:17,38 --> 0:14:22,48
negatively, It's not like vacuum
fails or cannot work at all.
268
0:14:22,48 --> 0:14:23,44
It works partially.
269
0:14:23,76 --> 0:14:29,24
It cleans up the dead tuples which
became dead before xmin horizon.
270
0:14:30,06 --> 0:14:34,24
And it just keeps and reports it
in the logs, tuples which it
271
0:14:34,24 --> 0:14:37,96
cannot clean as dead, already dead
but cannot be cleaned up yet.
272
0:14:37,96 --> 0:14:38,8
I don't remember.
273
0:14:38,8 --> 0:14:40,24
It cannot be removed, cannot be
deleted.
274
0:14:40,24 --> 0:14:41,34
I don't remember exactly.
275
0:14:42,04 --> 0:14:45,06
But this is what's useful to see
because you can see for each
276
0:14:45,06 --> 0:14:49,64
table, a vacuum visited, autovacuum
visited, you can see what
277
0:14:49,64 --> 0:14:51,74
is the damage for this.
278
0:14:52,08 --> 0:14:56,52
You can also run VACUUM VERBOSE
and VERBOSE will report such
279
0:14:56,52 --> 0:15:0,18
tuples which are dead, but cannot
be deleted because it's xmin horizon.
280
0:15:0,28 --> 0:15:2,72
It actually also reports xmin horizon.
281
0:15:3,34 --> 0:15:7,44
It just prints the transaction
ID and what's the age of it, which
282
0:15:7,44 --> 0:15:8,74
is very useful as well.
283
0:15:8,74 --> 0:15:11,4
So that's why autovacuum logging
should be enabled.
284
0:15:12,38 --> 0:15:16,22
And maybe not 0, which means all
occurrences of autovacuum,
285
0:15:16,32 --> 0:15:21,06
but maybe at least above a few
seconds, 10 seconds or 1 second,
286
0:15:21,34 --> 0:15:26,92
if you're concerned about some
very often executions, which are
287
0:15:26,92 --> 0:15:27,74
very fast.
288
0:15:27,8 --> 0:15:31,3
So anyway, autovacuum logs are
quite useful in this area.
289
0:15:31,88 --> 0:15:32,72
What's next?
290
0:15:33,26 --> 0:15:36,1
Michael: I think we've not really
even discussed what hot_stand
291
0:15:36,22 --> 0:15:37,58
by_feedback does.
292
0:15:37,68 --> 0:15:43,32
We've talked about the retention
of dead tuples while like something's
293
0:15:43,32 --> 0:15:48,26
happening, but hot_standby_feedback
comes in because it's something
294
0:15:48,26 --> 0:15:49,9
happening not on the primary, right?
295
0:15:49,9 --> 0:15:50,28
Like it's
296
0:15:50,28 --> 0:15:52,12
Nik: something happening on your
replica.
297
0:15:52,12 --> 0:15:53,54
Yeah, it's conflicts.
298
0:15:53,8 --> 0:15:57,54
So if you think how replication
works, physical replication,
299
0:15:57,56 --> 0:16:0,06
it replays WAL like it was recovery.
300
0:16:0,06 --> 0:16:3,48
It was built on top of recovery
from crashes, right?
301
0:16:3,82 --> 0:16:6,96
And the hot_standby_feedback is
just constantly replaying WAL
302
0:16:6,96 --> 0:16:8,3
like it was a recovery.
303
0:16:8,88 --> 0:16:13,34
That's why a function to check
is it primary or standby is called
304
0:16:13,34 --> 0:16:15,36
pg_is_in_recovery, which is confusing.
305
0:16:15,54 --> 0:16:16,04
Right?
306
0:16:16,68 --> 0:16:17,54
And it replays.
307
0:16:17,54 --> 0:16:18,16
WAL, good.
308
0:16:18,16 --> 0:16:22,58
But what if we have some query
ongoing, selecting from a huge
309
0:16:22,58 --> 0:16:24,06
table, for example, reporting,
right?
310
0:16:24,06 --> 0:16:24,96
It's pretty common.
311
0:16:24,96 --> 0:16:28,48
Let's offload heavy reporting queries
to read replicas.
312
0:16:29,24 --> 0:16:34,52
If it's running there and WAL has
instruction to clean up dead
313
0:16:34,66 --> 0:16:37,5
tuples which on the primary already
dead but we still reading
314
0:16:37,5 --> 0:16:38,0
them.
315
0:16:38,72 --> 0:16:42,76
This is 1 of conflicts and this
is snapshot conflict of replication
316
0:16:42,78 --> 0:16:47,54
conflicts with a query or query
or transaction conflicts with
317
0:16:47,54 --> 0:16:50,26
this WAL data right It cannot be
replayed.
318
0:16:50,94 --> 0:16:56,4
So depending on the type of physical
standby, 1 of 2 settings
319
0:16:56,68 --> 0:16:59,78
are used to decide what to do.
320
0:16:59,86 --> 0:17:1,72
So what to do is obvious.
321
0:17:2,08 --> 0:17:3,54
Replication is paused.
322
0:17:4,2 --> 0:17:5,26
It cannot be replayed.
323
0:17:7,42 --> 0:17:8,6
This is default option.
324
0:17:9,82 --> 0:17:13,04
So we wait until the end of this
transaction.
325
0:17:14,1 --> 0:17:15,78
Michael: And then we continue replaying.
326
0:17:17,22 --> 0:17:19,88
Nik: We wait until xmin horizon
shifts.
327
0:17:20,58 --> 0:17:22,54
If it shifts, we can replay it.
328
0:17:23,36 --> 0:17:26,86
So you can see it manually in pg_stat_
activity backend_xmin
329
0:17:27,26 --> 0:17:27,76
column.
330
0:17:28,14 --> 0:17:35,44
And also it's, yeah, So backend_
xmin is for each backend, you
331
0:17:35,44 --> 0:17:41,12
can see this local horizon for
each backend and minimum of them
332
0:17:41,12 --> 0:17:43,16
is our horizon for this node.
333
0:17:44,02 --> 0:17:46,74
It waits, it waits not forever.
334
0:17:47,62 --> 0:17:53,82
There is a setting I believe it's
called max_standby_streaming_
335
0:17:54,48 --> 0:17:54,98
delay.
336
0:17:55,86 --> 0:17:56,36
Yes,
337
0:17:56,74 --> 0:17:59,54
Michael: there's max_standby_streaming_
delay and max_standby_
338
0:17:59,54 --> 0:18:0,54
archive_delay.
339
0:18:0,72 --> 0:18:2,84
Nik: I will never remember all
GUCs.
340
0:18:2,84 --> 0:18:3,34
Yeah.
341
0:18:3,46 --> 0:18:6,76
So yeah, there are 2, 1 for streaming,
1 for WAL replay.
342
0:18:6,86 --> 0:18:10,74
Replicas which are consuming WALs
from archive, like S3 archive
343
0:18:10,74 --> 0:18:13,4
or something, according to restore_
command.
344
0:18:13,58 --> 0:18:17,44
And, but more common because they
have smaller lags is streaming
345
0:18:17,44 --> 0:18:22,36
replication, which connects to
the primary, unless we use cascade
346
0:18:22,36 --> 0:18:26,6
replication, and just streams the
WALs and applies them.
347
0:18:26,82 --> 0:18:29,98
And this is when we use max_standby_
streaming_delay.
348
0:18:30,86 --> 0:18:32,72
By default, I think it's 30 seconds.
349
0:18:33,54 --> 0:18:35,42
It waits a maximum of 30 seconds.
350
0:18:36,22 --> 0:18:42,04
After this, Postgres says replication
is more important and just
351
0:18:42,04 --> 0:18:43,72
cancels the query.
352
0:18:44,54 --> 0:18:47,76
And the 1st thing that happens,
people don't like queries being
353
0:18:47,76 --> 0:18:48,48
canceled, right?
354
0:18:48,48 --> 0:18:51,24
Or important reports.
355
0:18:51,6 --> 0:18:53,94
Michael: Especially sometimes long
ones, like if it's a long
356
0:18:53,94 --> 0:18:57,44
reporting query, you've already
waited 10 minutes for it.
357
0:18:57,44 --> 0:18:57,66
Nik: Right.
358
0:18:57,66 --> 0:19:1,26
And that's very visible because
reporting usually is close to
359
0:19:1,26 --> 0:19:3,74
decision makers, So they start
complaining very fast.
360
0:19:3,74 --> 0:19:5,64
Like, why report was not delivered?
361
0:19:5,64 --> 0:19:7,68
Okay, we have conflicts.
362
0:19:7,7 --> 0:19:8,2
Canceled.
363
0:19:8,46 --> 0:19:13,08
Okay, let's increase this to 3
minutes, 30 minutes, 3 hours a
364
0:19:13,08 --> 0:19:13,58
day.
365
0:19:14,44 --> 0:19:15,34
And it's okay.
366
0:19:15,9 --> 0:19:16,4
Yeah.
367
0:19:16,94 --> 0:19:18,96
At extreme, you can set minus 1.
368
0:19:19,62 --> 0:19:23,98
And so replication will wait until,
369
0:19:24,28 --> 0:19:24,78
Michael: yeah.
370
0:19:25,32 --> 0:19:26,04
So what happens now?
371
0:19:26,04 --> 0:19:28,38
And this whole time, I think it's
really important to stress,
372
0:19:28,38 --> 0:19:33,28
this whole time anything newer
than that conflict is not getting
373
0:19:33,28 --> 0:19:35,4
replayed on the replica.
374
0:19:35,46 --> 0:19:39,96
So no, any new inserts, any updates,
any deletes, nothing's coming
375
0:19:39,96 --> 0:19:40,46
through
376
0:19:40,96 --> 0:19:42,44
Nik: until that query finishes.
377
0:19:42,44 --> 0:19:43,055
Yeah, it's global.
378
0:19:43,055 --> 0:19:43,84
Horizon was global.
379
0:19:43,94 --> 0:19:46,42
Replication is just single process
actually.
380
0:19:46,72 --> 0:19:49,58
You can see a startup process in
top or ps.
381
0:19:50,02 --> 0:19:51,94
And this is what applies changes.
382
0:19:52,2 --> 0:19:55,76
By the way, changes might be received
already because the replication
383
0:19:55,76 --> 0:19:56,5
has stages.
384
0:19:56,68 --> 0:20:0,06
They can be flushed to local pg_wal
directory, but they just
385
0:20:0,06 --> 0:20:3,82
cannot be applied because somebody
needs very old, all the data,
386
0:20:4,0 --> 0:20:6,86
which we already want to clean
up.
387
0:20:7,8 --> 0:20:8,3
Right.
388
0:20:8,8 --> 0:20:11,92
So what happens next?
389
0:20:12,5 --> 0:20:13,54
Reports work.
390
0:20:13,62 --> 0:20:17,64
For example, we set 3 hours, All
reports last no more than 1
391
0:20:17,64 --> 0:20:18,12
hour.
392
0:20:18,12 --> 0:20:18,98
We are good.
393
0:20:19,74 --> 0:20:25,06
But then, usually what happens,
some users started to complain.
394
0:20:25,24 --> 0:20:26,1
Stale data.
395
0:20:27,74 --> 0:20:30,96
And if your load balancer is not
smart enough, If you have a
396
0:20:30,96 --> 0:20:35,44
smart load balancer, by the way,
you mentioned Postgres 19 has
397
0:20:35,44 --> 0:20:37,24
a wait for LSN feature, right?
398
0:20:37,24 --> 0:20:39,3
We probably should talk about it
separately.
399
0:20:39,44 --> 0:20:41,66
I have a lot to talk about this.
400
0:20:41,76 --> 0:20:45,42
But smart load balancing understands
that some replica is lagging
401
0:20:45,9 --> 0:20:47,28
and stops using it.
402
0:20:48,7 --> 0:20:52,44
Just because we don't want to deliver
stale data to our clients.
403
0:20:52,66 --> 0:20:57,1
But if you have pretty basic load
balancing and some either queries
404
0:20:57,34 --> 0:21:2,72
or transactions went to read replica,
It takes some time because
405
0:21:3,48 --> 0:21:7,08
usually who suffers is not decision
makers like management of
406
0:21:7,08 --> 0:21:9,72
a company wanting reports, but
some users.
407
0:21:9,88 --> 0:21:14,64
And they also usually not every
user is willing to waste energy
408
0:21:14,64 --> 0:21:16,16
to report problems, right?
409
0:21:16,16 --> 0:21:18,3
It takes some time usually, that's
a problem.
410
0:21:18,82 --> 0:21:21,66
Michael: It takes time and also
you have to rule out other issues
411
0:21:21,66 --> 0:21:22,2
1st, right?
412
0:21:22,2 --> 0:21:24,44
Like you have to make sure it's
not on your side.
413
0:21:24,44 --> 0:21:27,58
Like there's just because something
seems like it's not working
414
0:21:27,64 --> 0:21:30,36
doesn't you don't automatically
assume it's a service.
415
0:21:30,9 --> 0:21:33,7
Nik: Yeah And what I'm describing
is a very common situation,
416
0:21:34,24 --> 0:21:39,66
which I went through in 2006 or
7 and just thought it's solved.
417
0:21:39,8 --> 0:21:43,58
But due to defaults, especially
defaults on managed Postgres
418
0:21:43,58 --> 0:21:48,2
platforms, which keep hot_standby_feedback
off, This is what's happening.
419
0:21:48,48 --> 0:21:54,4
So people increase max_standby_
streaming_delay and then they
420
0:21:54,4 --> 0:21:58,22
just say, okay, we have replication
lags.
421
0:21:58,22 --> 0:22:1,34
And they start thinking why, because
it's not obvious why.
422
0:22:1,56 --> 0:22:6,26
It's not really not obvious, because
yeah, it's just lags and
423
0:22:6,26 --> 0:22:6,94
so on.
424
0:22:7,66 --> 0:22:12,52
Michael: But so should we switch,
should we move to then why
425
0:22:12,52 --> 0:22:16,08
we now have hot_standby_feedback
and what it does?
426
0:22:16,1 --> 0:22:20,2
Nik: So hot_standby_feedback reports
xmin horizon observed on
427
0:22:20,2 --> 0:22:21,38
replica to the primary.
428
0:22:21,82 --> 0:22:27,02
So the primary can involve it in
calculation of the final xmin horizon
429
0:22:27,44 --> 0:22:32,12
used by Vacuum, deciding what can
be cleaned safely, what still
430
0:22:32,12 --> 0:22:33,22
cannot be cleaned.
431
0:22:33,68 --> 0:22:40,58
So if you had 1 hour query on the
primary, which led to bloat
432
0:22:41,2 --> 0:22:45,02
sometimes, right, because xmin
horizon again, global, it
433
0:22:45,02 --> 0:22:45,74
was bloat.
434
0:22:45,86 --> 0:22:50,4
And then you offloaded to read
replica without hot_standby_feedback,
435
0:22:50,4 --> 0:22:54,3
you have lags up to 1 hour because
your queries are 1 hour.
436
0:22:54,52 --> 0:22:56,02
And it's terrible lag, right?
437
0:22:57,04 --> 0:23:2,8
Or when you switch to hot_standby_feedback
to on, you have the problem
438
0:23:2,8 --> 0:23:6,14
like the horizon being reported
and you have the similar situation
439
0:23:6,22 --> 0:23:8,8
as it was executed on the primary
itself.
440
0:23:9,34 --> 0:23:10,02
Matthew 14.
441
0:23:11,12 --> 0:23:14,82
Michael: And I think the critical
thing to mention is the chance
442
0:23:14,82 --> 0:23:18,56
of you then getting conflicts are
massively reduced because the
443
0:23:18,56 --> 0:23:23,8
primary won't have cleaned up those
dead tuples and therefore
444
0:23:24,18 --> 0:23:28,94
won't send those conflicts through
to the replica until it's
445
0:23:28,94 --> 0:23:30,32
finished what it was doing.
446
0:23:30,42 --> 0:23:33,92
So it avoids that case we were
just talking about in most cases.
447
0:23:34,02 --> 0:23:35,54
Nik: That's why lag doesn't happen.
448
0:23:35,54 --> 0:23:37,18
So conflict leads to lag.
449
0:23:37,54 --> 0:23:39,38
Conflict leads to 2 things.
450
0:23:39,38 --> 0:23:43,08
1st, lag of replication, and when
it's too much lag, then cancelling
451
0:23:43,22 --> 0:23:44,62
the source of the problem.
452
0:23:44,64 --> 0:23:45,14
Query.
453
0:23:45,72 --> 0:23:46,22
Right?
454
0:23:46,7 --> 0:23:49,32
So, don't block xmin horizon Progress.
455
0:23:49,66 --> 0:23:51,2
So, you are right.
456
0:23:51,22 --> 0:23:55,64
I like that you used reduce number
of conflicts because it doesn't.
457
0:23:55,64 --> 0:23:56,14
Yeah.
458
0:23:56,82 --> 0:24:0,02
For example, we talked about the
conflict when we need to clean
459
0:24:0,02 --> 0:24:4,46
up, but tuples still needed there,
Like this is a snapshot conflict,
460
0:24:4,46 --> 0:24:6,36
but there might be also a conflict.
461
0:24:6,38 --> 0:24:9,44
When we change schema, we need
ACCESS EXCLUSIVE lock.
462
0:24:9,52 --> 0:24:13,46
And the mistake is to keep this
ACCESS EXCLUSIVE lock for long.
463
0:24:15,06 --> 0:24:18,52
And on the primary, it leads to
many problems.
464
0:24:18,52 --> 0:24:21,02
We know like some selects even
will wait.
465
0:24:21,02 --> 0:24:23,34
It's ACCESS EXCLUSIVE lock, blocks
even selects.
466
0:24:23,68 --> 0:24:24,48
Schema change.
467
0:24:24,48 --> 0:24:28,52
So if you open transaction, edit
a column very briefly, and then
468
0:24:28,52 --> 0:24:30,26
start to read something from somewhere.
469
0:24:30,48 --> 0:24:32,68
You keep the lock until very end
of transactions.
470
0:24:32,68 --> 0:24:35,96
We discussed many times lock cannot
be released midway.
471
0:24:36,02 --> 0:24:37,54
It's released only at the end.
472
0:24:37,54 --> 0:24:42,38
If you do something else, this
lock is held and nobody can work
473
0:24:42,38 --> 0:24:43,3
with that table.
474
0:24:43,32 --> 0:24:46,74
But on the replica also, if you
keep this lock on the primary.
475
0:24:47,08 --> 0:24:52,44
WAL comes saying lock and again,
lag will happen.
476
0:24:52,44 --> 0:24:54,94
And hot_standby_feedback doesn't
solve this at all.
477
0:24:55,76 --> 0:24:56,66
This should be clear.
478
0:24:56,66 --> 0:24:58,48
So yeah, you're very good warning.
479
0:24:58,48 --> 0:24:59,06
I liked it.
480
0:24:59,06 --> 0:25:0,52
It's not solving fully.
481
0:25:0,8 --> 0:25:3,7
Even more, I learned it only recently.
482
0:25:4,64 --> 0:25:9,64
So if you usually use standard
replicas, these days I used streaming
483
0:25:9,64 --> 0:25:11,62
replication plus replication slots.
484
0:25:11,68 --> 0:25:15,36
Replication slots were created
later than streaming replication.
485
0:25:15,36 --> 0:25:16,9
It's like additional level.
486
0:25:17,36 --> 0:25:21,7
And you can use streaming replication
without slots.
487
0:25:21,7 --> 0:25:26,26
Usually people think replication
slots are protection from WALs
488
0:25:26,58 --> 0:25:28,16
being deleted from the primary.
489
0:25:28,44 --> 0:25:31,78
The replica lags too much, it cannot
converge anymore, right?
490
0:25:31,92 --> 0:25:35,38
It's solved usually if you have
good backups in S3, object storage,
491
0:25:35,38 --> 0:25:39,02
you can configure restore_command
on the replica and it will
492
0:25:39,02 --> 0:25:40,84
just take WALs from there, no
problem.
493
0:25:40,84 --> 0:25:42,64
So slots can be like mitigated.
494
0:25:42,9 --> 0:25:45,26
Also slots are convenient for observability.
495
0:25:45,72 --> 0:25:49,12
But what I also learned, imagine
we have streaming replication
496
0:25:49,12 --> 0:25:49,96
without slot.
497
0:25:50,28 --> 0:25:55,24
Slots have xmin horizon reporting
right in pg_replication_slots,
498
0:25:55,24 --> 0:25:57,52
you can see it, xmin, column.
499
0:25:57,98 --> 0:26:1,1
But if you don't have slot, streaming
replication, It works all
500
0:26:1,1 --> 0:26:1,6
good.
501
0:26:1,72 --> 0:26:5,1
But then some network issue, replica
disconnected.
502
0:26:5,46 --> 0:26:8,68
On the primary, for example, vacuum
deleted tuples, reconnection
503
0:26:8,82 --> 0:26:12,48
happened, vacuum already deleted
tuples and WAL is delivered,
504
0:26:12,54 --> 0:26:13,68
tuples are deleted.
505
0:26:14,16 --> 0:26:18,28
And despite hot_standby_feedback
being on, we have situation like
506
0:26:18,28 --> 0:26:19,26
it was off.
507
0:26:20,28 --> 0:26:23,1
This was an interesting finding
for me last week when I dived
508
0:26:23,1 --> 0:26:28,14
into this topic deeper, using our
new tool for experiments, reproduced
509
0:26:28,14 --> 0:26:30,56
a lot of failure scenarios, including
this 1.
510
0:26:30,72 --> 0:26:31,76
So it's interesting.
511
0:26:32,08 --> 0:26:35,28
It means that slots are useful
in many different cases also,
512
0:26:35,28 --> 0:26:36,84
like in sensors, right?
513
0:26:37,54 --> 0:26:42,34
So yeah, hot_standby_feedback
on drastically reduce the chance
514
0:26:42,34 --> 0:26:44,56
that you will have lag on standby.
515
0:26:45,06 --> 0:26:48,68
And this is what we want for the
replicas because we want them
516
0:26:48,68 --> 0:26:51,16
to be up to date as much as possible.
517
0:26:51,98 --> 0:26:53,9
Michael: But it also does 1 other
thing that I think is quite
518
0:26:53,9 --> 0:26:54,4
important.
519
0:26:54,94 --> 0:26:58,4
It should also drastically reduce
the number of cancellations
520
0:26:58,68 --> 0:27:1,12
you get if you have these long
running queries.
521
0:27:1,4 --> 0:27:4,58
Let's say longer than 30 seconds
or whatever you set that timing
522
0:27:4,64 --> 0:27:9,44
to, it should reduce those getting
cancelled because the conflicts
523
0:27:9,44 --> 0:27:10,32
aren't going through.
524
0:27:10,32 --> 0:27:11,76
Because you're not getting conflicts
in the
525
0:27:11,76 --> 0:27:12,7
Nik: 1st place, right?
526
0:27:13,5 --> 0:27:13,9742
Right.
527
0:27:13,9742 --> 0:27:18,74
Cancellation is a result of delay,
which is a result of conflict.
528
0:27:18,8 --> 0:27:23,7
So conflict, delay or replication
lag, and then cancellation
529
0:27:23,82 --> 0:27:25,26
when it's too high.
530
0:27:25,96 --> 0:27:27,6
Value of lag is too high.
531
0:27:28,34 --> 0:27:33,14
So that's why I think read replicas
should have hot_standby_feedback
532
0:27:33,14 --> 0:27:37,52
on and reasoning that it will be
worse because they will lead
533
0:27:37,52 --> 0:27:38,04
to bloat.
534
0:27:38,04 --> 0:27:40,6
This is just how Postgres works.
535
0:27:40,6 --> 0:27:44,24
It was usually people start with
all queries going to primary,
536
0:27:44,24 --> 0:27:46,16
then they offload queries to replica.
537
0:27:46,16 --> 0:27:49,28
It's just a mistake to expect that
those queries won't affect
538
0:27:49,28 --> 0:27:50,14
vacuum behavior.
539
0:27:51,14 --> 0:27:51,88
That's it.
540
0:27:51,9 --> 0:27:54,26
Of course, you can live with off.
541
0:27:55,16 --> 0:27:56,54
Actually, 2 points here.
542
0:27:56,54 --> 0:27:58,12
1st point is quite interesting.
543
0:27:59,06 --> 0:28:7,06
So we want to minimize damage of
long-running transactions which
544
0:28:7,06 --> 0:28:10,8
block xmin horizon progress, and
how can we do it?
545
0:28:10,8 --> 0:28:16,56
We can set transaction_timeout,
both on primary and all read
546
0:28:16,56 --> 0:28:17,06
replicas.
547
0:28:17,32 --> 0:28:20,96
For example, if we set it to 3
hours, that's it.
548
0:28:21,1 --> 0:28:22,7
No more 3 hours of damage.
549
0:28:22,74 --> 0:28:25,02
But as we discussed, damage is
relative.
550
0:28:25,14 --> 0:28:28,76
3 hours, we don't know how many
transactions happened, right?
551
0:28:29,2 --> 0:28:33,34
I think actually it would make
sense, it might make sense to
552
0:28:33,34 --> 0:28:35,2
have all timeouts.
553
0:28:35,46 --> 0:28:38,4
We have transaction_timeout, we
have statement_timeout, and
554
0:28:38,4 --> 0:28:39,9
idle_in_transaction_session_timeout.
555
0:28:40,64 --> 0:28:43,22
transaction_timeout limits whole
transaction, which is good.
556
0:28:43,26 --> 0:28:47,86
But I think it might make sense
either to have all these 3 measured
557
0:28:47,9 --> 0:28:52,32
in not in milliseconds or seconds,
but in a transaction count.
558
0:28:52,86 --> 0:28:56,5
Imagine transaction_timeout, but
count of transactions, right?
559
0:28:56,74 --> 0:29:1,72
Or adjust hot_standby_feedback so
it would be not like on and
560
0:29:1,72 --> 0:29:5,72
off, but some threshold after which
we cancel.
561
0:29:6,28 --> 0:29:9,14
So there is definitely room for
improvement here.
562
0:29:9,14 --> 0:29:13,92
And if you look, my AI told me
other database systems ship settings
563
0:29:14,22 --> 0:29:14,94
for replicas.
564
0:29:15,54 --> 0:29:18,96
Postgres ships 2 extremes, on and
off.
565
0:29:20,14 --> 0:29:23,86
But with transaction_timeout, at
least measured in seconds, right?
566
0:29:23,86 --> 0:29:27,62
It's indirect, but it's already
good enough in many cases, which
567
0:29:27,62 --> 0:29:29,42
was implemented with Postgres 17.
568
0:29:30,14 --> 0:29:32,22
So on old Postgres it's not available.
569
0:29:32,78 --> 0:29:33,9
But it's good enough.
570
0:29:33,9 --> 0:29:37,62
My recommendation, on and 3 hours
transaction_timeout.
571
0:29:39,0 --> 0:29:42,42
So nobody could leave transaction
open which blocks xmin horizon
572
0:29:42,5 --> 0:29:44,08
progress for a very long time.
573
0:29:44,16 --> 0:29:46,96
But you need to do it on the primary
as well, right?
574
0:29:46,96 --> 0:29:49,14
Because who knows what happens
there.
575
0:29:49,28 --> 0:29:50,88
It's not no difference here.
576
0:29:51,74 --> 0:29:57,22
Michael: I think the big difference
is people not offloading
577
0:29:57,36 --> 0:30:0,06
just like OLTP traffic to a replica.
578
0:30:0,06 --> 0:30:3,82
I think it's people think that
I've got a replica, I can send
579
0:30:3,82 --> 0:30:7,2
my reporting or analytics queries
there or I can give a data
580
0:30:7,2 --> 0:30:11,08
team access to that and it doesn't
matter, it protects the primary.
581
0:30:11,08 --> 0:30:16,32
I think the key learning from this
setting is That's not true.
582
0:30:17,54 --> 0:30:22,02
You have to be, it can still affect
the primary and therefore
583
0:30:22,02 --> 0:30:25,7
you have to factor that in on almost
like an architectural level.
584
0:30:25,76 --> 0:30:28,78
Should you even be running those
reporting queries there if it
585
0:30:28,78 --> 0:30:32,84
on an OLTP, like an important OLTP
system or should you not do
586
0:30:32,84 --> 0:30:33,34
that?
587
0:30:33,4 --> 0:30:35,7
I think it raises those important
questions.
588
0:30:36,42 --> 0:30:36,92
Nik: Yeah.
589
0:30:37,66 --> 0:30:41,68
For reporting replicas, maybe you
should keep it off, but the
590
0:30:41,68 --> 0:30:45,46
consequences are very often replication
lags.
591
0:30:45,86 --> 0:30:49,24
And I even say that it makes like
basically this replication
592
0:30:49,28 --> 0:30:54,92
replica, like single user, like
you take around very long query,
593
0:30:54,92 --> 0:30:57,8
all other users like suffer and
cannot use it anymore.
594
0:30:57,8 --> 0:30:59,64
Like they say, oh, it's very old
data.
595
0:30:59,64 --> 0:31:0,8
We need fresh data.
596
0:31:1,28 --> 0:31:6,26
So like you become very like expensive
user, but maybe it's fine
597
0:31:6,26 --> 0:31:10,64
in some cases, like maybe it's
better to do that and then catch
598
0:31:10,64 --> 0:31:11,14
up.
599
0:31:11,64 --> 0:31:13,3
I don't know, but it's quite expensive.
600
0:31:13,94 --> 0:31:17,78
It's quite expensive to have a
whole node for reports, which,
601
0:31:17,84 --> 0:31:19,84
and this node is lagging and catches
up.
602
0:31:19,84 --> 0:31:20,42
I don't know.
603
0:31:20,42 --> 0:31:23,68
Michael: Like it's, but some, some
people do like shipping it
604
0:31:23,68 --> 0:31:24,5
to like ClickHouse.
605
0:31:24,6 --> 0:31:26,46
We did whole episodes on this,
didn't we?
606
0:31:26,46 --> 0:31:28,76
And I think there's some interesting
alternatives.
607
0:31:29,34 --> 0:31:31,8
Nik: But this is not really a replica,
it's an analytical replica,
608
0:31:31,8 --> 0:31:31,96
right?
609
0:31:31,96 --> 0:31:36,94
Or if you have different storage,
there are some, not Postgres,
610
0:31:36,94 --> 0:31:40,02
but some alternative to Postgres,
which stores data differently
611
0:31:40,12 --> 0:31:42,14
and executes queries much faster.
612
0:31:42,66 --> 0:31:46,56
In this case, maybe hot_standby_feedback
on is still good because
613
0:31:46,56 --> 0:31:48,82
negative effect to vacuum will
be low, right?
614
0:31:49,24 --> 0:31:52,08
I can imagine some cases where
off is good.
615
0:31:52,08 --> 0:31:54,06
For example, if it's a delayed
replica.
616
0:31:55,2 --> 0:31:59,54
Some people keep 8 hours, 12 hours
delayed replica, which replays
617
0:31:59,54 --> 0:32:4,84
WAL with delay 10, 12 hours to
be able to very quickly restore
618
0:32:5,38 --> 0:32:7,86
point-in-time recovery, to perform
point-in-time recovery.
619
0:32:8,14 --> 0:32:11,78
I think this recipe is quite outdated
because now we have snapshots
620
0:32:11,82 --> 0:32:15,66
and if even with lazy load, it's
quite fast to provision multi-terabyte
621
0:32:15,88 --> 0:32:18,06
databases from cloud snapshots.
622
0:32:18,9 --> 0:32:22,36
But if you use like delayed replica,
of course you don't want
623
0:32:22,36 --> 0:32:23,16
hot_standby_feedback.
624
0:32:23,16 --> 0:32:25,68
I actually think it's not possible
to use it there because hot_stand
625
0:32:25,68 --> 0:32:28,3
by_feedback works only with streaming
replication, right?
626
0:32:28,925 --> 0:32:36,5
So with archive replicas which
work using restore_command, replaying
627
0:32:36,5 --> 0:32:38,5
WALs from archive or from some place.
628
0:32:39,24 --> 0:32:41,18
hot_standby_feedback cannot be applied
there.
629
0:32:41,76 --> 0:32:45,48
And you cannot see them in replication
slots, so these replicas
630
0:32:45,48 --> 0:32:46,02
are invisible.
631
0:32:46,02 --> 0:32:48,54
And this is how delayed replicas
should work.
632
0:32:48,62 --> 0:32:51,88
And in this case, you'll be dealing
with the same mechanics,
633
0:32:52,0 --> 0:32:59,35
but defined by max_standby, not
streaming, but what the other
634
0:32:59,35 --> 0:32:59,84
alternative?
635
0:32:59,84 --> 0:33:0,34
Shipping.
636
0:33:1,08 --> 0:33:5,46
Archive delay, max_standby_archive_
delay.
637
0:33:5,46 --> 0:33:5,96
Right.
638
0:33:6,46 --> 0:33:10,3
So I think this is, this covers
quite well this topic.
639
0:33:10,4 --> 0:33:14,88
I think Postgres has opportunity
to, to improve things drastically,
640
0:33:15,04 --> 0:33:16,68
like for better control.
641
0:33:17,38 --> 0:33:22,34
I wish like I had a capability
to define the damage, not in seconds,
642
0:33:22,34 --> 0:33:28,4
but precise, like maximum, like
100000 transaction IDs, yeah.
643
0:33:29,18 --> 0:33:34,28
And if it happens, if somebody
exceeds it, I want to cancel that
644
0:33:34,28 --> 0:33:34,78
1.
645
0:33:34,92 --> 0:33:38,66
And for reporting, the most standard
approach for large databases,
646
0:33:38,86 --> 0:33:43,1
I agree with you, it remains another
database system and you
647
0:33:43,1 --> 0:33:47,46
need to create some pipeline, like
a logical replication pipeline
648
0:33:47,56 --> 0:33:51,36
to ship data to ClickHouse or anything
else like Snowflake.
649
0:33:51,82 --> 0:33:52,32
Michael: Yeah.
650
0:33:52,42 --> 0:33:52,66
Yeah.
651
0:33:52,66 --> 0:33:53,86
We could monitor for that.
652
0:33:53,86 --> 0:33:55,22
Like you can do that.
653
0:33:55,32 --> 0:33:56,98
You can build that yourself, right?
654
0:33:56,98 --> 0:34:2,5
If you monitor for things holding
back the xmin horizon and you
655
0:34:2,5 --> 0:34:5,78
can check the age in like transaction
IDs.
656
0:34:5,86 --> 0:34:9,92
So that is that's already possible
today right but we just have
657
0:34:9,92 --> 0:34:12,74
to do it ourselves rather than
a configuration parameter.
658
0:34:14,24 --> 0:34:19,86
Nik: Yeah so yeah you are talking
about some automation which
659
0:34:19,86 --> 0:34:23,14
will be outside Postgres but it
will demonstrate all 5 reasons
660
0:34:23,14 --> 0:34:25,96
for xmin horizon being blocked,
identify them and then cancel
661
0:34:25,96 --> 0:34:26,46
them.
662
0:34:26,54 --> 0:34:27,14
Then 5.
663
0:34:27,74 --> 0:34:28,24
Michael: Yeah.
664
0:34:28,66 --> 0:34:29,16
Nik: Yeah.
665
0:34:29,2 --> 0:34:32,88
You like you're talking about self-driving
ideas, but it shouldn't
666
0:34:32,88 --> 0:34:34,12
be in Postgres in my opinion.
667
0:34:34,12 --> 0:34:35,78
This is, this is the wrong example.
668
0:34:36,02 --> 0:34:40,68
This logic is too basic, too fundamental
to be, it's possible
669
0:34:40,68 --> 0:34:41,78
to implement outside.
670
0:34:42,34 --> 0:34:42,84
Definitely.
671
0:34:42,9 --> 0:34:46,56
I remember actually many times
I implemented like something in
672
0:34:46,56 --> 0:34:50,02
pg_cron or regular cron, like
canceling long running transactions,
673
0:34:50,02 --> 0:34:51,68
which are like offensive.
674
0:34:52,72 --> 0:34:54,72
Looking at backend_xmin
and so on.
675
0:34:54,72 --> 0:34:56,02
Yeah, we did it actually.
676
0:34:56,14 --> 0:34:59,0
And this is like duct taping, right?
677
0:34:59,64 --> 0:35:0,46
Michael: Yeah, sure.
678
0:35:0,92 --> 0:35:5,58
Nik: This is not, but I'm talking
like, I just feel, I feel the
679
0:35:5,58 --> 0:35:10,02
need in Postgres itself could be
improved, maybe some listener
680
0:35:10,9 --> 0:35:12,28
should propose some patches.
681
0:35:12,88 --> 0:35:15,8
Michael: I think it would be tough
to, I think this is a really
682
0:35:15,8 --> 0:35:18,14
tough 1 because it's a genuine
trade off.
683
0:35:18,24 --> 0:35:22,72
I don't feel like there's a right
solution and it's going to
684
0:35:22,72 --> 0:35:27,84
depend on what you're using your
replica for and maybe there's
685
0:35:27,84 --> 0:35:32,9
like a case that 95% of people
want and like therefore we should
686
0:35:32,9 --> 0:35:33,98
just change it.
687
0:35:34,04 --> 0:35:37,8
But it feels to me like some people
do prefer the trade-off of
688
0:35:37,8 --> 0:35:40,38
having it on and some people prefer
the trade-off of having it
689
0:35:40,38 --> 0:35:40,88
off.
690
0:35:41,18 --> 0:35:45,36
And it's maybe tricky, gonna be
hard to find something everybody's
691
0:35:45,48 --> 0:35:46,56
actually happy with.
692
0:35:47,12 --> 0:35:51,04
Nik: Yeah, actually, I think for,
yeah, this is, yeah, I think
693
0:35:51,04 --> 0:35:54,18
to leave the choice on the user
who provisions read replica,
694
0:35:54,24 --> 0:35:55,52
it is also a good idea.
695
0:35:57,72 --> 0:36:1,16
By the way, I've read, I saw many
hackers proposed to switch
696
0:36:1,16 --> 0:36:3,3
default in Postgres itself to on.
697
0:36:3,64 --> 0:36:7,0
There was such opinion, very strong
1, multiple, very well-known
698
0:36:7,02 --> 0:36:7,86
hackers proposed it.
699
0:36:7,86 --> 0:36:11,32
But then it was a discussion, it
was opposition to it.
700
0:36:11,32 --> 0:36:15,06
And by the way, at that time,
transaction_timeout didn't exist.
701
0:36:15,06 --> 0:36:17,06
So maybe now it should be reconsidered.
702
0:36:17,44 --> 0:36:17,9
Yeah.
703
0:36:17,9 --> 0:36:19,18
It was before Postgres 17.
704
0:36:19,82 --> 0:36:24,78
And the reason I remember, which
like hits in my mind brightly,
705
0:36:25,08 --> 0:36:30,22
that it was named that problems
with lags and conflicts canceled,
706
0:36:30,66 --> 0:36:33,62
canceled like lags and canceled
queries on replica.
707
0:36:34,06 --> 0:36:34,56
Yes.
708
0:36:34,94 --> 0:36:36,24
Very obvious to you.
709
0:36:36,26 --> 0:36:39,1
And it's better to discover what's
happening, understand and
710
0:36:39,1 --> 0:36:43,32
then make decision based on your
situation and experience rather
711
0:36:43,32 --> 0:36:47,7
than you don't notice or bloat.
712
0:36:48,32 --> 0:36:48,84
Bloat on
713
0:36:48,84 --> 0:36:50,88
Michael: the primary building up
slowly.
714
0:36:51,04 --> 0:36:54,02
Nik: But at the same time, the
same problem exists on the primary
715
0:36:54,02 --> 0:36:54,52
itself.
716
0:36:54,64 --> 0:36:56,14
Absolutely the same, right?
717
0:36:57,18 --> 0:36:59,42
Michael: And I think you can make
the opposite argument.
718
0:37:0,92 --> 0:37:2,88
Bloat is quieter.
719
0:37:3,22 --> 0:37:5,6
The reason you don't notice bloat
is because maybe it's not as
720
0:37:5,6 --> 0:37:5,92
bad.
721
0:37:5,92 --> 0:37:7,86
Nik: Yeah, we should control bloat
anyway.
722
0:37:8,08 --> 0:37:10,1
We should control bloat anyway,
right?
723
0:37:10,2 --> 0:37:13,48
And on the primary it happens exactly
in the same mechanism,
724
0:37:13,52 --> 0:37:17,6
so why should we make different
approach, apply different approach
725
0:37:17,6 --> 0:37:18,66
to replica.
726
0:37:18,74 --> 0:37:20,9
And bloat control is a whole topic
anyway, right?
727
0:37:20,9 --> 0:37:23,72
If you want to improve that experience,
it should be done on
728
0:37:23,72 --> 0:37:24,7
the primary 1st.
729
0:37:25,42 --> 0:37:29,14
Michael: And if we care about that
from a Postgres tuning perspective,
730
0:37:29,2 --> 0:37:32,36
we should probably be changing
the defaults of the various auto
731
0:37:32,36 --> 0:37:33,62
vacuum settings 1st.
732
0:37:33,64 --> 0:37:37,04
Nik: You know what, I like how
I'm pulling you into discussion
733
0:37:37,12 --> 0:37:37,74
and hackers.
734
0:37:37,84 --> 0:37:40,46
You should think and write your
opinion next time.
735
0:37:41,6 --> 0:37:43,24
Michael: Yeah, I'll try and get
braver.
736
0:37:44,2 --> 0:37:46,66
Nik: Yeah, it's a rabbit hole,
I know.
737
0:37:48,28 --> 0:37:48,78
Yeah.
738
0:37:49,7 --> 0:37:51,74
Michael: Anyway, this was great
and helpful.
739
0:37:51,74 --> 0:37:56,5
And I think I agree with you that
it feels like the vast majority
740
0:37:56,82 --> 0:38:1,02
of OLTP replicas where you're just
offloading a huge number of
741
0:38:1,02 --> 0:38:4,7
really quick queries, hot_stand
by_feedback On makes so much more
742
0:38:4,7 --> 0:38:6,22
sense to me than Off.
743
0:38:6,62 --> 0:38:10,28
But I can't agree that like all
read replicas should have it
744
0:38:10,28 --> 0:38:10,46
off.
745
0:38:10,46 --> 0:38:14,06
Like I do think there are these
people using it, probably still
746
0:38:14,06 --> 0:38:18,16
it's a good architectural choice
as these analytics replicas
747
0:38:18,48 --> 0:38:21,0
and I think you're right that it
makes sense to keep those off
748
0:38:21,0 --> 0:38:23,86
and it's nice that you can do it
on a per replica basis like
749
0:38:23,86 --> 0:38:26,48
it doesn't have to be the same
for all of your replicas which
750
0:38:26,48 --> 0:38:27,14
is cool.
751
0:38:27,44 --> 0:38:27,94
Yeah.
752
0:38:28,7 --> 0:38:29,2
Good.
753
0:38:29,38 --> 0:38:29,88
Awesome.
754
0:38:29,96 --> 0:38:30,78
Anything else?
755
0:38:31,1 --> 0:38:31,6
Nik: No.
756
0:38:31,88 --> 0:38:35,94
Just don't let your replica to
lag too often.
757
0:38:36,22 --> 0:38:37,04
Michael: Nice 1, Nik.
758
0:38:37,04 --> 0:38:37,9
Thanks so much.