1
0:0:0,06 --> 0:0:1,98
Nik: Hello, hello, this is Postgres.FM

2
0:0:2,36 --> 0:0:7,06
It is Nik, as usual, PostgresAI,
and as usual, my co-host is

3
0:0:7,06 --> 0:0:8,24
Michael pgMustard.

6
0:0:8,24 --> 0:0:9,059999
Hi, Michael.

7
0:0:11,92 --> 0:0:16,32
And today we have a guest, which
I know for quite some time.

8
0:0:16,32 --> 0:0:21,0
I think never met in person, but
somehow our paths crossed many

9
0:0:21,0 --> 0:0:24,220001
times when we write materials or
read other people's materials,

10
0:0:24,220001 --> 0:0:26,34
obviously, and also on social media.

11
0:0:27,099998 --> 0:0:29,08
It's Shaun Thomas from pgEdge.

12
0:0:29,18 --> 0:0:29,92
Hello, Shaun.

13
0:0:29,92 --> 0:0:30,92
Thank you for coming.

14
0:0:31,08 --> 0:0:33,14
Shaun: Hi, Nik, Michael.

15
0:0:33,28 --> 0:0:34,18
What's going on?

16
0:0:35,02 --> 0:0:38,6
Nik: Yeah, and it took us some
time to choose the topic because

17
0:0:38,6 --> 0:0:41,559998
you obviously write about a lot
of various stuff.

18
0:0:42,52 --> 0:0:46,02
Shaun: Yeah, it's a very eclectic
grab bag, but I like to make

19
0:0:46,02 --> 0:0:48,78
sure that cover all the interesting
topics as they come up.

20
0:0:49,86 --> 0:0:53,8
Nik: And I was tempted to choose
an AI-related topic, obviously,

21
0:0:54,0 --> 0:0:57,26
because I know you also, since
the very beginning of GPT-4 and

22
0:0:57,26 --> 0:1:1,76
so on, I know you're like very
actively use it, right?

23
0:1:2,22 --> 0:1:3,94
Shaun: Yeah, a lot of that's your
fault, honestly.

24
0:1:3,94 --> 0:1:6,78
You pulled me in, I was like, well,
that's really a lot more behind

25
0:1:6,78 --> 0:1:8,18
the scenes than I would have realized.

26
0:1:9,32 --> 0:1:9,82
Cool.

27
0:1:9,92 --> 0:1:12,5
Nik: I'm glad that I influenced
a little bit.

28
0:1:12,5 --> 0:1:13,479996
That's great to hear.

29
0:1:13,479996 --> 0:1:14,18
Thank you.

30
0:1:14,28 --> 0:1:17,64
But we chose very boring on 1 side,
like a very technical topic,

31
0:1:17,64 --> 0:1:21,44
but I think what's important is
that this topic is very often

32
0:1:21,48 --> 0:1:26,54
misunderstood, especially if you
don't spend every day in Postgres

33
0:1:26,54 --> 0:1:28,86
internals a lot of hours, right?

34
0:1:28,86 --> 0:1:33,34
So it's easy to forget how to tune
memory properly, how to deal

35
0:1:33,34 --> 0:1:37,26
with work_mem, what to choose, and
pros and cons and trade-offs

36
0:1:37,28 --> 0:1:38,8
about various choices.

37
0:1:39,06 --> 0:1:41,12
So I think topic choice is great
here.

38
0:1:41,12 --> 0:1:43,94
Do you remember when 1st time you
needed to tune work_mem?

39
0:1:45,3 --> 0:1:46,78
It was long ago, right?

40
0:1:47,92 --> 0:1:48,76
Shaun: Yeah, that's...

41
0:1:49,14 --> 0:1:52,94
Some of these older settings, like,
there's something you fiddled

42
0:1:53,0 --> 0:1:57,72
with sometime 20 years ago, and
I'm like, oh yeah, I did have

43
0:1:57,72 --> 0:2:0,84
to fight with that a lot, but it
still comes up frequently, like

44
0:2:0,84 --> 0:2:5,74
in the mailing lists and in message
boards and Discord, Slack,

45
0:2:5,74 --> 0:2:9,1
wherever, there's always someone
that's wondering about how to

46
0:2:9,1 --> 0:2:9,78
set it.

47
0:2:9,84 --> 0:2:12,72
And I wouldn't say it's a black
art, but there's definitely some

48
0:2:12,72 --> 0:2:13,76
ambiguity there.

49
0:2:14,44 --> 0:2:17,82
Nik: I think It takes some significant
time to understand nuances

50
0:2:17,92 --> 0:2:18,78
around it.

51
0:2:19,2 --> 0:2:22,58
Because what people expect, obviously,
is that work_mem is some

52
0:2:22,58 --> 0:2:24,18
limit for the whole thing.

53
0:2:24,64 --> 0:2:26,14
It's like the number 1 problem.

54
0:2:26,82 --> 0:2:28,52
And yeah, it's obviously not so.

55
0:2:28,52 --> 0:2:32,76
So you published a blog post recently,
Let's Write Extension,

56
0:2:33,56 --> 0:2:36,2
that will help tune work_mem.

57
0:2:36,66 --> 0:2:40,82
I see it like 2 goals of this blog
post.

58
0:2:40,84 --> 0:2:42,32
1 is let's write extension.

59
0:2:43,18 --> 0:2:47,0
And honestly, my opinion is maybe
not popular, but I think we

60
0:2:47,0 --> 0:2:51,02
should write extensions less because
they are not supported by

61
0:2:51,02 --> 0:2:52,1
managed providers.

62
0:2:52,2 --> 0:2:53,4
Unless it's pg_tle.

63
0:2:54,16 --> 0:2:57,28
That's okay, because it will be
possible to run it anywhere.

64
0:2:57,34 --> 0:3:1,82
And also, like yesterday, someone
posted how many vulnerabilities

65
0:3:5,08 --> 0:3:7,06
various third-party extensions
bring.

66
0:3:7,8 --> 0:3:11,3
Then platforms like Neon, Supabase
and others are badly affected.

67
0:3:11,8 --> 0:3:14,76
So this is 1 more reason not to
write extension.

68
0:3:15,36 --> 0:3:18,28
So We can spend some time there,
I think, to talk about writing

69
0:3:18,28 --> 0:3:19,84
extensions, because it's still
useful.

70
0:3:19,92 --> 0:3:22,7
Of course, my opinion is very practical.

71
0:3:23,1 --> 0:3:26,12
Of course, we should write extensions,
but we should understand

72
0:3:26,12 --> 0:3:29,98
that probably it will be very limited
in terms of use.

73
0:3:30,4 --> 0:3:33,62
But obviously, the 2nd goal is
work_mem tuning.

74
0:3:34,22 --> 0:3:39,16
And I'm very curious, and my goal
for myself today is to see

75
0:3:39,72 --> 0:3:42,74
how our visions are aligned in
memory tuning.

76
0:3:44,02 --> 0:3:48,74
In the beginning, I checked some
old EnterpriseDB posts and

77
0:3:48,74 --> 0:3:50,58
I saw a formula, a very simple
formula.

78
0:3:50,58 --> 0:3:52,66
And I'm very curious what you think
about this.

79
0:3:52,78 --> 0:3:58,28
Let's take whole memory, divide
by 4, usually it's done with

80
0:3:58,28 --> 0:3:59,4
shared_buffers, right?

81
0:3:59,44 --> 0:4:1,5
And then divide by max_connections.

82
0:4:1,88 --> 0:4:3,36
And this is our work_mem.

83
0:4:3,48 --> 0:4:4,9
What do you think about this?

84
0:4:6,04 --> 0:4:9,48
Shaun: So I actually did a blog
post like that while I was at

85
0:4:9,48 --> 0:4:12,32
EDB, and I think my formula was
slightly different.

86
0:4:12,84 --> 0:4:18,42
I said something like divide by
max_connections and divide by

87
0:4:18,42 --> 0:4:18,92
5.

88
0:4:19,64 --> 0:4:23,44
Because the assumption there was
every query could have some

89
0:4:23,44 --> 0:4:28,04
amount of query nodes and since
each of those can create an instance

90
0:4:28,04 --> 0:4:33,28
of work_mem, Assuming 1 is probably
a bad idea, because only the

91
0:4:33,28 --> 0:4:35,94
most simple queries will only have
1 query node.

92
0:4:36,58 --> 0:4:39,56
Nik: Yeah, so 5 is quite, like
I would say, conservative.

93
0:4:40,52 --> 0:4:41,5
Shaun: Yeah, it's aggressive.

94
0:4:41,58 --> 0:4:43,84
It's aggressively conservative
because I know that's, for 1,

95
0:4:43,84 --> 0:4:45,92
that's how the Postgres community
tends to operate.

96
0:4:45,92 --> 0:4:48,84
They use what they call sensible
defaults.

97
0:4:49,7 --> 0:4:53,1
And it's an easy way to avoid out
of memorying yourself until

98
0:4:53,1 --> 0:4:55,18
you can have more time to tweak
it.

99
0:4:55,44 --> 0:4:56,26
Nik: That's interesting.

100
0:4:56,28 --> 0:4:59,72
Also, if we think about both of
these approaches, and you're

101
0:4:59,72 --> 0:5:2,8
obviously more advanced than that
simple 1.

102
0:5:3,34 --> 0:5:7,9
Like we understand that each query
can consume multiple times

103
0:5:7,9 --> 0:5:8,6
of work_mem.

104
0:5:9,28 --> 0:5:13,64
If we go with this formula to RDS,
you know what will happen,

105
0:5:13,64 --> 0:5:14,14
right?

106
0:5:14,72 --> 0:5:15,54
Shaun: No, I don't.

107
0:5:15,54 --> 0:5:16,5
What will RDS do?

108
0:5:16,5 --> 0:5:17,78
Nik: max_connections, 5, 000.

109
0:5:18,52 --> 0:5:19,7
Even for small clusters.

110
0:5:19,7 --> 0:5:20,28
Oh, geez.

111
0:5:21,04 --> 0:5:21,54
Yes.

112
0:5:21,9 --> 0:5:22,52
Shaun: All right.

113
0:5:22,96 --> 0:5:25,68
Nik: And they expect that everyone
will probably use RDS Proxy

114
0:5:25,68 --> 0:5:28,18
But we observe a lot of customers
who come to us.

115
0:5:28,18 --> 0:5:32,32
They don't know PgBouncer no,
this proxy only some poolers

116
0:5:32,32 --> 0:5:38,3
or on application side And those
guys who run application nodes,

117
0:5:38,3 --> 0:5:40,7
they put it to Kubernetes with
auto-scaling.

118
0:5:41,4 --> 0:5:45,61
So nobody knows which, like, probability
of reaching those 5,

119
0:5:45,61 --> 0:5:46,62
000 connections.

120
0:5:47,22 --> 0:5:50,0
Shaun: Yeah, we could have a whole
another conversation on pooling.

121
0:5:50,22 --> 0:5:52,64
My opinion there is like you should
never connect directly to

122
0:5:52,64 --> 0:5:53,14
Postgres.

123
0:5:53,42 --> 0:5:55,24
Nik: Right, yeah, poolers should
be inside.

124
0:5:55,24 --> 0:5:56,24
Yeah, I agree.

125
0:5:56,4 --> 0:6:0,64
But this is the reality and RDS
is the most popular 1 And people

126
0:6:0,64 --> 0:6:6,52
come with 16 vCPUs, 2,500 or 5,000
max_connections.

127
0:6:7,24 --> 0:6:7,72
Shaun: Yeah.

128
0:6:7,72 --> 0:6:10,84
At that point you get to an area
where you have to say, sample

129
0:6:10,84 --> 0:6:13,18
your number of active connections
and then it's a little more

130
0:6:13,18 --> 0:6:15,04
involved, but it's roughly the
same idea.

131
0:6:15,28 --> 0:6:15,54
Nik: Yeah.

132
0:6:15,54 --> 0:6:16,9
So this is great.

133
0:6:17,38 --> 0:6:19,08
And this is like for many years.

134
0:6:19,08 --> 0:6:22,36
It was with number 1 like approach
We basically we tune based

135
0:6:22,36 --> 0:6:27,86
on observations feedback loop,
right but I really want something

136
0:6:27,86 --> 0:6:32,64
more like not looking into actual
thing, but predicting something,

137
0:6:32,64 --> 0:6:32,84
right?

138
0:6:32,84 --> 0:6:36,2
Because sometimes you launch new
service, or sometimes you expect

139
0:6:36,2 --> 0:6:41,76
some growth, and observing production,
it just doesn't feel super,

140
0:6:41,76 --> 0:6:43,42
it's very practical, of course.

141
0:6:43,66 --> 0:6:45,76
And it can work in either way as
well.

142
0:6:45,76 --> 0:6:51,2
For example, if we take your formula,
divide by 5, and even drop

143
0:6:51,2 --> 0:6:55,82
that 5, just no additional dividing
at all.

144
0:6:56,4 --> 0:7:0,04
And then we say, you know what,
we still need more memory.

145
0:7:0,24 --> 0:7:4,2
And we just observe there's a lot
of like page cache is huge

146
0:7:4,74 --> 0:7:7,74
Because work_mem is not allocated
immediately, right?

147
0:7:7,74 --> 0:7:12,04
It's allocated in chunks like it's
gradually So work_mem is

148
0:7:12,04 --> 0:7:16,26
limit for 1 operation inside query
But it doesn't like it doesn't

149
0:7:16,26 --> 0:7:20,02
say that within a query, each
operation will have work_mem, maybe

150
0:7:20,02 --> 0:7:20,52
less.

151
0:7:20,82 --> 0:7:26,26
If we observe from reality that
we actually use much less, we

152
0:7:26,26 --> 0:7:27,9
can over-commit here.

153
0:7:28,04 --> 0:7:29,06
This is what we do.

154
0:7:29,06 --> 0:7:34,46
Like we say, We go beyond theoretical
limits because we see that

155
0:7:34,46 --> 0:7:37,54
practically we don't have out-of-memory
risks.

156
0:7:38,24 --> 0:7:40,92
Theoretically we have them, but
practically no.

157
0:7:40,96 --> 0:7:44,76
And I know such clusters, huge
clusters, they are running, like,

158
0:7:44,76 --> 0:7:46,22
just ignoring this formula.

159
0:7:46,22 --> 0:7:49,52
And it's okay because majority
of queries they actually use tiny

160
0:7:49,54 --> 0:7:50,46
amount of work_mem.

161
0:7:50,46 --> 0:7:51,88
What do you think about this?

162
0:7:52,36 --> 0:7:57,74
Overall it's like a bag of very
different ideas and how to find

163
0:7:57,74 --> 0:8:0,26
better like path to some recipe?

164
0:8:0,76 --> 0:8:5,2
Shaun: The recipe I usually use
and the 1 I started with, honestly,

165
0:8:5,44 --> 0:8:8,38
is to use the database statistics.

166
0:8:9,06 --> 0:8:12,72
I think it's pg_stat_database, or
it could be in pg_database, where

167
0:8:12,72 --> 0:8:16,26
it tracks the number of temp files
and the number of temp file

168
0:8:16,26 --> 0:8:16,76
size.

169
0:8:17,32 --> 0:8:20,78
So what I do is I find out the
average size of the temp files

170
0:8:20,78 --> 0:8:25,6
Postgres is producing and then
I add that to whatever the work_mem

171
0:8:25,6 --> 0:8:26,42
setting is.

172
0:8:26,92 --> 0:8:30,06
So if it starts out at the default
4 megs and then I find out

173
0:8:30,06 --> 0:8:34,62
that Postgres is producing on average
8 megabyte temporary files.

174
0:8:34,96 --> 0:8:37,96
I'll add some padding onto that
and add that to the work_mem so

175
0:8:37,96 --> 0:8:40,46
I end up with 12 or 16 would be
my setting.

176
0:8:40,88 --> 0:8:45,26
And that reduces all your disk
spills by 90 plus percent and

177
0:8:45,26 --> 0:8:47,18
that usually addresses the problem.

178
0:8:47,66 --> 0:8:48,48
Nik: That's interesting.

179
0:8:48,48 --> 0:8:51,24
Let's have some small pause here.

180
0:8:51,46 --> 0:8:54,94
1st of all, obviously you take
shared blocks dirtied?

181
0:8:54,94 --> 0:8:58,62
Oh, not shared, temp blocks dirtied,
right?

182
0:9:0,52 --> 0:9:3,74
Shaun: Temp files and temp bytes,
like literally those 2 fields.

183
0:9:4,74 --> 0:9:6,56
Nik: Ah, okay, in pg_stat_database.

184
0:9:6,56 --> 0:9:8,36
I'm thinking about pg_stat_statements.

185
0:9:8,6 --> 0:9:12,26
This is where I struggle, because
Postgres has a huge gap.

186
0:9:12,26 --> 0:9:13,86
It doesn't have a number of calls.

187
0:9:14,24 --> 0:9:15,76
It has only a number of transactions.

188
0:9:16,64 --> 0:9:20,94
So you need to divide by calls
to have average temporary bytes

189
0:9:20,94 --> 0:9:22,42
written per call, right?

190
0:9:22,42 --> 0:9:24,62
But we don't have it in the
pg_stat_database.

191
0:9:24,9 --> 0:9:27,44
Shaun: Yeah, you just have a very
coarse heuristic that just

192
0:9:27,44 --> 0:9:31,36
says, I produced this many of temporary
files and these are how

193
0:9:31,36 --> 0:9:32,26
big they were.

194
0:9:32,5 --> 0:9:35,58
So you don't really have enough
fine-grained ability to see how

195
0:9:35,58 --> 0:9:38,22
big they were like for each individual
thing.

196
0:9:38,94 --> 0:9:42,72
Nik: Yeah, that's why I look at
pg_stat_statements because it has

197
0:9:42,72 --> 0:9:43,22
calls.

198
0:9:43,26 --> 0:9:46,62
Of course, it maybe tracks not
all queries, but by default only

199
0:9:46,62 --> 0:9:47,12
5, 000.

200
0:9:48,14 --> 0:9:50,04
pg_stat_statements.max is 5, 000.

201
0:9:50,58 --> 0:9:54,96
But we have both temporary blocks
written and calls.

202
0:9:54,96 --> 0:9:58,42
We can divide 1 by another and
get average per call.

203
0:9:58,42 --> 0:10:2,34
But then I literally thought about
this last week, even before

204
0:10:2,36 --> 0:10:6,12
your blog post, because we also,
we deal with temporary files

205
0:10:6,12 --> 0:10:9,52
generation in many customers, it's
normal, they don't tune it

206
0:10:9,52 --> 0:10:12,18
and then it becomes a problem.

207
0:10:12,26 --> 0:10:13,18
Why it's a problem?

208
0:10:13,18 --> 0:10:16,3
Because spilling to disk is slow,
right?

209
0:10:16,56 --> 0:10:17,78
So it affects performance.

210
0:10:18,24 --> 0:10:20,32
And I was thinking, OK, we have
average.

211
0:10:20,32 --> 0:10:22,96
We know, for example, 8 megabytes,
as you said, like per call,

212
0:10:22,96 --> 0:10:23,48
for example.

213
0:10:23,48 --> 0:10:25,58
And we have 8 megabytes work_mem.

214
0:10:25,86 --> 0:10:28,22
Thinking, OK, we will set up 16.

215
0:10:28,84 --> 0:10:32,23
But then I was thinking, What if
it's average, right?

216
0:10:32,23 --> 0:10:37,66
What if 90% of those queries are
tiny and the remaining 10, they

217
0:10:37,66 --> 0:10:38,98
need 100 megabytes?

218
0:10:39,64 --> 0:10:41,94
Shaun: Yeah, I usually run into
situations like that.

219
0:10:41,94 --> 0:10:44,0
You just kind of have to live with
the disk spilling.

220
0:10:44,54 --> 0:10:47,04
You obviously don't want to get
it past a certain amount where

221
0:10:47,04 --> 0:10:49,84
your queries are having to fight
for disk I/O.

222
0:10:49,84 --> 0:10:54,28
But at the same time, if the average
is too high, you can't make

223
0:10:54,28 --> 0:10:55,46
it infinitely high.

224
0:10:55,9 --> 0:10:56,26
Nik: Yeah.

225
0:10:56,26 --> 0:11:0,72
So it's a problem of averages and
lack of percentiles again here.

226
0:11:0,72 --> 0:11:1,22
Right.

227
0:11:2,06 --> 0:11:4,64
Michael: I've seen once you understand
your workload, I've seen

228
0:11:4,64 --> 0:11:7,44
some people tweak it on a per user.

229
0:11:7,44 --> 0:11:10,9
Instead of setting it globally,
keep the global low, but then

230
0:11:10,9 --> 0:11:14,28
set an application, like if there's
a reporting application that

231
0:11:14,28 --> 0:11:18,52
we know only ever fires off 1 or
2 queries at a time, set its

232
0:11:18,52 --> 0:11:19,6
work_mem higher.

233
0:11:19,6 --> 0:11:22,44
Have you seen things like that
or done that in the past, Shaun?

234
0:11:22,74 --> 0:11:25,9
Shaun: I haven't, but that would
definitely be another way out

235
0:11:25,9 --> 0:11:26,32
of it.

236
0:11:26,32 --> 0:11:30,4
The fact that you can set a user
level session GUCs is a way

237
0:11:30,4 --> 0:11:31,44
out of a lot of ways.

238
0:11:31,44 --> 0:11:34,94
In the past I've used that to turn
off nested looping for example.

239
0:11:35,22 --> 0:11:38,16
Back when the planner wasn't so
great and it still occasionally

240
0:11:38,16 --> 0:11:40,96
runs into stuff like this, you
get a query that just resists

241
0:11:41,1 --> 0:11:44,44
every attempt to get to run with
the same plan.

242
0:11:44,44 --> 0:11:47,9
So you just, You take out a hammer
and you knock out the parts

243
0:11:47,9 --> 0:11:50,18
you don't want to do and magically
it starts working.

244
0:11:50,38 --> 0:11:53,3
Now that's not so much a problem
with Postgres 19 coming with

245
0:11:53,3 --> 0:11:57,56
the new hint syntax So you can
tell it to prefer certain paths

246
0:11:57,56 --> 0:12:2,42
but before that you had a CTE or
some kind of wall of materialization

247
0:12:2,56 --> 0:12:5,62
you had to construct to make it
act a certain way.

248
0:12:6,22 --> 0:12:10,02
And 1 of those is, yeah, the coarse
knobs of adjusting the planner

249
0:12:10,02 --> 0:12:13,64
or working with work_mem or some
other thing.

250
0:12:14,34 --> 0:12:18,48
Nik: Yeah, and It's interesting
that work_mem is not, if you select

251
0:12:18,48 --> 0:12:23,5
star from pg_settings, work_mem is
not sitting in the Planner category.

252
0:12:23,68 --> 0:12:24,78
It's about memory.

253
0:12:25,08 --> 0:12:28,74
But it affects how Planner chooses
the plan.

254
0:12:29,06 --> 0:12:31,08
So it's also an interesting thing.

255
0:12:31,16 --> 0:12:32,64
It might change the plan.

256
0:12:33,54 --> 0:12:36,54
So yeah, and the good thing that
you can set either at session

257
0:12:36,54 --> 0:12:41,08
level or at user level as well,
alter role.

258
0:12:41,48 --> 0:12:45,64
So I don't think people use it
often, but it's a good thing for

259
0:12:45,66 --> 0:12:48,76
some heavy queries to move this
workload to a different user

260
0:12:48,76 --> 0:12:49,26
completely.

261
0:12:49,9 --> 0:12:54,3
But also this makes understanding
of the whole picture harder,

262
0:12:55,28 --> 0:12:55,78
slightly.

263
0:12:55,84 --> 0:12:59,04
Shaun: Yeah, but if you have a
mixed use case system, you have

264
0:12:59,04 --> 0:13:1,58
to choose your battles, right?

265
0:13:1,82 --> 0:13:1,98
Nik: So

266
0:13:1,98 --> 0:13:5,02
Shaun: I think Michael's point
is very salient because if you

267
0:13:5,02 --> 0:13:8,6
have an OLTP system that's taking,
that's 90% of doing what it's

268
0:13:8,6 --> 0:13:11,32
doing and every once in a while
a huge query sneaks in where

269
0:13:11,32 --> 0:13:14,92
you've got a batch job you need
to run like setting for the long

270
0:13:14,92 --> 0:13:19,46
batch job, the transaction length
to some larger amount.

271
0:13:20,28 --> 0:13:22,9
So there's lots of other different
settings you might do for

272
0:13:22,9 --> 0:13:25,68
that 1 particular thing that you
wouldn't want to change for

273
0:13:25,68 --> 0:13:26,54
the whole server.

274
0:13:26,98 --> 0:13:29,86
So usually in a case like that
I'd say make a reporting system

275
0:13:29,86 --> 0:13:33,84
so you can direct your OLTP stuff
there but that's a architectural

276
0:13:33,96 --> 0:13:35,42
thing for a later time.

277
0:13:36,34 --> 0:13:39,96
Michael: 1 thing that's changed
in recent years is the introduction

278
0:13:39,96 --> 0:13:43,58
of hash_mem_multiplier and the change
of the default of it from

279
0:13:43,58 --> 0:13:44,8
1 to 2.

280
0:13:45,04 --> 0:13:47,92
It was really great in your blog
post to see you taking that

281
0:13:47,92 --> 0:13:51,38
into account when estimating how
much a single query is going

282
0:13:51,38 --> 0:13:56,16
to take, but I haven't seen that
in many other blog posts even

283
0:13:56,16 --> 0:13:57,34
since it was changed.

284
0:13:57,66 --> 0:14:0,9
So do you think maybe some of the
formulas you might come across

285
0:14:1,4 --> 0:14:6,1
via search or even LLMs these days,
might be maybe not even conservative

286
0:14:6,16 --> 0:14:9,36
enough because there's this multiplier
now for quite a few operations.

287
0:14:9,96 --> 0:14:12,78
Shaun: Yeah, the problem with using
the hash_mem_multiplier

288
0:14:12,92 --> 0:14:17,36
as part of a generic formula is,
Unless you know how many hash

289
0:14:17,36 --> 0:14:19,96
memory operations are going to
occur within a query, you're just

290
0:14:19,96 --> 0:14:20,46
guessing.

291
0:14:21,04 --> 0:14:23,86
At least with the number of nodes,
you can say, I'll say there's

292
0:14:23,86 --> 0:14:26,88
like a 4 or 5 average or a 2 or
3 average or whatever it is for

293
0:14:26,88 --> 0:14:29,52
your platform, you can say, just
multiply it out.

294
0:14:29,54 --> 0:14:32,6
But with hash_mem_multiplier, it's just, it's
another factor that complicates

295
0:14:32,6 --> 0:14:34,6
the formula in a way that's not
really meaningful.

296
0:14:34,6 --> 0:14:36,36
Because we're already estimating,
right?

297
0:14:36,5 --> 0:14:38,86
We're already just picking a number
out of the sky about how

298
0:14:38,86 --> 0:14:40,84
many query nodes and how many things
that's going to be in your

299
0:14:40,84 --> 0:14:41,34
estimate.

300
0:14:41,52 --> 0:14:44,84
And even the extension that I wrote
is very coarse, it's just

301
0:14:45,06 --> 0:14:46,74
Not every hash node will be 2x.

302
0:14:46,76 --> 0:14:50,0
Not every, it doesn't handle
Appends, it doesn't walk the entire

303
0:14:50,0 --> 0:14:50,38
tree.

304
0:14:50,38 --> 0:14:54,16
Like it was a 1st approximation
to give a guess at a worst case

305
0:14:54,16 --> 0:14:54,66
scenario.

306
0:14:54,86 --> 0:14:57,38
Nik: Worst case scenario is a key
here, yeah, I agree.

307
0:14:57,56 --> 0:15:0,04
Michael: I understand that, but
I think the worst case scenario

308
0:15:0,04 --> 0:15:5,28
has just got worse since the, so
the multiplier of 2 means everybody's

309
0:15:5,6 --> 0:15:10,94
worst-case scenario has got worse
because those nodes could well

310
0:15:10,96 --> 0:15:12,58
could take more memory now.

311
0:15:12,88 --> 0:15:15,48
Shaun: To an extent but I believe
the reason they actually put

312
0:15:15,48 --> 0:15:18,26
that there was to constrain it,
constrain memory more.

313
0:15:18,56 --> 0:15:21,82
Previously, I could have this wrong,
but I remember a couple

314
0:15:21,82 --> 0:15:25,12
of threads in the mailing list
where someone was saying that

315
0:15:25,12 --> 0:15:28,14
hash memory was unbounded by work_mem
at all.

316
0:15:28,14 --> 0:15:31,4
So it was causing them to get OOMs
because 1 query would create

317
0:15:31,4 --> 0:15:33,9
an infinitely large hash and it
would just crash the system.

318
0:15:34,28 --> 0:15:39,4
So now they actually, hash mems,
honor a limit which they didn't

319
0:15:39,4 --> 0:15:39,9
before.

320
0:15:40,94 --> 0:15:42,78
So yes and no.

321
0:15:43,38 --> 0:15:46,4
Nik: Yeah, and your extension is
looking at the plan, right?

322
0:15:46,4 --> 0:15:51,42
So it knows exactly how many hashing
happened and other operations.

323
0:15:52,06 --> 0:15:53,08
That's a great idea.

324
0:15:53,42 --> 0:15:55,96
I think it's possible to apply
it at scale.

325
0:15:56,28 --> 0:15:59,66
For example, at least if, very
roughly, we talk about estimates.

326
0:15:59,86 --> 0:16:4,7
What if we take all queries from
pg_stat_statements, 5, 000

327
0:16:5,14 --> 0:16:8,5
by default, maximum, and just collect
generic plans.

328
0:16:8,94 --> 0:16:9,44
Yeah.

329
0:16:9,72 --> 0:16:10,64
That's it, right?

330
0:16:10,64 --> 0:16:13,82
So your extension, obviously, it
was like at a micro level, just

331
0:16:13,82 --> 0:16:15,18
a single query, right?

332
0:16:15,18 --> 0:16:20,32
And for those who haven't seen,
it's just producing some additional

333
0:16:20,6 --> 0:16:25,52
hints, log messages, hints what
peak usage for this query could

334
0:16:25,52 --> 0:16:26,02
be.

335
0:16:26,26 --> 0:16:30,46
And then you can understand, like
for session level, at least

336
0:16:30,46 --> 0:16:34,02
for this query, you can understand
how to avoid spilling to temporary

337
0:16:34,02 --> 0:16:35,26
files, to disk.

338
0:16:35,66 --> 0:16:38,9
But we can apply it to macro level,
right?

339
0:16:38,9 --> 0:16:40,86
With just collecting generic plans.

340
0:16:41,38 --> 0:16:44,76
Of course, it won't be super precise
because custom plans might

341
0:16:44,76 --> 0:16:49,44
have very different opinion how
many hashing or ordering should

342
0:16:49,44 --> 0:16:49,94
happen.

343
0:16:50,22 --> 0:16:52,98
But it can be good enough for estimate,
I think.

344
0:16:53,3 --> 0:16:54,02
Do you agree?

345
0:16:55,56 --> 0:16:56,06
Shaun: Yeah.

346
0:16:57,8 --> 0:17:1,44
So the way I wrote it was, the
extension was basically to exercise

347
0:17:1,62 --> 0:17:5,2
an example of, here's how you write
a function that a user can

348
0:17:5,2 --> 0:17:5,7
call.

349
0:17:5,82 --> 0:17:8,04
Here's how you can leverage GUCs.

350
0:17:8,2 --> 0:17:10,02
Here's how you can override hooks.

351
0:17:10,16 --> 0:17:13,74
But because of all that, yeah,
you can, you've got to use a little

352
0:17:13,74 --> 0:17:16,16
function you can call and it's
like a debugging process.

353
0:17:16,16 --> 0:17:18,46
I've got this huge query, I might
want to run in production.

354
0:17:18,76 --> 0:17:20,1
Will it blow things up?

355
0:17:20,28 --> 0:17:23,1
Oh, it could use up to a gigabyte
of memory.

356
0:17:23,1 --> 0:17:25,32
Whoops, let me go back and look
at that again.

357
0:17:25,32 --> 0:17:28,22
Or maybe I want to adjust my
work_mem to make it use less memory

358
0:17:28,22 --> 0:17:29,24
overall in general.

359
0:17:30,04 --> 0:17:32,72
But then yeah, you could in theory
point whatever's in pg_stat_statements

360
0:17:32,72 --> 0:17:36,04
at it and just send
them all through the function

361
0:17:36,04 --> 0:17:40,04
in the loop, or just as part of
a query call and get some kind

362
0:17:40,04 --> 0:17:41,74
of maximum estimate out of that.

363
0:17:42,04 --> 0:17:44,16
Nik: Yeah, that's I think quite
doable.

364
0:17:45,22 --> 0:17:48,02
And it could help to tune better,
actually, right?

365
0:17:48,54 --> 0:17:51,26
Yeah, that's good that you aligned
here as well.

366
0:17:51,26 --> 0:17:53,66
I have a very interesting specific
question.

367
0:17:54,44 --> 0:17:57,42
Maybe you or Michael knows.

368
0:17:58,32 --> 0:18:6,34
So Why does Postgres crash when
it reaches, like when there is

369
0:18:6,34 --> 0:18:7,32
not enough memory?

370
0:18:7,82 --> 0:18:13,12
Instead of crashing 1 backend somehow,
right?

371
0:18:13,26 --> 0:18:14,76
It crashes the whole cluster.

372
0:18:14,76 --> 0:18:18,18
Of course, if it's crashed the
backend abnormally, I understand

373
0:18:18,18 --> 0:18:19,3
like for protection.

374
0:18:21,56 --> 0:18:23,0
We need this to restart.

375
0:18:23,0 --> 0:18:23,6
I understand that.

376
0:18:23,6 --> 0:18:29,72
But I know RDS had some extension
or logic, proprietary 1, to

377
0:18:29,72 --> 0:18:34,54
just to give up only those backends
which cannot be, cannot like

378
0:18:34,54 --> 0:18:37,76
switch to memory, but not to lose
others.

379
0:18:38,46 --> 0:18:41,18
But then they somehow removed it,
deprecated.

380
0:18:41,82 --> 0:18:45,08
And I'm very curious what's happening
in this area and why Postgres

381
0:18:46,0 --> 0:18:49,6
cannot protect other, like others
already running give them a

382
0:18:49,6 --> 0:18:50,96
chance to complete, right?

383
0:18:51,5 --> 0:18:52,48
Do you know, no?

384
0:18:52,48 --> 0:18:54,56
Like, I'm very curious about this.

385
0:18:54,56 --> 0:18:55,34
I think

386
0:18:55,9 --> 0:18:56,98
Michael: it's a Linux thing.

387
0:18:56,98 --> 0:18:59,94
Correct me if I'm wrong, Shaun,
but I think it's the out-of-memory

388
0:19:0,06 --> 0:19:2,92
killer and the fact we've got the
postmaster.

389
0:19:4,28 --> 0:19:7,84
Shaun: So yeah, if you've got a
backend that's using a lot of

390
0:19:7,84 --> 0:19:12,72
RAM and Postgres gets killed arbitrarily
by the kernel, sure,

391
0:19:12,72 --> 0:19:15,92
that'll cause it to complain about
stuff.

392
0:19:16,38 --> 0:19:20,6
And I believe if there's no memory
available and you do a memory

393
0:19:20,6 --> 0:19:23,9
request, you'll just get back an
error and your backend won't

394
0:19:23,9 --> 0:19:24,24
die.

395
0:19:24,24 --> 0:19:27,4
I need to run a test to see actually
what happens if you run

396
0:19:27,4 --> 0:19:29,86
into a situation like that where
you've turned off over commit,

397
0:19:29,86 --> 0:19:32,52
for example, so you actually get
an out of memory error from

398
0:19:32,52 --> 0:19:35,18
your backend if it tries to allocate
more than is available.

399
0:19:35,9 --> 0:19:39,02
But yeah, I think that's, I think
what it is that the OOM killer

400
0:19:39,14 --> 0:19:43,3
terminates the backend, and then
since that backend can't clean

401
0:19:43,3 --> 0:19:47,56
up after itself, you could have
corrupted memory context because

402
0:19:47,56 --> 0:19:51,1
Postgres uses arenas with shared
memory context.

403
0:19:51,1 --> 0:19:54,92
And if you can't roll back your
arena back or clean out your

404
0:19:54,92 --> 0:19:58,5
context, the entire shared state
of the buffers could be corrupt

405
0:19:58,5 --> 0:20:0,68
in some way that you can't really
consider.

406
0:20:1,24 --> 0:20:4,44
So as a defensive measure, Postgres
shuts the entire thing down

407
0:20:4,44 --> 0:20:5,26
and then restarts.

408
0:20:6,28 --> 0:20:9,3
But aside from that I'm not entirely
sure.

409
0:20:9,32 --> 0:20:11,46
We need someone who's worked with
it.

410
0:20:11,6 --> 0:20:14,34
Andres or Masahiko Sawada, they would
know.

411
0:20:15,14 --> 0:20:16,24
Or maybe Álvaro.

412
0:20:17,28 --> 0:20:21,82
Nik: So yeah, I practically I would
prefer to have errors for

413
0:20:21,82 --> 0:20:25,58
specific sessions, but all others
are still working.

414
0:20:25,68 --> 0:20:29,78
And if we see that we're about
to be out of memory, that's practically

415
0:20:30,48 --> 0:20:31,3
more convenient.

416
0:20:32,32 --> 0:20:34,64
Similar case is with out-of-disk
space.

417
0:20:34,82 --> 0:20:36,68
It's not a good place to be.

418
0:20:36,9 --> 0:20:41,24
And I know some people place some
file filled by zeros and they

419
0:20:41,24 --> 0:20:43,98
just remove it if urgency happens,
right?

420
0:20:45,06 --> 0:20:47,78
Anyway, yeah, that's interesting
question as well.

421
0:20:48,34 --> 0:20:49,18
Good, okay.

422
0:20:49,64 --> 0:20:55,08
Speaking of writing extensions,
I have a feeling 20 years ago,

423
0:20:55,08 --> 0:20:58,02
Postgres is extensible, extensions
are great, and then managed

424
0:20:58,02 --> 0:21:2,98
services, managed providers, they
just somehow brought us to

425
0:21:2,98 --> 0:21:7,46
the point when extensions are against
extensibility, because

426
0:21:7,46 --> 0:21:9,52
they need to approve it.

427
0:21:9,52 --> 0:21:14,08
And it's like with all those scary
stories about vulnerabilities,

428
0:21:15,06 --> 0:21:17,06
their approval rates will slow
down.

429
0:21:18,26 --> 0:21:22,4
Shaun: Yeah, so the issue with
Postgres extensions is they're

430
0:21:22,5 --> 0:21:24,06
very much a double-edged sword.

431
0:21:24,52 --> 0:21:28,52
It adds this new functionality
that Postgres never had, and that's

432
0:21:28,52 --> 0:21:28,94
amazing.

433
0:21:28,94 --> 0:21:32,18
We wouldn't have pgvector, for
example, right now, or PostGIS

434
0:21:32,18 --> 0:21:35,26
or any of those other huge extensions
everyone relies on now,

435
0:21:35,38 --> 0:21:36,3
without it.

436
0:21:36,72 --> 0:21:41,4
But at the same time, the Postgres
extension mechanisms, and

437
0:21:41,4 --> 0:21:44,12
I now know this first-hand since
I've played with it with my

438
0:21:44,12 --> 0:21:46,88
tutorials, is awful.

439
0:21:47,68 --> 0:21:49,34
Like, for example, the hook system.

440
0:21:49,6 --> 0:21:53,3
There's no, like you normally see
register hook, some kind of

441
0:21:53,3 --> 0:21:56,28
function you'd call that would
put your hook on a stack and it

442
0:21:56,28 --> 0:21:59,64
would control that whatever the
hooks are called in, you wouldn't

443
0:21:59,64 --> 0:22:4,5
be able to accidentally not call
the next extension in the list.

444
0:22:4,54 --> 0:22:6,88
Right now, you actually have to
check and see if there's a next

445
0:22:6,88 --> 0:22:7,94
or previous hook.

446
0:22:8,08 --> 0:22:11,86
If there is, save it for later
and call it yourself in your extension.

447
0:22:12,18 --> 0:22:14,6
And if you don't do that, or if
someone decides they want to

448
0:22:14,6 --> 0:22:17,72
be a bad actor and don't do that
in their extension, the whole

449
0:22:17,72 --> 0:22:18,84
stack gets broken.

450
0:22:19,54 --> 0:22:21,76
The other issue is there's no sandbox.

451
0:22:22,36 --> 0:22:25,68
Even browsers, right, just some
user's browser they're using

452
0:22:25,68 --> 0:22:31,22
to browse the web has a sandbox
per thing, and Postgres has no

453
0:22:31,22 --> 0:22:32,94
even concept of that right now.

454
0:22:33,08 --> 0:22:36,48
So you end up with CVEs that get
escalated into the core because

455
0:22:37,28 --> 0:22:40,22
the Postgres extensions are literally
calling core structures.

456
0:22:40,52 --> 0:22:42,78
There's no API for any of this
stuff.

457
0:22:42,78 --> 0:22:45,06
It's just, oh, you know Postgres
function?

458
0:22:45,06 --> 0:22:46,86
Go ahead and include it and call
it internally.

459
0:22:46,92 --> 0:22:50,22
You have direct core access to
every part of Postgres all the

460
0:22:50,22 --> 0:22:50,72
time.

461
0:22:51,42 --> 0:22:53,4
Nik: This would be great to fix,
by the way.

462
0:22:53,4 --> 0:22:55,82
Do you know anyone looking in this
direction?

463
0:22:56,26 --> 0:23:0,36
Shaun: I don't, but I can't imagine
that nobody's thought about

464
0:23:0,36 --> 0:23:3,76
this or nobody's thought to, can
we add sandboxing or can we

465
0:23:3,76 --> 0:23:6,54
add a hook management mechanism
of some kind.

466
0:23:6,56 --> 0:23:9,82
But it's just low priority because
right now they, all the core

467
0:23:9,82 --> 0:23:13,74
developers work on core Postgres
and their interest in extensions

468
0:23:13,86 --> 0:23:15,72
is tangential at best, right?

469
0:23:15,72 --> 0:23:18,52
Because if they want something,
they just put it in the core,

470
0:23:18,52 --> 0:23:20,64
and if someone else wants something,
they can add it for their

471
0:23:20,64 --> 0:23:21,26
own convenience.

472
0:23:22,14 --> 0:23:26,32
Nik: And the core just experienced
the biggest, in terms of CVEs

473
0:23:26,32 --> 0:23:28,94
fixed, the biggest minor release,
28.

474
0:23:29,02 --> 0:23:32,8
Shaun: Oh, the 28 of the 28 CVEs
they fixed in 19, yeah.

475
0:23:32,8 --> 0:23:34,28
Nik: 18.6, yeah.

476
0:23:34,28 --> 0:23:35,32
Shaun: Yeah, right, 18.6.

477
0:23:35,46 --> 0:23:39,5
And then 18.5 never came out because
it got reverted.

478
0:23:39,88 --> 0:23:43,6
Nik: Which happened last time,
it happened in 2008, yeah.

479
0:23:43,66 --> 0:23:44,66
Shaun: It's been a while.

480
0:23:45,02 --> 0:23:46,36
Nik: Yeah, Yeah, obviously.

481
0:23:46,56 --> 0:23:51,46
But exactly this question triggers
the question about will the

482
0:23:51,46 --> 0:23:54,74
next minor release have even more
CVEs?

483
0:23:56,04 --> 0:23:58,82
And this triggers the next question,
what about extensions we

484
0:23:58,82 --> 0:24:0,18
have on managed providers?

485
0:24:0,86 --> 0:24:3,98
Or not managed providers, just
if we'll stay close to focus.

486
0:24:4,3 --> 0:24:9,02
Shaun: Not only will the next 1
have more, it's going to climb

487
0:24:9,02 --> 0:24:10,74
exponentially, in my opinion.

488
0:24:10,76 --> 0:24:15,22
Because, not just because of the
visibility, because obviously

489
0:24:15,94 --> 0:24:18,62
the 28 number sounds high and people
are like, we should probably

490
0:24:18,62 --> 0:24:21,26
take a look at this now and there's
gonna be more eyes on it,

491
0:24:21,3 --> 0:24:22,36
but because of AI.

492
0:24:23,0 --> 0:24:26,18
You said you didn't want, we weren't
gonna focus on that, but

493
0:24:26,38 --> 0:24:29,76
if we're being honest here, everyone
is pointing their AI agent

494
0:24:30,3 --> 0:24:32,46
at everything they possibly can.

495
0:24:32,98 --> 0:24:36,6
And now those can find bugs that
we never would have thought

496
0:24:36,6 --> 0:24:40,46
about because we just didn't have
time or priority or there wasn't

497
0:24:40,46 --> 0:24:41,82
a test case for it.

498
0:24:41,82 --> 0:24:46,28
And now there's infinite amount
of research area there.

499
0:24:46,44 --> 0:24:50,26
So not only are we going to see
that, we're going to see it accelerate.

500
0:24:52,06 --> 0:24:52,96
Michael: Is it infinite?

501
0:24:53,44 --> 0:24:55,42
I think it's finite, isn't it?

502
0:24:55,52 --> 0:24:57,34
Nik: There should be a plateau
at some point.

503
0:24:58,14 --> 0:25:1,32
Michael: The number might be high,
and I have no idea if 28 is

504
0:25:1,32 --> 0:25:4,46
getting even close to the right
order of magnitude per release

505
0:25:4,54 --> 0:25:9,74
but it feels like it will accelerate
for a while and then hopefully

506
0:25:9,96 --> 0:25:10,82
slow down?

507
0:25:11,6 --> 0:25:14,82
Shaun: Yeah that's a valid point
but I think at least in the

508
0:25:14,82 --> 0:25:17,58
short term we are definitely going
to see an acceleration.

509
0:25:17,72 --> 0:25:21,18
28 is just a drop in the bucket
because I don't know about being

510
0:25:21,18 --> 0:25:25,32
honest is the right way to phrase
this, but Postgres being written

511
0:25:25,32 --> 0:25:30,38
in C has a lot of potential for
edge cases that we just haven't

512
0:25:30,38 --> 0:25:31,56
even looked at.

513
0:25:31,56 --> 0:25:35,4
You have fuzzers, you have memory
leak checkers, you've got pointer

514
0:25:35,74 --> 0:25:38,24
checks that are done right now
by various different mechanisms

515
0:25:38,24 --> 0:25:38,68
we have.

516
0:25:38,68 --> 0:25:39,82
We've got our build farms.

517
0:25:39,82 --> 0:25:43,7
There's lots of stuff to catch
this early, but you don't miss

518
0:25:43,7 --> 0:25:45,66
28 CVEs because you're dumb.

519
0:25:45,78 --> 0:25:50,14
It's just there are new attack
vectors that nobody thought of

520
0:25:50,28 --> 0:25:53,04
and extensions open that up even
more.

521
0:25:53,6 --> 0:25:57,04
So the popular extensions are going
to be also attack vectors

522
0:25:57,04 --> 0:26:0,72
and since extensions themselves
are a way to get into the Postgres

523
0:26:0,72 --> 0:26:3,58
core, if you exploit an extension,
you have access to core.

524
0:26:4,0 --> 0:26:8,32
So that even adds more vectors
that either they have to start

525
0:26:8,32 --> 0:26:12,24
to find a way to enforce a sandbox
or it's going to make that

526
0:26:12,24 --> 0:26:13,64
discussion a lot more germane.

527
0:26:14,38 --> 0:26:19,28
And I saw someone post on X, it
was a security researcher, and

528
0:26:19,28 --> 0:26:23,1
he's going to do a four-part or
a five-part series on how he

529
0:26:23,1 --> 0:26:26,14
broke into PostGIS, for example.

530
0:26:26,5 --> 0:26:29,72
And he escalated that all the way
to a root RCE where he could

531
0:26:29,72 --> 0:26:32,86
arbitrarily affect files on the
file system from PostGIS.

532
0:26:33,48 --> 0:26:35,24
And that's just part 1 of 5.

533
0:26:35,24 --> 0:26:37,84
And he's actually going to slowly
escalate the amount of attacks

534
0:26:37,84 --> 0:26:41,06
he does all the way to the point
of getting root on the container

535
0:26:41,18 --> 0:26:42,68
through Kubernetes.

536
0:26:43,38 --> 0:26:48,52
So it's just ridiculous the amount
of analysis that people have

537
0:26:48,52 --> 0:26:51,26
really not done to this point because
it just wasn't on their

538
0:26:51,26 --> 0:26:51,76
radar.

539
0:26:51,88 --> 0:26:55,12
But now it is and it has to be
because everyone and their dog

540
0:26:55,12 --> 0:26:58,94
has an AI that can be like oh I'll
just attack this 24 hours

541
0:26:58,94 --> 0:27:2,0
a day 7 days a week until I find
something.

542
0:27:2,86 --> 0:27:7,62
Nik: We should say thank you to
Anthropic and OpenAI for limiting

543
0:27:7,68 --> 0:27:8,18
capabilities.

544
0:27:9,24 --> 0:27:11,64
You cannot do it with their latest
models right now.

545
0:27:11,64 --> 0:27:14,18
They will like completely say I'm
not continuing.

546
0:27:14,76 --> 0:27:19,32
Shaun: Yeah, those models, but
anyone who has a modern Qwen or

547
0:27:19,7 --> 0:27:24,24
K3 or any of the local models that's
been put through a LoRA

548
0:27:24,24 --> 0:27:28,28
that is uncensored, and then if
you have access to, I don't know,

549
0:27:28,28 --> 0:27:31,56
$50, 000 worth of equipment, You
can direct it at anything.

550
0:27:31,96 --> 0:27:34,74
Nik: That's interesting, because
I saw Anthropic was mentioned

551
0:27:34,74 --> 0:27:38,16
a few times in the release notes
for those minor releases, but

552
0:27:38,16 --> 0:27:41,06
I don't remember any other AI systems
mentioned.

553
0:27:41,54 --> 0:27:43,68
Shaun: They're just the ones that
people are focusing on because

554
0:27:43,68 --> 0:27:47,06
they're the frontier models, but
anything that's been open sourced

555
0:27:47,44 --> 0:27:48,46
can be repurposed.

556
0:27:48,68 --> 0:27:49,44
Nik: Yeah, yeah,

557
0:27:49,44 --> 0:27:49,78
Shaun: yeah.

558
0:27:49,78 --> 0:27:53,98
We're getting a little bit off
the topic, but the reason extensions

559
0:27:54,24 --> 0:27:58,58
are at this point and Postgres,
and actually really any software

560
0:27:58,66 --> 0:28:2,76
now, is because of AI, for good
or ill.

561
0:28:4,3 --> 0:28:7,44
Nik: Yeah, maybe let's connect
topics so we could use AI to tune

562
0:28:7,44 --> 0:28:7,94
work_mem.

563
0:28:8,9 --> 0:28:12,86
Shaun: Yeah, I actually used it
to find all the bugs in my extension

564
0:28:12,86 --> 0:28:14,6
because my extension was a proof
of concept, right?

565
0:28:14,6 --> 0:28:16,02
It's just, here, you can do this.

566
0:28:16,02 --> 0:28:18,54
It's kind of fun, throwaway kind
of material.

567
0:28:19,06 --> 0:28:20,74
So it found all the issues in it.

568
0:28:20,74 --> 0:28:23,8
Oh, you're not looking at append,
you're not looking at whatever,

569
0:28:24,0 --> 0:28:27,04
you can use the built-in plan walker
and get all these extra

570
0:28:27,04 --> 0:28:27,84
things you missed.

571
0:28:27,84 --> 0:28:30,8
And oh, you don't realize that
if you run out with these inputs

572
0:28:30,8 --> 0:28:32,96
it causes your extension to crash,
which takes down the

573
0:28:32,96 --> 0:28:33,24
backend.

574
0:28:33,24 --> 0:28:36,84
And it found dozens of things I
could fix, were I so inclined.

575
0:28:37,48 --> 0:28:39,44
So anyone can do that to their
extension.

576
0:28:39,72 --> 0:28:41,02
And it's a huge help.

577
0:28:41,76 --> 0:28:42,7
Nik: And should do.

578
0:28:42,7 --> 0:28:43,45
Shaun: Should, yes, also.

579
0:28:43,45 --> 0:28:45,06
Nik: At this point, already, yeah.

580
0:28:45,06 --> 0:28:47,88
Shaun: Because the amount of eyes,
like, the thing is, even in

581
0:28:47,88 --> 0:28:51,34
our company, we don't have my pgEdge
Ansible project that I

582
0:28:51,34 --> 0:28:55,08
work on for doing distributions
of architectures, there's like

583
0:28:55,08 --> 0:28:57,6
maybe 2 other people in the company
that can help me do code

584
0:28:57,6 --> 0:28:58,58
review on that.

585
0:28:58,78 --> 0:29:2,26
But I always have Claude available
to look over things or

586
0:29:2,26 --> 0:29:5,28
CodeRabbit or whatever tool you
want to use and they'll catch

587
0:29:5,28 --> 0:29:6,66
stuff that I didn't consider.

588
0:29:7,9 --> 0:29:10,12
Michael: I had a question on things
you considered.

589
0:29:11,12 --> 0:29:14,62
I liked that it was simple, I liked
the formulas I could follow

590
0:29:14,62 --> 0:29:18,42
them, the examples I could follow
them, But I did wonder if you,

591
0:29:19,3 --> 0:29:23,1
like for example, sequential scans
were adding 1 times work_mem,

592
0:29:23,1 --> 0:29:24,96
like it was every node type it
seemed.

593
0:29:24,96 --> 0:29:28,62
Shaun: Yeah, like it was a very
naive, like I just, anything

594
0:29:28,62 --> 0:29:31,28
that was a node I counted it, I
just didn't even like check,

595
0:29:31,28 --> 0:29:33,76
The only thing that I gave an extra
bonus to was anything that

596
0:29:33,76 --> 0:29:34,76
had the word hash in it.

597
0:29:34,76 --> 0:29:38,68
Like literally, I just grepped for
the word hash for the node types

598
0:29:38,68 --> 0:29:40,46
and I just put them all on that
big list.

599
0:29:40,84 --> 0:29:43,58
But yeah, like that was the naive
approach.

600
0:29:43,68 --> 0:29:46,84
A more refined approach would be
to actually go through and figure

601
0:29:46,84 --> 0:29:48,46
out which nodes actually do what.

602
0:29:49,12 --> 0:29:52,42
Because the parent hash node is
not where the memory gets allocated,

603
0:29:52,42 --> 0:29:54,36
it's actually the child hash elements.

604
0:29:54,52 --> 0:29:57,28
So I was actually double counting
the hash nodes, ironically

605
0:29:57,62 --> 0:29:58,74
inflating the results.

606
0:29:59,44 --> 0:30:4,34
So Yeah, obviously a good opportunity
there would be to spend

607
0:30:4,34 --> 0:30:7,34
some time refining the algorithm,
but my worst case scenario

608
0:30:7,34 --> 0:30:9,52
was just like, let's just count
all the nodes and then multiply

609
0:30:9,52 --> 0:30:12,28
and then you get like a, here's
the maximum amount this thing

610
0:30:12,28 --> 0:30:13,08
could possibly take.

611
0:30:13,08 --> 0:30:15,06
It's off by a little bit, but it's
better than the estimates

612
0:30:15,06 --> 0:30:16,72
we've been relying on.

613
0:30:16,78 --> 0:30:20,64
And it was a semi-useful kind of
extension and it demonstrated

614
0:30:20,68 --> 0:30:23,62
the process of writing an extension,
except for the fact that

615
0:30:23,62 --> 0:30:25,22
I didn't create a memory context.

616
0:30:28,36 --> 0:30:31,98
Michael: 1 more question was Back
to what you said at the start

617
0:30:31,98 --> 0:30:36,72
around using max_connections as
a multiplier I wondered it wouldn't

618
0:30:36,72 --> 0:30:40,0
be the same formula, but I wondered
if instead it might be sensible

619
0:30:40,0 --> 0:30:44,86
to use some multiple of the number
of cores The reason I came

620
0:30:44,86 --> 0:30:51,1
to that was thinking parallelism
like a parallel plan could use

621
0:30:51,1 --> 0:30:53,6
multiple times the multiple work_mems.

622
0:30:54,1 --> 0:30:57,8
So like I've seen sorts as part
of a parallel plan use kind of

623
0:30:57,8 --> 0:31:2,9
4 or 5 times work_mem, But obviously
that's a much lower number

624
0:31:2,9 --> 0:31:5,2
in most cases, so it'd be a very
different formula.

625
0:31:5,2 --> 0:31:8,8
But I wondered if there was any
merit to that maybe in formulas.

626
0:31:10,08 --> 0:31:15,06
Shaun: So, yeah, like, in that
case you would look at your max

627
0:31:15,06 --> 0:31:18,12
parallel_workers_per_gather option,
because that's the maximum

628
0:31:18,12 --> 0:31:21,22
number of cores that it would actually
leverage in a single query.

629
0:31:21,3 --> 0:31:23,26
And then multiply that by the number
of backends.

630
0:31:23,94 --> 0:31:25,54
But yeah, you could do that.

631
0:31:26,2 --> 0:31:26,68
Michael: Yeah.

632
0:31:26,68 --> 0:31:31,16
Just thinking if you've got 16
cores and you've got a bunch of

633
0:31:31,16 --> 0:31:34,34
queries trying to fire off lots
of parallel workers.

634
0:31:34,44 --> 0:31:37,12
The 1st few might get all of the
workers they want, but the next

635
0:31:37,12 --> 0:31:39,3
ones are only going to get a single
1.

636
0:31:39,52 --> 0:31:41,76
Like, it won't let you run more
than.

637
0:31:41,76 --> 0:31:43,76
Shaun: Yeah, yeah, that would act
as a cap.

638
0:31:44,1 --> 0:31:45,9
Michael: Have you ever done anything
like that, Nik?

639
0:31:46,62 --> 0:31:49,18
Nik: Yeah, I barely understand
what you're saying, Michael.

640
0:31:50,66 --> 0:31:56,26
Shaun: I think he was asking if
you had used the number of cores

641
0:31:56,26 --> 0:31:59,24
as part of your estimate for work_mem.

642
0:32:0,06 --> 0:32:3,16
And I kind of get where he's going
with it, because even if you

643
0:32:3,16 --> 0:32:7,78
set max_parallel_workers_per_gather
to limit the amount, eventually

644
0:32:7,78 --> 0:32:9,78
they'll run out of, you'll hit
your max_parallel_workers.

645
0:32:11,12 --> 0:32:13,06
Nik: Right, not vCPU count, max_parallel_workers.

646
0:32:13,38 --> 0:32:16,62
Postgres has no idea how many cores
or how much.

647
0:32:16,62 --> 0:32:19,82
Shaun: Right, but which is why
you set the max_parallel_workers

648
0:32:19,84 --> 0:32:22,62
and various other settings so you
wouldn't exceed it.

649
0:32:22,86 --> 0:32:23,26
Nik: Why?

650
0:32:23,26 --> 0:32:25,14
Like we can exceed it, we can exceed
it.

651
0:32:25,14 --> 0:32:26,92
Shaun: You can, you can, you can.

652
0:32:26,92 --> 0:32:28,3
I wouldn't recommend it.

653
0:32:29,34 --> 0:32:33,5
Nik: I agree with you But looking
at guys who come to us like

654
0:32:33,9 --> 0:32:39,72
16 or 32 cores and max_connections
5000, I already have a shift

655
0:32:39,72 --> 0:32:41,18
in my mind.

656
0:32:41,26 --> 0:32:42,66
I cannot convince them.

657
0:32:42,66 --> 0:32:47,06
We spent a lot of efforts saying
this max_connections is abnormal.

658
0:32:48,08 --> 0:32:51,8
Especially before Postgres 13,
14 when Andres Freund improved

659
0:32:52,36 --> 0:32:56,5
work with snapshots, which is very
related to memory consumption,

660
0:32:56,5 --> 0:32:57,0
right?

661
0:32:57,5 --> 0:33:1,36
Yeah, we tried like max_connections
should be like 3, 4 times

662
0:33:1,36 --> 0:33:3,3
more than vCPU count.

663
0:33:3,42 --> 0:33:5,52
Shaun: That's what everyone says,
but...

664
0:33:5,66 --> 0:33:6,6
Nik: No, not anymore.

665
0:33:6,6 --> 0:33:9,36
We don't say it anymore because
since Postgres 14, it's much

666
0:33:9,36 --> 0:33:9,86
better.

667
0:33:10,08 --> 0:33:13,0
And I think I had some tests showing,
we should revisit this

668
0:33:13,0 --> 0:33:13,5
by the way.

669
0:33:13,5 --> 0:33:18,62
I want to revisit with benchmarks
and see exactly how it degrades.

670
0:33:18,62 --> 0:33:20,42
But now it degrades less.

671
0:33:21,02 --> 0:33:22,54
Shaun: Yeah, it's not nearly as
bad.

672
0:33:22,54 --> 0:33:25,2
You still have to fight the kernel
process table, but it's not

673
0:33:25,2 --> 0:33:26,34
as bad as it was.

674
0:33:26,78 --> 0:33:28,48
Nik: Right, so this is just reality.

675
0:33:28,58 --> 0:33:32,72
These guys come to us with RDS
and telling them that you need

676
0:33:32,72 --> 0:33:39,04
to reduce max_connections drastically,
you need restart for it.

677
0:33:40,26 --> 0:33:44,28
And if they don't have proper database
side pooler, we cannot

678
0:33:44,38 --> 0:33:48,12
convince them to get rid of huge
amount of idle connections.

679
0:33:48,12 --> 0:33:53,2
They just need them to satisfy
application guys needs because

680
0:33:53,2 --> 0:33:57,24
those guys, as I said, they scale
their application nodes like

681
0:33:57,24 --> 0:34:0,36
this, especially e-commerce when
Black Friday happens, they just

682
0:34:0,36 --> 0:34:1,18
need to scale.

683
0:34:2,36 --> 0:34:6,2
Or some new system as well, like
social media, they need to be

684
0:34:6,2 --> 0:34:8,94
able to scale and they need those
idle connections.

685
0:34:9,78 --> 0:34:14,44
So this, back to work_mem here,
I don't know, it depends.

686
0:34:14,44 --> 0:34:19,62
Also, sometimes we need to reproduce
plans in an environment

687
0:34:19,74 --> 0:34:23,46
which is much weaker physically
than production, because we study

688
0:34:23,46 --> 0:34:24,98
behavior of Postgres.

689
0:34:25,44 --> 0:34:29,62
And we are okay for some contention
happening in terms of physical

690
0:34:29,62 --> 0:34:32,72
resources, But we want the planner
to behave exactly like in

691
0:34:32,72 --> 0:34:33,22
production.

692
0:34:33,66 --> 0:34:34,94
So there are some nuances.

693
0:34:36,26 --> 0:34:37,36
But I agree with you.

694
0:34:37,36 --> 0:34:40,6
Overall, I would like to see average
number of sessions below

695
0:34:40,6 --> 0:34:41,68
vCPU count.

696
0:34:41,68 --> 0:34:42,54
This is great.

697
0:34:43,26 --> 0:34:46,84
Shaun: Yeah, that's usually that's
what I still tell people.

698
0:34:46,84 --> 0:34:50,66
Not because necessarily they're
going to see a huge drop in performance

699
0:34:50,66 --> 0:34:53,72
because it's let if we let's face
it's going to be around

700
0:34:53,72 --> 0:34:56,42
the 20 or 30 percent mark at maximum
even if they're sending

701
0:34:56,42 --> 0:35:1,18
hundreds of thousands but it's
still I would say a best practice

702
0:35:1,18 --> 0:35:2,06
to do so

703
0:35:2,64 --> 0:35:5,94
Michael: yeah I'm going Maybe going
back to basics a little bit,

704
0:35:6,62 --> 0:35:11,68
when you're tuning work_mem, is
it always to do with latency?

705
0:35:11,98 --> 0:35:14,58
Just like speed of queries on average?

706
0:35:15,66 --> 0:35:19,02
Or are we sometimes trying to look
after the disks a little bit?

707
0:35:19,02 --> 0:35:21,06
Are we trying to increase headroom
there a little bit?

708
0:35:21,06 --> 0:35:24,22
Or is it mostly just user-facing
query times?

709
0:35:24,64 --> 0:35:26,58
Shaun: I guess it depends on what
hat you're wearing.

710
0:35:26,58 --> 0:35:29,8
As a DBA, you're just like, I don't
want my database to crash,

711
0:35:29,8 --> 0:35:32,32
or I don't want the hardware to
burst into flames because it's

712
0:35:32,32 --> 0:35:33,9
being misused in some way.

713
0:35:34,12 --> 0:35:36,9
From that perspective you're like,
okay, I'll set it to be just

714
0:35:36,9 --> 0:35:40,38
enough that I avoid lots of disk
spilling and then causing disk

715
0:35:40,38 --> 0:35:42,78
wear or really slow latency.

716
0:35:43,58 --> 0:35:45,48
But there's also a point of diminishing
returns, right?

717
0:35:45,48 --> 0:35:48,16
If you set it to some infinitely
high amount, you're not gaining

718
0:35:48,16 --> 0:35:49,28
anything out of it.

719
0:35:49,28 --> 0:35:52,06
All you're really doing is making
it so your maximum is higher

720
0:35:52,06 --> 0:35:55,34
for no reason, and you end up getting
more risk of an out-of-memory

721
0:35:55,38 --> 0:35:55,88
error.

722
0:35:56,12 --> 0:36:0,12
Really, it's just 1 of those things
like Nik had said, it's

723
0:36:0,38 --> 0:36:2,8
How do we set it properly without
going overboard?

724
0:36:2,8 --> 0:36:6,24
How do we go do it without going
too little?

725
0:36:7,06 --> 0:36:12,18
And part of that is taking what
you have and using heuristics

726
0:36:12,26 --> 0:36:16,72
to come up with some kind of reasonable
number, like using pg_stat_statements

727
0:36:16,72 --> 0:36:19,3
and sending it
through some kind of estimation

728
0:36:19,86 --> 0:36:20,36
process.

729
0:36:21,26 --> 0:36:24,16
Or I found out that apparently
if you send the query through

730
0:36:24,16 --> 0:36:28,86
the pre-execution step, it actually
calculates all the memory

731
0:36:28,86 --> 0:36:31,26
that it would allocate, but it
doesn't allocate it yet.

732
0:36:31,48 --> 0:36:34,28
So in theory I could walk the plan
nodes and actually get the

733
0:36:34,28 --> 0:36:37,88
estimates directly from the planner,
whereas my approach was

734
0:36:37,88 --> 0:36:41,98
very coarse and it just did it
based on the node types, you could

735
0:36:41,98 --> 0:36:45,28
get it directly from the plan output,
from the executor step

736
0:36:45,28 --> 0:36:46,4
itself, from the pre-executor.

737
0:36:46,62 --> 0:36:49,64
And you could actually pull those
bits of data and actually get

738
0:36:49,64 --> 0:36:54,62
an exact number of what the allocator
would have actually asked

739
0:36:54,62 --> 0:36:56,02
for from Postgres.

740
0:36:57,18 --> 0:37:0,4
So a better approach I would say,
at least as far as revising

741
0:37:0,4 --> 0:37:4,08
my extension, would be to say use
the built-in plan walker, do

742
0:37:4,08 --> 0:37:7,78
it after the pre-execution steps
so you have all the estimates

743
0:37:8,26 --> 0:37:11,68
of memory usage that it would have
done in the 1st place, and

744
0:37:11,68 --> 0:37:13,16
sum those totals instead.

745
0:37:13,78 --> 0:37:16,4
Then what you end up with is a
real estimate of what all the

746
0:37:16,4 --> 0:37:18,18
queries would have taken without
them executing.

747
0:37:19,02 --> 0:37:22,7
And then you can use that to design
your ideal work_mem based

748
0:37:22,7 --> 0:37:26,7
on your amount of average active
backends and whatnot.

749
0:37:27,72 --> 0:37:29,44
But it's just 1 of those things
with Postgres.

750
0:37:29,44 --> 0:37:34,2
You have to always go back and
retroactively examine how your

751
0:37:34,2 --> 0:37:35,18
system's been operating.

752
0:37:35,66 --> 0:37:37,36
And a lot of that is observation.

753
0:37:37,36 --> 0:37:38,38
Do you have a dashboard?

754
0:37:38,44 --> 0:37:41,24
Do you have observability and visibility
across your entire cluster

755
0:37:41,24 --> 0:37:43,66
to see what, how it's actually
operating?

756
0:37:43,98 --> 0:37:46,04
And that should be how you drive
your systems.

757
0:37:46,04 --> 0:37:49,34
If you see that you're always running
out of memory at some point,

758
0:37:49,34 --> 0:37:51,58
look at your memory settings, look
at shared_buffers, look at

759
0:37:51,58 --> 0:37:54,34
work_mem, look at anything that
could possibly allocate stuff

760
0:37:54,34 --> 0:37:56,82
and then maybe reduce it a little
bit if it's running out of

761
0:37:56,82 --> 0:37:58,52
memory or increase it if it's not.

762
0:37:58,94 --> 0:38:2,38
It's a delicate balancing act and
unfortunately there's no 1

763
0:38:2,38 --> 0:38:5,82
size fits all way to addressing
everything, which is why there's

764
0:38:5,82 --> 0:38:11,98
so many guides, there's blogs and
tutorials and videos and everything

765
0:38:11,98 --> 0:38:15,48
galore of how to do it, and no
1 really can agree on 1 final

766
0:38:15,48 --> 0:38:15,98
answer.

767
0:38:16,1 --> 0:38:19,14
Nik: There is no official runbook
how-to documentation.

768
0:38:19,4 --> 0:38:22,54
This is sad actually, but I can
imagine how hard it would be

769
0:38:22,54 --> 0:38:26,38
to achieve consensus on the concrete
protocol.

770
0:38:27,26 --> 0:38:30,84
Actually, we somehow avoided the
topic of swap as well.

771
0:38:31,02 --> 0:38:32,64
We could enable swap, right?

772
0:38:32,64 --> 0:38:35,68
This is like instead of temporary
file for each query, let's

773
0:38:35,68 --> 0:38:37,58
enable on that far end.

774
0:38:37,58 --> 0:38:40,46
If we achieve that, let's swap
there, 1 thing.

775
0:38:40,64 --> 0:38:43,66
And another thing, like I just
resonate very, like a lot with

776
0:38:43,66 --> 0:38:49,12
your words about, for example,
maintenance_work_mem, which usually

777
0:38:49,12 --> 0:38:52,94
is inherited by autovacuum_work_mem,
being minus 1, right?

778
0:38:53,1 --> 0:38:57,08
And then we tell everyone we should
have more workers for autovacuum

779
0:38:57,24 --> 0:38:57,7
workers.

780
0:38:57,7 --> 0:39:3,56
And then Postgres 17 silently,
like unexpectedly, lifts unspoken

781
0:39:3,7 --> 0:39:4,84
limit 1 gigabyte.

782
0:39:5,24 --> 0:39:9,34
It was not like obvious that we
actually were limited, but guys

783
0:39:9,34 --> 0:39:13,44
already raised maintenance_work_mem
to say 8 gigabytes and

784
0:39:13,44 --> 0:39:15,42
raise number of workers to say
25.

785
0:39:15,42 --> 0:39:18,9
And now we have interesting memory
allocation for autovacuum,

786
0:39:18,9 --> 0:39:20,04
which we didn't want.

787
0:39:20,5 --> 0:39:24,44
So now we say raise the number
of autovacuum workers, but also

788
0:39:24,62 --> 0:39:28,1
limit autovacuum_work_mem by 1
gigabyte, because when you will

789
0:39:28,1 --> 0:39:31,72
upgrade to 17, so Layers of logic,
yeah.

790
0:39:31,72 --> 0:39:34,08
Shaun: I totally forgot about maintenance_work_mem
and autovacuum_work_mem

791
0:39:34,08 --> 0:39:37,12
because those, you don't
really think about those because

792
0:39:37,12 --> 0:39:39,62
the 1 that really bites everyone
is work_mem because they set

793
0:39:39,62 --> 0:39:41,88
it to some value and then it explodes
on them.

794
0:39:41,94 --> 0:39:44,52
But yeah, the other 2 definitely
are a factor.

795
0:39:45,06 --> 0:39:50,68
Nik: Swap, like swap, like I always
try to avoid swap on Postgres

796
0:39:50,68 --> 0:39:52,78
machines, I remember, but it's
very old.

797
0:39:52,78 --> 0:39:57,88
I haven't revisited this topic
because I remember dealing with

798
0:39:58,48 --> 0:40:1,94
database, Postgres database, which
experiences heavy swap.

799
0:40:2,22 --> 0:40:4,02
Since then, I always avoided it.

800
0:40:4,02 --> 0:40:6,94
But then I remember Bruce Momjian
said, we should just, small

801
0:40:6,94 --> 0:40:7,9
swap is good.

802
0:40:8,0 --> 0:40:9,78
I said, no, it is completely avoided.

803
0:40:9,78 --> 0:40:12,98
I would better see out of memory
and fix my memory settings and

804
0:40:12,98 --> 0:40:13,58
so on.

805
0:40:13,58 --> 0:40:18,78
What's your opinion about having
swap enabled on the machine

806
0:40:18,78 --> 0:40:19,44
with Postgres?

807
0:40:20,34 --> 0:40:24,52
Shaun: The problem I usually see
with swap is you can't really

808
0:40:24,52 --> 0:40:27,04
account for what the kernel or
the memory pressure systems will

809
0:40:27,04 --> 0:40:27,54
do.

810
0:40:28,08 --> 0:40:32,02
And I've had problems in the past
with previous kernels doing

811
0:40:32,02 --> 0:40:33,14
things that they shouldn't.

812
0:40:33,58 --> 0:40:36,24
So I try to get as much control
as I can.

813
0:40:36,34 --> 0:40:39,16
So in that case, I usually set
swappiness to 1, because if you

814
0:40:39,16 --> 0:40:42,54
set it too low or to 0, the memory
pressure systems go wonky.

815
0:40:43,46 --> 0:40:47,7
And then I set to some low amount,
like 2 gigs, 4 gigs, some

816
0:40:47,72 --> 0:40:51,02
token amount just so the kernel
has area to work with.

817
0:40:51,02 --> 0:40:53,98
Then I set overcommit_memory to
2 so you can't overcommit.

818
0:40:54,78 --> 0:40:59,18
And then I set overcommit_kbytes
to the exact amount of physical

819
0:40:59,18 --> 0:41:0,82
memory that there is on the system.

820
0:41:1,24 --> 0:41:5,8
So it will not use swap because
it basically can't, because it's

821
0:41:5,8 --> 0:41:7,32
been subtracted from the total.

822
0:41:8,3 --> 0:41:11,72
Any allocation has to be physically
backed by actual RAM, at

823
0:41:11,72 --> 0:41:14,06
least as far as the database is
concerned, so you don't end up

824
0:41:14,06 --> 0:41:18,48
with overcommit and the OOM killer
won't kick in because there's

825
0:41:18,48 --> 0:41:21,16
not anything using too much RAM,
because it can't.

826
0:41:21,18 --> 0:41:23,26
If you make a request, you simply
get denied.

827
0:41:23,32 --> 0:41:25,56
And then your backend will
go, oh, I can't allocate memory,

828
0:41:25,56 --> 0:41:27,08
so I won't run this query for you.

829
0:41:27,08 --> 0:41:29,68
And I'd rather have a failed query
and have someone have to go

830
0:41:29,68 --> 0:41:32,9
back and look at their query or
revise it or whatever, then take

831
0:41:32,9 --> 0:41:36,14
the system down because OOM killer
decided that there's a rogue

832
0:41:36,14 --> 0:41:36,64
process.

833
0:41:37,06 --> 0:41:41,82
So basically, it always comes down
to, as a DBA, getting as much

834
0:41:41,82 --> 0:41:44,58
control as you can over the system
and enforcing it stringently,

835
0:41:45,06 --> 0:41:48,76
which is a little harder to do
in the Kubernetes context because

836
0:41:49,14 --> 0:41:51,38
those limits aren't enforced the
same way.

837
0:41:52,28 --> 0:41:54,9
Nik: Right, and there is 1 more
point.

838
0:41:55,24 --> 0:41:58,88
We somehow also avoided an important
topic.

839
0:41:59,44 --> 0:42:3,42
We could plan everything very well,
shared_buffers,

840
0:42:3,42 --> 0:42:8,0
maintenance_work_mem for index creation,
autovacuum workers, then our backends

841
0:42:8,06 --> 0:42:11,54
with work_mem, But somehow we forget
about page cache.

842
0:42:11,54 --> 0:42:14,08
And in many systems, page cache
is super important.

843
0:42:15,1 --> 0:42:19,46
Sometimes, like if it's, for example,
if we had a situation when

844
0:42:19,46 --> 0:42:23,04
it was exceeding shared_buffers,
for example, a simple example,

845
0:42:23,3 --> 0:42:27,54
we perform minor upgrade, we restart
server, and we don't think

846
0:42:27,54 --> 0:42:30,44
about pre-warming because actually
there is page cache sitting

847
0:42:30,44 --> 0:42:33,92
there, which helps us to have better
performance sooner.

848
0:42:34,78 --> 0:42:37,2
We recently had an internal discussion
about that.

849
0:42:37,2 --> 0:42:42,54
Do we need to have a pre-warming,
pg_prewarm, automated or not

850
0:42:42,54 --> 0:42:43,04
automated?

851
0:42:43,28 --> 0:42:45,76
And the question is, if there is
huge page cache, probably we

852
0:42:45,76 --> 0:42:46,94
don't need to bother.

853
0:42:47,3 --> 0:42:53,28
But if we start tuning work_mem,
page cache will become thin and

854
0:42:53,3 --> 0:42:54,18
very narrow, right?

855
0:42:54,18 --> 0:42:56,14
And this can be a problem as well.

856
0:42:56,72 --> 0:42:58,08
So it's very tricky, right?

857
0:42:58,08 --> 0:43:0,88
To think about macro level and
everything.

858
0:43:2,34 --> 0:43:5,2
Shaun: That definitely directs
how you will want to choose your

859
0:43:5,2 --> 0:43:8,86
hardware because 1 of my talks
at Postgres Open, I think it was

860
0:43:8,86 --> 0:43:14,52
2012, was about our woes with trying
to get pre-warming working.

861
0:43:15,04 --> 0:43:17,44
We would have a Postgres crash
because we were using EDB at the

862
0:43:17,44 --> 0:43:20,84
time and there were, we were using
a couple of extensions that

863
0:43:20,84 --> 0:43:24,28
from EDB that were, let's just
say beta quality, but they were

864
0:43:24,28 --> 0:43:25,42
doing something we needed.

865
0:43:25,68 --> 0:43:27,94
So occasionally we'd get a crash,
fine, whatever.

866
0:43:28,26 --> 0:43:31,56
So the database goes down, suddenly
our shared_buffers have been

867
0:43:31,56 --> 0:43:35,76
invalidated, but we were also finding
that the page cache was

868
0:43:35,76 --> 0:43:39,68
not sufficient because a lot of
those backends, at least at the

869
0:43:39,68 --> 0:43:43,98
time, were Postgres backends and
they had their minimum memory

870
0:43:43,98 --> 0:43:47,28
allocation and The page caches
were too small because of all

871
0:43:47,28 --> 0:43:47,78
that.

872
0:43:48,12 --> 0:43:53,1
You end up with Postgres RAM, backend
allocations, and then a

873
0:43:53,1 --> 0:43:54,72
small functional page cache.

874
0:43:54,72 --> 0:43:58,92
So what ended up happening is,
after the crash, Postgres would

875
0:43:58,92 --> 0:44:2,7
take an hour to warm up based on
user queries.

876
0:44:2,9 --> 0:44:7,16
So that whole time your latency
jumps by like 20 times because

877
0:44:7,16 --> 0:44:9,5
you're running off of an old RAID
array or something.

878
0:44:10,52 --> 0:44:14,46
So our fix was to get a Fusion-io
drive, which would be equivalent

879
0:44:14,46 --> 0:44:20,2
to a 100, 000 IOPS device from EBS
or something, like an io2,

880
0:44:21,04 --> 0:44:24,96
or just a really high-end NVMe
M.2 stick or something.

881
0:44:24,96 --> 0:44:26,26
Nik: Local NVMe, yeah.

882
0:44:26,4 --> 0:44:27,14
It's great.

883
0:44:27,28 --> 0:44:30,24
Shaun: It was really the only way
out at the time because you'd

884
0:44:30,24 --> 0:44:36,28
see the spike of activity and the
I/O usage from iostat, right?

885
0:44:36,28 --> 0:44:40,38
It would shoot up to 100% usage
for, and it would just stay there

886
0:44:40,38 --> 0:44:41,26
for an hour.

887
0:44:41,78 --> 0:44:45,36
As soon as we upgraded to the newer
device, it would spike once

888
0:44:45,36 --> 0:44:47,96
in the beginning, as soon as the
crash was over, and then it

889
0:44:47,96 --> 0:44:49,26
would just hover around 20%.

890
0:44:49,9 --> 0:44:52,7
But that 20% was on a 100, 000 IOPS
device.

891
0:44:53,16 --> 0:44:56,66
So if it were anything less than
that, it would be a lot worse.

892
0:44:57,54 --> 0:45:2,72
So even page cache can't really
save you in certain circumstances.

893
0:45:2,8 --> 0:45:4,5
It really depends on your workload.

894
0:45:5,8 --> 0:45:9,06
And with Postgres needing 0.25
of your RAM, well, not needing,

895
0:45:9,14 --> 0:45:13,1
we recommend using 0.25 of your
RAM up to a certain limit.

896
0:45:13,78 --> 0:45:16,24
If your page cache is too big,
then you're essentially double

897
0:45:16,24 --> 0:45:16,74
buffering.

898
0:45:17,21 --> 0:45:21,58
Nik: Yeah, and you have much higher
likelihood that temporary

899
0:45:21,58 --> 0:45:23,42
files spilling to disk will happen,
right?

900
0:45:23,42 --> 0:45:25,78
So this is the whole point to avoid.

901
0:45:26,38 --> 0:45:29,34
This, the case you just described
exactly like what I'm saying

902
0:45:29,34 --> 0:45:32,32
about, and Having faster disks
definitely helps.

903
0:45:32,86 --> 0:45:36,42
These days we can have millions
of IOPS with local NVMEs, right?

904
0:45:36,94 --> 0:45:44,22
But I also think if we tune work_mem,
so we make page cache quite

905
0:45:44,22 --> 0:45:48,52
thin, and we need to think about
how fast or slow disks are.

906
0:45:48,52 --> 0:45:52,32
And if we know they are slow, then
maybe we should consider automatic

907
0:45:52,6 --> 0:45:53,1
pre-warming.

908
0:45:53,44 --> 0:45:57,392
Because pg_prewarm right now supports
automated pre-warming, so

909
0:45:57,392 --> 0:46:2,68
it can capture maybe this is the
exact moment, slow disks and

910
0:46:2,68 --> 0:46:4,9
small page cache and which should
start.

911
0:46:4,96 --> 0:46:8,62
This actually adds to the whole
picture of tuning work_mem, right?

912
0:46:8,94 --> 0:46:12,1
Shaun: Yeah, and back when this
happened, pg_prewarm wasn't really

913
0:46:12,1 --> 0:46:12,76
a thing.

914
0:46:13,32 --> 0:46:16,58
So I cheated by just like using
dd.

915
0:46:17,22 --> 0:46:20,98
So I queried the catalog, figured
out which backend files went

916
0:46:20,98 --> 0:46:23,76
with the most used tables, and
I would just, before I started

917
0:46:23,76 --> 0:46:26,78
the server, I would dd them all
into memory and then I would

918
0:46:26,78 --> 0:46:27,48
start Postgres.

919
0:46:27,52 --> 0:46:31,26
And then that solved 80% of the
problem, but that wasn't sustainable

920
0:46:31,26 --> 0:46:33,3
long term, so that's why we bought
the storage.

921
0:46:33,34 --> 0:46:34,92
But there's ways you can get around
it.

922
0:46:34,92 --> 0:46:36,24
So definitely pg_prewarm.

923
0:46:36,88 --> 0:46:39,52
It's an extension people don't
really think about, because it's

924
0:46:39,52 --> 0:46:41,98
not really a problem so much anymore,
because everyone's got

925
0:46:41,98 --> 0:46:43,78
infinite IOPS, it's roughly.

926
0:46:44,24 --> 0:46:48,96
But if you don't, and you don't
want to pay io2 fees or load

927
0:46:48,96 --> 0:46:52,32
up your system with expensive storage,
then pre-warming is still

928
0:46:52,32 --> 0:46:53,0
an option.

929
0:46:53,2 --> 0:46:56,6934
Nik: I agree with you and like
bigger databases with local NVMes,

930
0:46:56,6934 --> 0:47:1,32
it's like we know a few companies
who bet on it heavily, right?

931
0:47:1,52 --> 0:47:6,3
But also there are many more smaller
clusters, usually single

932
0:47:6,3 --> 0:47:13,98
node clusters, which are needed
to support some AI building,

933
0:47:14,16 --> 0:47:15,64
AI builders products.

934
0:47:15,78 --> 0:47:19,02
They just experiment a lot and
they don't need serious database,

935
0:47:19,02 --> 0:47:20,78
but they still need some database.

936
0:47:21,04 --> 0:47:24,9
And in that case, tuning, like
what we just discussed could be

937
0:47:24,9 --> 0:47:30,06
used for them because tuning work_mem,
so queries are good enough

938
0:47:30,06 --> 0:47:34,6
in terms of performance, but also
you know that restart will

939
0:47:34,6 --> 0:47:39,38
help you survive not being super
slow for 0.5 an hour.

940
0:47:39,72 --> 0:47:43,64
So I think there are interesting
cases and this case with smaller

941
0:47:44,02 --> 0:47:49,32
databases because of AI, I think
they will grow a lot.

942
0:47:50,64 --> 0:47:54,66
Shaun: Well, I mean, that's actually
a good point, because especially

943
0:47:54,66 --> 0:47:57,84
if you have database branching,
like I know that you have in

944
0:47:57,84 --> 0:48:1,5
your product, you've got the ability
to fork off, like dev instances

945
0:48:1,5 --> 0:48:3,66
of databases, and those are entirely
cold.

946
0:48:3,9 --> 0:48:6,34
Without proper backing on them,
they're going to be slow for

947
0:48:6,34 --> 0:48:7,78
a while, at least after start.

948
0:48:8,16 --> 0:48:8,36
In the

949
0:48:8,36 --> 0:48:12,22
Nik: case of DBLab, it's ZFS, and
ZFS has ARC.

950
0:48:12,26 --> 0:48:17,22
We usually allocate 0.5 of memory
to it, so Those blocks are

951
0:48:17,22 --> 0:48:20,02
warmed up already usually, so that
helps a lot.

952
0:48:20,02 --> 0:48:21,34
We have a different concern.

953
0:48:21,68 --> 0:48:25,24
Developers ask us, can you implement
cold cache?

954
0:48:25,24 --> 0:48:29,7
Because we study EXPLAIN plans,
we want cold cache to see how

955
0:48:29,7 --> 0:48:30,66
the worst case.

956
0:48:30,92 --> 0:48:35,1
And this is tricky in this architecture
because it's a multi-tenant

957
0:48:35,32 --> 0:48:35,82
thing.

958
0:48:36,38 --> 0:48:40,94
A lot of Postgres exploration happening
on the same VM and we

959
0:48:40,94 --> 0:48:42,1
need that cache actually.

960
0:48:43,52 --> 0:48:45,32
Shaun: Yeah, how would you even
do that?

961
0:48:45,32 --> 0:48:48,18
You'd have to move it over to another
instance, and that's cold.

962
0:48:48,2 --> 0:48:51,96
Nik: Yeah, you cannot do it without
losing the common cache.

963
0:48:51,96 --> 0:48:55,36
But common cache helps others,
because we have different use

964
0:48:55,36 --> 0:48:55,68
cases.

965
0:48:55,68 --> 0:48:59,38
It's not always exploration of
EXPLAIN plans, but also sometimes

966
0:48:59,38 --> 0:49:0,98
just preview environments for testing.

967
0:49:1,64 --> 0:49:3,96
And those guys want better performance.

968
0:49:4,82 --> 0:49:6,6
So it's a complex topic.

969
0:49:6,9 --> 0:49:11,04
But we learned very early, actually,
that we should match work_mem

970
0:49:11,04 --> 0:49:14,54
to production because it affects
the planner behavior, which

971
0:49:14,54 --> 0:49:15,18
I mentioned.

972
0:49:16,22 --> 0:49:18,46
This is important for any lab environment.

973
0:49:19,12 --> 0:49:20,42
Okay, I'm out of questions.

974
0:49:20,42 --> 0:49:24,14
We touched a lot, like we went
quite broadly, touched a lot of

975
0:49:24,14 --> 0:49:25,06
additional questions.

976
0:49:25,08 --> 0:49:25,76
Thank you so much.

977
0:49:25,76 --> 0:49:27,68
It was a very interesting discussion.

978
0:49:29,16 --> 0:49:31,76
I especially like you confirmed
a lot of things I have in my

979
0:49:31,76 --> 0:49:33,98
head, so it's great to hear confirmation.

980
0:49:34,66 --> 0:49:37,2
But also learned a lot of new stuff,
thank you so much.

981
0:49:37,2 --> 0:49:41,12
I will follow your new blog posts,
don't stop writing, it's very

982
0:49:41,12 --> 0:49:42,02
interesting always.

983
0:49:42,26 --> 0:49:43,2
Shaun: Yeah, I don't plan to.

984
0:49:43,2 --> 0:49:45,9
And I always like to remind people
that if you've ever heard

985
0:49:45,9 --> 0:49:49,4
of Perl as the pathologically eclectic
rubbish lister, that's

986
0:49:49,4 --> 0:49:50,86
basically how my brain works.

987
0:49:51,82 --> 0:49:53,8
Nik: Okay, okay.

988
0:49:54,48 --> 0:49:55,58
Great, yeah.

989
0:49:56,68 --> 0:49:57,84
Michael: Well, really nice to meet
you.

990
0:49:57,84 --> 0:49:59,08
Thanks for joining us.

991
0:49:59,16 --> 0:49:59,68
Nik: Thank you.

992
0:49:59,68 --> 0:50:0,16
Shaun: You too.

993
0:50:0,16 --> 0:50:1,18
Hope you have a good day.

994
0:50:1,32 --> 0:50:2,36
Nik: You have a great week.