1
0:0:0,06 --> 0:0:2,06
Nikolay: Hello, hello, this is
PostgresFM.
2
0:0:2,12 --> 0:0:7,24
My name is Nik, PostgresAI, and
as usual with me, Michael, pgMustard.
3
0:0:7,5 --> 0:0:8,299999
Hi, Michael.
4
0:0:9,28 --> 0:0:10,12
Michael: Hi, Nik.
How are you doing?
5
0:0:11,24 --> 0:0:12,139999
Nikolay: So the topic...
6
0:0:12,7 --> 0:0:14,28
I'm doing great, how are
you?
7
0:0:14,599999 --> 0:0:15,86
Michael: I'm good, Thank you.
8
0:0:15,94 --> 0:0:16,86
What's the topic?
9
0:0:17,26 --> 0:0:18,119999
Nikolay: What's the topic?
10
0:0:18,119999 --> 0:0:21,66
The topic I chose, and everyone
already saw it on cover you created,
11
0:0:21,66 --> 0:0:22,06
right?
12
0:0:22,06 --> 0:0:29,86
So the topic is 5 or so, maybe
6 things you should check when
13
0:0:29,86 --> 0:0:32,98
you start new schema or you already
have some schema and your
14
0:0:32,98 --> 0:0:38,32
project is growing and you want
to be better prepared for bigger
15
0:0:38,32 --> 0:0:40,02
scale and performance wise.
16
0:0:40,6 --> 0:0:45,32
So what, especially if you use
AI to design and improve your
17
0:0:45,32 --> 0:0:48,48
schema and your project is growing.
18
0:0:48,76 --> 0:0:55,02
So what should you look at to check
your schema health?
19
0:0:55,38 --> 0:0:56,14
Michael: Makes sense.
20
0:0:56,32 --> 0:1:0,2
Just on the topic in general, I
do think there are some things
21
0:1:0,2 --> 0:1:3,58
that are better suited to asking
for help from AI and not?
22
0:1:3,68 --> 0:1:7,58
Do you think like schema design
is 1 of them or actually is it
23
0:1:7,58 --> 0:1:11,32
1 of those ones that maybe we should
still do it a little bit
24
0:1:11,32 --> 0:1:13,12
more ourselves at the moment?
25
0:1:13,66 --> 0:1:18,04
Nikolay: Well as you know I'm very
very very intensive user of
26
0:1:18,04 --> 0:1:18,54
AI.
27
0:1:18,86 --> 0:1:19,36
Yeah.
28
0:1:20,28 --> 0:1:23,4
Honestly, like I do everything
through AI right now.
29
0:1:24,44 --> 0:1:29,24
And I'm 100% schema design is a
topic where AI can help a lot.
30
0:1:29,24 --> 0:1:32,36
But it also matters which questions
you ask.
31
0:1:32,98 --> 0:1:37,06
If you just say, I want this and
don't pay attention, you will
32
0:1:37,06 --> 0:1:39,64
have low quality schema in many
cases still.
33
0:1:40,44 --> 0:1:44,34
That's why I said, let's have this
episode because it might be
34
0:1:44,34 --> 0:1:48,74
quite basic for many advanced users
of Postgres, But this checklist
35
0:1:48,84 --> 0:1:53,08
we are going to present today,
it can be good thing to keep in
36
0:1:53,08 --> 0:1:56,82
mind always when you design schema,
just to ask right questions
37
0:1:56,94 --> 0:1:57,66
and revisit.
38
0:1:58,26 --> 0:2:1,4
Also, I'm not sure how exactly
you use AI.
39
0:2:1,4 --> 0:2:6,16
I use it always like I work with
people, developers.
40
0:2:7,4 --> 0:2:12,58
When you just ask something and
think those engineers, humans
41
0:2:12,58 --> 0:2:16,46
or AI, will solve all your problems
and make all the decisions,
42
0:2:17,1 --> 0:2:20,28
you're offloading full responsibility
to their shoulders, so
43
0:2:20,28 --> 0:2:24,36
then you have big negative surprises
always, right?
44
0:2:24,6 --> 0:2:29,64
So it's better to keep requests
as detailed as possible when
45
0:2:29,64 --> 0:2:34,48
you work with AI, and also make
some iterations to review and
46
0:2:34,48 --> 0:2:34,98
improve.
47
0:2:35,54 --> 0:2:38,92
And when making iterations, like
first, like I want some schema,
48
0:2:39,02 --> 0:2:44,12
like, I don't know, like LinkedIn
social media schema or something,
49
0:2:44,12 --> 0:2:44,62
right?
50
0:2:45,04 --> 0:2:48,98
It will give you immediately something,
but then you want some
51
0:2:49,22 --> 0:2:50,52
details to be checked.
52
0:2:50,54 --> 0:2:53,76
And this is exactly when this checklist can be very helpful.
53
0:2:54,02 --> 0:2:58,82
So you can revisit and think and imagine when you will have gigabytes
54
0:2:58,86 --> 0:3:1,58
and terabytes of data, what will happen.
55
0:3:1,86 --> 0:3:7,64
So to avoid painful refactoring in the future and outages maybe
56
0:3:7,64 --> 0:3:12,16
or performance degradation, the price you can pay in the beginning
57
0:3:12,16 --> 0:3:14,98
is much lower than the price you pay later.
58
0:3:16,96 --> 0:3:21,86
So I never work with AI in 1 shot, I always make iterations to
59
0:3:21,86 --> 0:3:25,08
improve in any question and to revisit and sometimes combine
60
0:3:25,08 --> 0:3:26,18
multiple LLMs.
61
0:3:28,26 --> 0:3:31,36
This is exactly when you can use this list of questions.
62
0:3:31,36 --> 0:3:35,58
So first topic is the data type of primary key.
63
0:3:36,68 --> 0:3:39,56
Michael: Yeah, and I feel like the answer to this has changed,
64
0:3:39,64 --> 0:3:43,9
or my opinion on what the best default for this has changed,
65
0:3:43,9 --> 0:3:49,2
actually, in the last few years, just off the back of learning
66
0:3:49,2 --> 0:3:53,4
more about timestamp ordered UUIDs, right?
67
0:3:53,56 --> 0:3:57,88
This used to be an age-old debate, right, UUID versus integer
68
0:3:57,88 --> 0:3:59,34
or big integer really.
69
0:4:0,06 --> 0:4:3,84
And most of the kind of blog posts I would see are like trying
70
0:4:3,84 --> 0:4:6,3
to just encourage people to use BIGINT over int.
71
0:4:7,06 --> 0:4:12,48
But now that we have first-class support for time-ordered UUIDs,
72
0:4:13,48 --> 0:4:15,92
I don't see many reasons not to go with that.
73
0:4:15,92 --> 0:4:16,8
What do you think?
74
0:4:16,8 --> 0:4:20,78
Nikolay: Well, the size, 8, is double.
75
0:4:21,04 --> 0:4:21,54
Yeah.
76
0:4:21,58 --> 0:4:24,94
Yeah, your ID is 16 bytes, so it's quite a big price to pay.
77
0:4:25,2 --> 0:4:29,44
Each row, you need to have 8 more bytes.
78
0:4:30,14 --> 0:4:31,015
It's not that much, is it?
79
0:4:31,015 --> 0:4:32,02
In many cases, it's fine.
80
0:4:32,2 --> 0:4:34,34
Well, indexes will be also bigger.
81
0:4:34,82 --> 0:4:36,3
Indexes double, yeah.
82
0:4:37,12 --> 0:4:39,98
It might make sense if volumes are huge.
83
0:4:40,12 --> 0:4:44,44
But anyway, to keep things simple, a rule to remember, red flags,
84
0:4:45,06 --> 0:4:49,5
If you are prepared for really large volumes of data, it must
85
0:4:49,5 --> 0:4:55,16
not be int4, it should be int8.
86
0:4:55,44 --> 0:5:2,24
And in terms of UUID path, data type is always UUID for both
87
0:5:2,24 --> 0:5:4,18
version 4 and version 7.
88
0:5:4,6 --> 0:5:8,7
But you can check default how it's generated.
89
0:5:8,94 --> 0:5:13,78
And in modern Postgres, there is a function, I think in 18, only
90
0:5:13,78 --> 0:5:14,6
in the latest version.
91
0:5:14,6 --> 0:5:15,02
I think
92
0:5:15,02 --> 0:5:15,26
Michael: so.
93
0:5:15,26 --> 0:5:17,44
It didn't quite make 17, did it?
94
0:5:17,44 --> 0:5:21,76
Nikolay: So if you're on Postgres 18, so it's better to use UUIDv7
95
0:5:21,76 --> 0:5:25,86
but also remembering trade-offs, because there is
96
0:5:25,86 --> 0:5:29,52
timestamp there, and the values will be ordered, like with integer
97
0:5:30,06 --> 0:5:31,08
or big integer.
98
0:5:31,4 --> 0:5:33,18
So there is a nuance here.
99
0:5:33,58 --> 0:5:37,12
But performance-wise it's much better than UUIDv4.
100
0:5:37,9 --> 0:5:41,54
There are many articles about it and you just need to check,
101
0:5:41,54 --> 0:5:44,36
okay, are we going to go with integer?
102
0:5:45,04 --> 0:5:48,7
Then it's int8 or BIGINT, right?
103
0:5:48,96 --> 0:5:50,64
Are we going to go with UUID?
104
0:5:51,06 --> 0:5:56,2
Then default generation, default
should be version 7 for Postgres
105
0:5:56,2 --> 0:5:56,58
18.
106
0:5:56,58 --> 0:5:59,92
For older version, it's also not
an excuse to stick with version
107
0:5:59,92 --> 0:6:0,28
4.
108
0:6:0,28 --> 0:6:2,0
You can write your own function.
109
0:6:2,9 --> 0:6:5,42
And you ask AI to write your own
function.
110
0:6:6,04 --> 0:6:8,1
Michael: Yes, or generate them
outside of the database.
111
0:6:8,3 --> 0:6:12,62
Nikolay: I slightly prefer this
option less because if you have
112
0:6:12,62 --> 0:6:17,08
multiple application nodes, Who
knows what will happen with their
113
0:6:17,08 --> 0:6:17,58
clocks?
114
0:6:18,58 --> 0:6:25,4
And since UUIDv7 includes
timestamp inside it, order might
115
0:6:25,4 --> 0:6:29,9
be not, I don't know, like I prefer
to leave it on database shoulders
116
0:6:29,9 --> 0:6:32,8
to have like on the primary single
source of true.
117
0:6:32,8 --> 0:6:37,4
On the other side, UUID by design
is generated, it serves purpose
118
0:6:37,44 --> 0:6:40,46
to work on multiple, like on distributed
systems, right?
119
0:6:40,58 --> 0:6:42,18
So maybe it's not a big deal.
120
0:6:42,18 --> 0:6:43,82
Generate on client side.
121
0:6:44,38 --> 0:6:47,68
Michael: All I mean is if you're
on an old Postgres and it saves
122
0:6:47,68 --> 0:6:49,78
you creating your own function
or whatever.
123
0:6:50,28 --> 0:6:55,36
Nikolay: Postgres waited a couple
of years to bring UUIDv7 officially.
124
0:6:56,0 --> 0:6:59,1
Although again, you can create
even SQL function, even not PL/pgSQL
125
0:6:59,1 --> 0:7:0,8
function, simple SQL function.
126
0:7:0,8 --> 0:7:4,64
And if you ask your AI to search
for example, probably it will
127
0:7:4,64 --> 0:7:8,8
find my page how to do it, but
or other people's pages.
128
0:7:9,44 --> 0:7:13,68
But in other libraries from Google,
from Node.js library, everything,
129
0:7:14,24 --> 0:7:17,74
they have it for a couple of years
already, so for longer.
130
0:7:18,12 --> 0:7:20,74
They implemented before standard
was finalized.
131
0:7:21,62 --> 0:7:24,92
Michael: Yeah, so huge red flags
to look out for if it's using
132
0:7:24,92 --> 0:7:28,64
the integer data type, which is
the same as int4, or serial.
133
0:7:29,02 --> 0:7:31,92
I think AI sometimes uses, it will
define it as serial.
134
0:7:31,92 --> 0:7:35,46
So if you just see that word without
bigserial, you might hit
135
0:7:35,46 --> 0:7:38,48
that limit at 2000000000 or whatever
the, I can't remember the
136
0:7:38,48 --> 0:7:39,52
exact upper limit.
137
0:7:39,56 --> 0:7:44,08
Nikolay: Yeah, and avoid UUID version
4 if you want good performance
138
0:7:44,1 --> 0:7:45,4
on large volumes of data.
139
0:7:45,4 --> 0:7:46,56
Yeah, that's it actually.
140
0:7:46,56 --> 0:7:50,04
Let's keep it simple because we
could discuss more advanced situations.
141
0:7:50,74 --> 0:7:54,44
Michael: I think we should do a
whole episode someday on migrations,
142
0:7:54,72 --> 0:7:57,34
like for people that are getting
close to that 2 billion
143
0:7:57,34 --> 0:7:58,26
Nikolay: number, that
144
0:7:58,26 --> 0:7:59,44
Michael: would be a good topic.
145
0:7:59,44 --> 0:8:2,12
Nikolay: I can share, I had a lot
of experience with different
146
0:8:2,12 --> 0:8:2,62
approaches.
147
0:8:3,66 --> 0:8:4,28
Let's do that.
148
0:8:4,28 --> 0:8:4,78
Good.
149
0:8:5,66 --> 0:8:6,98
So how to define primary key?
150
0:8:6,98 --> 0:8:10,46
I can actually have an article
about it, how to...
151
0:8:11,28 --> 0:8:11,78
Cool.
152
0:8:12,1 --> 0:8:12,74
Michael: Let's do that.
153
0:8:12,74 --> 0:8:15,58
Nikolay: Covering multiple paths so we could talk about this.
154
0:8:16,38 --> 0:8:18,28
Number 2 is constraints.
155
0:8:18,62 --> 0:8:19,7
Topic is constraints.
156
0:8:21,38 --> 0:8:22,32
Michael: Other constraints.
157
0:8:22,54 --> 0:8:22,94
Nikolay: Yeah, yeah.
158
0:8:22,94 --> 0:8:23,6
Yeah, other.
159
0:8:23,94 --> 0:8:25,32
Beyond primary key.
160
0:8:25,58 --> 0:8:25,9
Yeah.
161
0:8:25,9 --> 0:8:31,68
So I would just ask, first thing, are we using constraints well?
162
0:8:31,78 --> 0:8:33,84
Like to myself or AI and so on.
163
0:8:33,84 --> 0:8:38,26
And because Postgres has 6 types of constraints and it's quite
164
0:8:38,26 --> 0:8:45,56
rich tooling, and it's much more convenient to have them earlier,
165
0:8:46,08 --> 0:8:48,48
not because adding them later is a big pain.
166
0:8:48,48 --> 0:8:54,44
Right now we have quite good support of two-step constraint definitions.
167
0:8:55,4 --> 0:9:0,48
I think in Postgres 19 it will be possible also if not null constraints,
168
0:9:0,48 --> 0:9:1,36
which is interesting.
169
0:9:1,36 --> 0:9:5,14
But anyway, check constraints, foreign keys, unique constraints,
170
0:9:5,14 --> 0:9:10,02
it's possible to create first in almost finished state and then
171
0:9:10,02 --> 0:9:13,38
finalize it to avoid the long-lasting locks.
172
0:9:13,38 --> 0:9:17,96
But this is not the reason why I prefer to think about it in
173
0:9:17,96 --> 0:9:18,82
the very beginning.
174
0:9:18,96 --> 0:9:21,8
The main reason is the quality of data.
175
0:9:21,98 --> 0:9:24,82
This is why constraints exist in the first place, right?
176
0:9:25,44 --> 0:9:29,62
So if you introduce constraints early in your project, data quality
177
0:9:29,62 --> 0:9:30,3
is good.
178
0:9:31,84 --> 0:9:32,52
Introducing them
179
0:9:32,52 --> 0:9:33,32
Michael: later probably will...
180
0:9:33,32 --> 0:9:36,42
Yeah, and database side as well.
181
0:9:36,42 --> 0:9:37,4
I mean, it just...
182
0:9:37,64 --> 0:9:38,6
Nikolay: Oh yeah, yeah.
183
0:9:38,94 --> 0:9:40,78
I don't trust constraints outside.
184
0:9:43,26 --> 0:9:45,54
With age, I became less radical.
185
0:9:45,66 --> 0:9:49,18
My points of view became less radical with it.
186
0:9:49,18 --> 0:9:53,54
So I just observe without fighting, like with backend engineers
187
0:9:53,94 --> 0:9:57,74
who try to implement constraints on application side.
188
0:9:57,74 --> 0:10:1,16
I just have a note, like they are not reliable because who knows
189
0:10:1,44 --> 0:10:5,08
which other layers of types of applications, especially these
190
0:10:5,08 --> 0:10:9,72
days with AI, it's so easy to add something else, implement it
191
0:10:9,72 --> 0:10:12,56
in a different framework or even a language, it's super easy
192
0:10:12,56 --> 0:10:13,24
right now.
193
0:10:13,38 --> 0:10:16,5
And you will need to reimplement those constraints, or you will
194
0:10:16,5 --> 0:10:19,46
just forget and miss them and data quality will suffer.
195
0:10:21,04 --> 0:10:25,64
Michael: So specifically, something I think I'd like to see more
196
0:10:25,64 --> 0:10:30,26
of or at least I see a lot of people not doing is columns that
197
0:10:30,26 --> 0:10:32,8
you're not expecting to put any null values in, or you don't
198
0:10:32,8 --> 0:10:36,22
want to have any null values in, actually explicitly making them
199
0:10:36,22 --> 0:10:37,0
not null.
200
0:10:37,06 --> 0:10:40,96
Like, if you don't specify, then columns are nullable.
201
0:10:41,76 --> 0:10:45,84
And I think most people, or at
least most AIs I see, are creating,
202
0:10:46,1 --> 0:10:49,58
like by default, they don't put
NOT NULL on every column.
203
0:10:49,84 --> 0:10:52,7
Nikolay: Yeah, there's an opinion
that NOT NULL should be default.
204
0:10:53,2 --> 0:10:56,2
Anyway, it's hard to have a default,
but inside your project
205
0:10:56,2 --> 0:11:0,1
you can say, okay, unless it's
explicitly specified, let's make
206
0:11:0,1 --> 0:11:1,28
columns NOT NULL.
207
0:11:1,62 --> 0:11:2,12
Exactly.
208
0:11:3,16 --> 0:11:6,82
And avoid troubles with NULL we
talked about in a special episode
209
0:11:6,82 --> 0:11:8,08
about NULLs, right?
210
0:11:8,26 --> 0:11:12,72
Michael: Yeah, well and I guess
in this specific case there is
211
0:11:12,72 --> 0:11:17,58
also, well with some constraints,
mostly we care about, we're
212
0:11:17,58 --> 0:11:20,52
doing it for quality reasons, but
there is, there is like a tiny
213
0:11:20,52 --> 0:11:23,56
amount of performance optimization
that comes out of them.
214
0:11:23,56 --> 0:11:28,48
I guess a bit more for unique constraints,
like if you, if the
215
0:11:28,48 --> 0:11:31,32
database knows for sure this is
going to be, this is unique,
216
0:11:31,32 --> 0:11:32,58
it can do some optimizations.
217
0:11:33,32 --> 0:11:36,02
So yeah, anyway, but yeah, good
point about, I like that about
218
0:11:36,02 --> 0:11:36,52
NOT NULL.
219
0:11:37,84 --> 0:11:39,82
Nikolay: Yeah, yeah, unique is
interesting stuff.
220
0:11:39,96 --> 0:11:44,08
And also, I don't think foreign
keys are underused in Postgres
221
0:11:44,12 --> 0:11:44,62
ecosystem.
222
0:11:45,3 --> 0:11:49,34
Days when everyone suggested that
foreign keys are super bad,
223
0:11:49,54 --> 0:11:52,8
for me it's 20 years ago, I remember
these topics, let's avoid
224
0:11:52,8 --> 0:11:53,76
foreign keys completely.
225
0:11:54,52 --> 0:11:57,5
Although we know that foreign keys
can bite badly, we had an
226
0:11:57,5 --> 0:12:4,14
episode with Metronome company
about issues basically with MultiXact
227
0:12:4,76 --> 0:12:8,56
related issues which can be caused
when you use foreign keys
228
0:12:8,56 --> 0:12:9,06
a lot.
229
0:12:9,06 --> 0:12:12,68
But anyway, I still think foreign
keys is a good thing to have,
230
0:12:12,8 --> 0:12:13,54
data quality.
231
0:12:13,86 --> 0:12:18,3
But I think what is usually underused
is check constraints.
232
0:12:18,84 --> 0:12:19,7
They are great.
233
0:12:20,38 --> 0:12:22,72
They could be used much more often.
234
0:12:23,1 --> 0:12:25,9
And for example, a classic example,
when I say, like, when I
235
0:12:25,9 --> 0:12:31,08
see in schema ENUM data type,
I think, okay, later, If we want
236
0:12:31,08 --> 0:12:36,36
to change this list of possible
values, how will our migration?
237
0:12:37,36 --> 0:12:38,94
Michael: Especially removing any.
238
0:12:39,74 --> 0:12:40,24
Yeah,
239
0:12:40,52 --> 0:12:43,76
Nikolay: so check constraints are
slightly more flexible.
240
0:12:46,58 --> 0:12:49,44
Well, Also removing is a topic,
right?
241
0:12:49,44 --> 0:12:52,18
But check constraint you can introduce
in 2 stages.
242
0:12:52,92 --> 0:12:57,04
Again, you can drop the old 1 and
you can first create a new
243
0:12:57,04 --> 0:13:0,6
1 in NOT VALID state and then validate
it in a separate transaction.
244
0:13:1,56 --> 0:13:7,12
But I just see it like quite useful,
quite convenient tool for
245
0:13:7,12 --> 0:13:11,04
me and you see it in schema right
away like this this column
246
0:13:11,04 --> 0:13:15,36
should be like for example in this
range or it should be this
247
0:13:15,36 --> 0:13:16,36
list or something.
248
0:13:17,02 --> 0:13:20,7
So I wish it was used more often.
249
0:13:21,48 --> 0:13:21,98
Michael: Yeah.
250
0:13:22,82 --> 0:13:24,3
What do you see in...
251
0:13:24,32 --> 0:13:28,9
Going back to foreign keys quickly, do you see a lot of the LLMs
252
0:13:29,32 --> 0:13:33,8
doing a lot of things on update cascade, on delete set NULL,
253
0:13:33,8 --> 0:13:34,76
things like that.
254
0:13:34,76 --> 0:13:38,68
Do you see any cascade stuff popping in there by default or not
255
0:13:38,68 --> 0:13:39,97
when it should be or when you think it
256
0:13:39,97 --> 0:13:40,08
Nikolay: should be?
257
0:13:40,08 --> 0:13:41,06
It's a good question.
258
0:13:41,18 --> 0:13:44,94
I actually don't remember having issues with this question.
259
0:13:45,12 --> 0:13:51,26
I know deferred constraints can might be an issue.
260
0:13:51,46 --> 0:13:55,12
I remember a case when pg_repack, it was a company called Miro,
261
0:13:55,12 --> 0:13:56,42
maybe you know it, right?
262
0:13:56,72 --> 0:13:59,7
They had issues with pg_repack and deferred constraints.
263
0:13:59,7 --> 0:14:3,42
There is an article, old article from them about this.
264
0:14:4,12 --> 0:14:8,72
But I don't remember particular issues here, and I think this
265
0:14:8,72 --> 0:14:12,48
leads us actually to the question for our particular projects,
266
0:14:12,8 --> 0:14:13,64
what's better?
267
0:14:14,68 --> 0:14:19,14
And If I had any doubts with AI, I would just make some experiments.
268
0:14:19,9 --> 0:14:24,96
Based on the product we are developing, I would say, okay, for
269
0:14:24,96 --> 0:14:30,58
example, in 1 year, how many of entities in which entity table
270
0:14:30,58 --> 0:14:31,4
we expect?
271
0:14:32,32 --> 0:14:37,46
All other tables, let's fill it with fake data, run vacuum Analyze
272
0:14:37,58 --> 0:14:41,76
to have good state, and then just explore plans with pgMustard,
273
0:14:41,98 --> 0:14:42,68
for example.
274
0:14:44,16 --> 0:14:47,96
Let's think what kind of workload we might have according to
275
0:14:47,96 --> 0:14:51,4
user stories in our specification of the product and so on.
276
0:14:51,58 --> 0:14:54,3
And then collect plans and see, right?
277
0:14:54,52 --> 0:14:59,44
And then include also modifying queries and collect those plans
278
0:14:59,44 --> 0:15:0,92
and think how it will work.
279
0:15:1,02 --> 0:15:5,74
It's so easy these days to make these experiments before we finalize
280
0:15:5,74 --> 0:15:9,38
all the decisions and then you will see it in action and it's
281
0:15:9,38 --> 0:15:9,88
great.
282
0:15:10,26 --> 0:15:15,22
So collect all the plans, find like weak spots where we have
283
0:15:15,38 --> 0:15:18,84
suboptimal plans and how we can deal with this.
284
0:15:18,84 --> 0:15:23,94
If, for example, we have entity and another, and 1 too many,
285
0:15:24,14 --> 0:15:26,14
and it's many, so many, right?
286
0:15:26,18 --> 0:15:30,14
And then we want Delete to be supported in UI, where people,
287
0:15:30,14 --> 0:15:33,58
as we know, expect 200 milliseconds, less than 1 second, let's
288
0:15:33,58 --> 0:15:34,26
say, right?
289
0:15:34,3 --> 0:15:38,48
And when this Delete of 1 row triggers propagating according
290
0:15:38,48 --> 0:15:43,62
to foreign key triggers, deletion of 1000000 rows, it cannot
291
0:15:43,62 --> 0:15:45,08
meet our requirements, right?
292
0:15:45,08 --> 0:15:49,64
Now we need to think about asynchronous deletion, maybe using
293
0:15:49,64 --> 0:15:52,66
some event queue system or something, right?
294
0:15:52,8 --> 0:15:53,3
Yeah.
295
0:15:53,6 --> 0:15:57,78
And this will pop up if you prepare a good experiment here, right?
296
0:15:57,78 --> 0:15:59,8
Without experiment, it's hard to say.
297
0:15:59,9 --> 0:16:1,3
You need to answer questions.
298
0:16:2,08 --> 0:16:5,64
How big are the tables and how this relationship between tables
299
0:16:5,64 --> 0:16:8,98
on foreign keys will be in the worst case, for example.
300
0:16:8,98 --> 0:16:13,52
Can user create like million of entities and then we need to
301
0:16:13,52 --> 0:16:14,96
delete this user, for example.
302
0:16:14,96 --> 0:16:18,16
This is a great question and with AI you can iterate so fast
303
0:16:18,16 --> 0:16:18,9
these days.
304
0:16:18,9 --> 0:16:19,62
So great.
305
0:16:20,28 --> 0:16:22,16
Collect plans and then think and decide.
306
0:16:23,5 --> 0:16:28,28
Okay, it's a good question, but I don't see it as a simple question.
307
0:16:28,78 --> 0:16:29,38
Michael: No, no, no.
308
0:16:29,38 --> 0:16:32,68
It was more, I just asked in case you'd seen there was a chance
309
0:16:32,68 --> 0:16:37,54
that LLMs were throwing it in on every foreign key and That was
310
0:16:37,54 --> 0:16:40,64
maybe a bad like bad deal Oh, but it sounds like they're not
311
0:16:40,64 --> 0:16:43,26
Nikolay: maybe a wrong person to ask about this because I don't
312
0:16:43,26 --> 0:16:46,24
just I design sometimes these days I have a few applications
313
0:16:46,36 --> 0:16:49,9
designed Yeah created from scratch with AI but I mostly deal
314
0:16:49,9 --> 0:16:53,26
with things which other people already created, and I just see
315
0:16:53,26 --> 0:16:54,9
how AI can help.
316
0:16:55,32 --> 0:17:0,12
So I'm not sure will this propagation of deletion, for example,
317
0:17:0,12 --> 0:17:6,5
will be supported in how exactly it will be implemented.
318
0:17:6,5 --> 0:17:10,02
But it's definitely worth thinking in the context of latencies
319
0:17:10,44 --> 0:17:13,44
and what should be offloaded and what tools we'll be using to
320
0:17:13,44 --> 0:17:15,78
offload in this, to background jobs, for example.
321
0:17:18,48 --> 0:17:24,72
It's just a thing that you need to, instead of thinking, looking
322
0:17:24,72 --> 0:17:30,08
at code, I would think looking at dynamic results, experiments.
323
0:17:31,64 --> 0:17:35,04
Michael: Yeah, and I'm thinking, like if we're talking specifically
324
0:17:35,4 --> 0:17:40,44
about schema design when you expect to hit scale, I might default
325
0:17:40,64 --> 0:17:47,56
to not using ON X CASCADE just by default because of those, because
326
0:17:47,56 --> 0:17:51,14
at scale it can become problematic so that would probably be,
327
0:17:51,34 --> 0:17:54,62
if you had to come up with a best practice for if you're designing
328
0:17:54,62 --> 0:17:56,58
at scale, maybe lean in that direction.
329
0:17:56,58 --> 0:18:0,22
Nikolay: That's interesting, that's interesting because This
330
0:18:0,22 --> 0:18:5,22
was my position for a long time, but it's not about AI building
331
0:18:5,22 --> 0:18:5,9
or something.
332
0:18:5,94 --> 0:18:6,92
This was my position.
333
0:18:7,04 --> 0:18:10,52
Let's, like, it's dangerous because who knows how many rows we
334
0:18:10,52 --> 0:18:11,46
will need to delete.
335
0:18:11,46 --> 0:18:13,98
Like, it's like it's unbounded delete, right?
336
0:18:14,06 --> 0:18:16,26
Unlimited delete, This is how it feels.
337
0:18:17,4 --> 0:18:18,8
Let's design without it.
338
0:18:18,8 --> 0:18:24,86
But then I see in my consulting practice, I see cases where really
339
0:18:24,86 --> 0:18:28,02
large projects have it and it's just not 1 project.
340
0:18:28,08 --> 0:18:31,96
Many projects I see and they keep it and somehow live with it.
341
0:18:31,96 --> 0:18:35,14
They at some point implement non-cascaded, like asynchronous
342
0:18:35,22 --> 0:18:39,22
deletion, but by default they still rely on cascaded deletes
343
0:18:39,22 --> 0:18:40,74
somehow and they are fine.
344
0:18:40,9 --> 0:18:41,68
This is interesting.
345
0:18:41,76 --> 0:18:44,28
So reality shows it's not that bad.
346
0:18:44,44 --> 0:18:48,34
But I agree with your way of thinking and this is my default
347
0:18:48,34 --> 0:18:49,66
way of thinking as well.
348
0:18:51,04 --> 0:18:55,6
Postgres can survive very terrible things these days much better
349
0:18:55,6 --> 0:18:57,1
than 20 years ago.
350
0:18:57,78 --> 0:18:58,78
Including the revolution
351
0:18:59,7 --> 0:19:0,06
of 100 years...
352
0:19:0,06 --> 0:19:0,48
Michael: The only
353
0:19:0,48 --> 0:19:4,44
time I see that being
a horrendous issue, even in small
354
0:19:4,44 --> 0:19:8,04
to medium-sized projects, is if
people haven't indexed the
355
0:19:8,48 --> 0:19:9,76
Nikolay: second part of foreign
key.
356
0:19:9,76 --> 0:19:11,3
Michael: Exactly, the referencing
column.
357
0:19:11,38 --> 0:19:12,88
Nikolay: There are 2 ends of foreign
key.
358
0:19:12,88 --> 0:19:18,42
1 is always indexed because it's
primary key, the other 1, it's
359
0:19:18,42 --> 0:19:20,04
your job to index it, right?
360
0:19:20,38 --> 0:19:21,48
To not forget.
361
0:19:22,06 --> 0:19:23,2
Yeah, yeah, yeah.
362
0:19:23,2 --> 0:19:25,52
We have now a checkup, old checkup,
actually.
363
0:19:25,52 --> 0:19:29,2
We have a special report just for
this case, yeah.
364
0:19:30,04 --> 0:19:35,06
Good, but it's all good questions,
but I feel we can spend a
365
0:19:35,06 --> 0:19:37,12
lot in the Constraints area.
366
0:19:37,12 --> 0:19:38,68
I think we had Constraints episodes.
367
0:19:38,68 --> 0:19:39,0
We did
368
0:19:39,0 --> 0:19:39,96
Michael: have an episode, yeah.
369
0:19:39,96 --> 0:19:41,74
Let's link that up and move on.
370
0:19:41,98 --> 0:19:42,86
Nikolay: Yeah, let's move on.
371
0:19:42,86 --> 0:19:47,18
Some entertaining topic, Column
Tetris, which is sometimes it
372
0:19:47,18 --> 0:19:51,3
might bring some quite interesting
and good savings.
373
0:19:52,12 --> 0:19:55,66
In many cases it won't, but I just
see people enjoy this topic,
374
0:19:55,94 --> 0:19:58,02
and I decided to include it.
375
0:19:58,02 --> 0:20:3,24
So the idea is, when you design
a new Schema, the price to pay
376
0:20:3,26 --> 0:20:5,16
to reorder Columns is very low.
377
0:20:5,16 --> 0:20:7,38
You can reorder it easily.
378
0:20:9,22 --> 0:20:13,48
The problem is that for Postgres,
physically, the Column order
379
0:20:13,48 --> 0:20:13,98
matters.
380
0:20:15,16 --> 0:20:19,92
Due to padding alignment, if int2, for example, is followed
381
0:20:19,92 --> 0:20:26,64
by int8, you will see a gap
of 6 bytes of zeros in every
382
0:20:26,64 --> 0:20:27,14
row.
383
0:20:27,34 --> 0:20:28,34
And this is bad.
384
0:20:28,58 --> 0:20:33,34
It's like a waste of storage and
also memory, which is not only
385
0:20:33,34 --> 0:20:36,54
disk, it will pollute with those
zeros, it will pollute memory.
386
0:20:36,58 --> 0:20:41,34
And sometimes you can be so unlucky,
I saw cases like 30% of
387
0:20:41,74 --> 0:20:43,48
each row is like zeros.
388
0:20:44,54 --> 0:20:48,96
It's, And Postgres doesn't have
automatic reordering of Columns.
389
0:20:48,96 --> 0:20:51,24
It could, actually, but it doesn't
have it.
390
0:20:51,9 --> 0:20:54,76
Michael: So the order you define
it in, let's say you do create
391
0:20:54,76 --> 0:20:58,78
table, the order you put those
Columns in, that's the order they
392
0:20:58,78 --> 0:20:59,76
end up on disk.
393
0:20:59,76 --> 0:21:3,14
And every new Column you add goes
at the end.
394
0:21:4,08 --> 0:21:5,28
That makes sense, right?
395
0:21:5,28 --> 0:21:8,68
But even if it could be slotted
in, let's say you add a new Boolean
396
0:21:8,68 --> 0:21:11,54
type, and it could, I don't actually
know if that would work,
397
0:21:11,54 --> 0:21:13,26
but it always goes on the end.
398
0:21:13,26 --> 0:21:14,044
Nikolay: And there's no way to
sum up.
399
0:21:14,044 --> 0:21:14,64
Boolean is 1 byte.
400
0:21:15,28 --> 0:21:17,64
Michael: Yeah, but I wonder if
that would work.
401
0:21:17,64 --> 0:21:19,58
Nikolay: In Boolean, 7 bits are
wasted already.
402
0:21:21,04 --> 0:21:22,96
If you zoom in like under my
403
0:21:22,96 --> 0:21:24,96
Michael: let's put brilliant brilliant
brilliant next to each
404
0:21:24,96 --> 0:21:25,66
other right
405
0:21:25,84 --> 0:21:28,18
Nikolay: no no no no no Boolean
406
0:21:28,18 --> 0:21:28,52
Michael: really
407
0:21:28,52 --> 0:21:29,74
Nikolay: is 1 bit.
408
0:21:30,16 --> 0:21:30,7
Michael: Oh bit.
409
0:21:30,7 --> 0:21:32,44
Yeah, good point good point, so
yeah,
410
0:21:32,64 --> 0:21:34,66
Nikolay: But it's stored as a 1
byte.
411
0:21:35,2 --> 0:21:37,36
7 bits already wasted.
412
0:21:37,8 --> 0:21:43,48
But if you have Boolean and then
timestamp, for example.
413
0:21:44,34 --> 0:21:48,58
For example, first column, as we
all agree, should be id, the
414
0:21:48,58 --> 0:21:49,56
primary key.
415
0:21:49,64 --> 0:21:52,56
I'm joking, it's not always, but
it's very popular.
416
0:21:52,9 --> 0:21:57,16
Id is the first column, int8, occupies the whole 8-byte
417
0:21:57,18 --> 0:21:57,68
word.
418
0:21:58,46 --> 0:22:0,38
And then we have Boolean and then
timestamp.
419
0:22:1,1 --> 0:22:5,98
Not only you wasted 7 bits inside
Boolean, it's already by design,
420
0:22:5,98 --> 0:22:9,12
like Postgres doesn't have less
than 1 byte.
421
0:22:10,02 --> 0:22:15,6
But then you waste 7 bytes, so
7 bits first and 7 bytes because
422
0:22:15,6 --> 0:22:17,06
of this alignment padding.
423
0:22:17,54 --> 0:22:23,0
And your Boolean, which is like,
by sense, it's only 1 bit, suddenly
424
0:22:23,0 --> 0:22:24,06
occupies 8 bytes.
425
0:22:24,06 --> 0:22:27,38
It's a huge waste of resources,
right?
426
0:22:27,72 --> 0:22:33,18
But the bigger picture here is
that people usually realize that
427
0:22:33,18 --> 0:22:37,48
waste is significant only when
it's a huge table, like 100 million
428
0:22:37,48 --> 0:22:38,2
rows, right?
429
0:22:38,44 --> 0:22:42,7
And then, oh, so many spaces wasted
because we didn't play this
430
0:22:42,7 --> 0:22:43,94
column Tetris initially.
431
0:22:45,06 --> 0:22:49,62
But this is like chicken or egg
problem, maybe inverted.
432
0:22:49,84 --> 0:22:54,02
Anyway, so you realize it later,
but you could do it earlier,
433
0:22:54,1 --> 0:22:56,96
but you didn't think about it because
when you have only 1,000
434
0:22:56,96 --> 0:22:59,32
rows, for example, it costs you
nothing.
435
0:23:0,18 --> 0:23:2,46
But again, with AI, it's so easy.
436
0:23:2,9 --> 0:23:5,3
You just say, let's play Column
Tetris in Postgres.
437
0:23:5,84 --> 0:23:7,04
AI should know it.
438
0:23:7,04 --> 0:23:9,88
I haven't tried, but I'm pretty
sure it should know it, because
439
0:23:9,88 --> 0:23:11,84
there are many articles about it.
440
0:23:12,12 --> 0:23:13,54
Michael: Oh, and we did an episode.
441
0:23:14,32 --> 0:23:17,08
Nikolay: Yeah, starting from Stack
Overflow, where this topic
442
0:23:17,08 --> 0:23:18,9
was very well described.
443
0:23:19,4 --> 0:23:22,16
So AI should know it, this topic.
444
0:23:22,66 --> 0:23:25,84
When you create a new table, you
think many rows will be stored,
445
0:23:25,84 --> 0:23:28,22
just let's apply column Tetris
to this.
446
0:23:28,48 --> 0:23:28,98
Boom.
447
0:23:29,38 --> 0:23:30,74
It will reorder columns.
448
0:23:32,2 --> 0:23:32,88
Never try it again.
449
0:23:32,88 --> 0:23:36,1
Michael: Yeah, what do you think,
like I feel like there's a...
450
0:23:37,58 --> 0:23:40,52
I don't know if I'd still want
to have, I'd still want to do
451
0:23:40,52 --> 0:23:46,32
it myself just because I feel like
there's also a natural order
452
0:23:46,32 --> 0:23:46,92
for columns.
453
0:23:46,92 --> 0:23:51,68
So I would want to put all the
8-byte ones together and 16-byte
454
0:23:51,76 --> 0:23:55,44
ones in to make sure they're all
with similar and then all of
455
0:23:55,44 --> 0:23:55,94
the...
456
0:23:56,54 --> 0:23:57,16
Yeah, exactly.
457
0:23:57,16 --> 0:23:58,04
Nikolay: Ah, similar.
458
0:23:59,06 --> 0:24:2,82
Michael: But equally, like, imagine
if it's like a users table
459
0:24:3,2 --> 0:24:8,72
and I could put the I could put
certain information about that
460
0:24:8,72 --> 0:24:12,08
user that makes sense to go together
together and then information
461
0:24:12,22 --> 0:24:15,92
about something like maybe like
there all the IDs that maybe
462
0:24:15,92 --> 0:24:19,24
the foreign keys like their team
ID or their organization, all
463
0:24:19,24 --> 0:24:22,58
those kind of like referencing
columns, put those together.
464
0:24:22,58 --> 0:24:25,78
So I know the order doesn't matter.
465
0:24:25,84 --> 0:24:28,86
I know you could just specify column
order, but you know when
466
0:24:28,86 --> 0:24:30,72
you do SELECT * or you just...
467
0:24:30,72 --> 0:24:34,34
Nikolay: This is what I was trying
to say, like, are you trying
468
0:24:34,34 --> 0:24:37,06
to tell me that you're using SELECT *?
469
0:24:38,04 --> 0:24:39,1
And here we go.
470
0:24:39,34 --> 0:24:41,28
So I'm joking, actually.
471
0:24:41,28 --> 0:24:43,16
I'm using SELECT * all the time.
472
0:24:44,34 --> 0:24:48,54
Also all the time I see articles
how bad it is to use SELECT *.
473
0:24:49,24 --> 0:24:49,74
Right?
474
0:24:50,14 --> 0:24:56,5
It's convenient, I know, yeah,
but also if you manually work,
475
0:24:56,98 --> 0:25:1,78
it's inconvenient to have order
and SELECT * doesn't work well.
476
0:25:1,84 --> 0:25:4,2
But applications shouldn't use
SELECT *.
477
0:25:4,2 --> 0:25:4,98
Michael: You're right.
478
0:25:5,08 --> 0:25:8,48
And I'm even thinking about examples
like the system views in
479
0:25:8,48 --> 0:25:8,86
Postgres.
480
0:25:8,86 --> 0:25:11,92
Like you do SELECT * FROM pg_stat_statements or something.
481
0:25:12,62 --> 0:25:14,12
But that's not even the table,
right?
482
0:25:14,12 --> 0:25:14,92
That's the view.
483
0:25:14,92 --> 0:25:16,4
And then they can define the order.
484
0:25:16,4 --> 0:25:18,3
So it doesn't actually matter.
485
0:25:19,12 --> 0:25:20,52
Nikolay: Yeah, it's an interesting
topic.
486
0:25:21,04 --> 0:25:24,66
I played Column Tetris a few times
heavily, and then it was really
487
0:25:24,66 --> 0:25:25,16
inconvenient.
488
0:25:25,68 --> 0:25:27,22
Oh, this is an ugly table.
489
0:25:27,6 --> 0:25:28,54
It looks ugly.
490
0:25:28,78 --> 0:25:30,4
Like, this order is ugly.
491
0:25:30,4 --> 0:25:33,34
But at the same time, it's your
problem that because you use
492
0:25:33,34 --> 0:25:36,1
SELECT *, which is considered
like not good practice.
493
0:25:36,28 --> 0:25:38,58
It's practice for exploring and
so on.
494
0:25:39,14 --> 0:25:42,8
Okay, explore with AI, ask to provide
better order or something.
495
0:25:42,8 --> 0:25:43,64
I don't know.
496
0:25:44,24 --> 0:25:47,84
But anyway, if you talk about,
if you focus on performance, again
497
0:25:47,84 --> 0:25:51,24
I saw cases where it was significant
waste.
498
0:25:51,42 --> 0:25:53,86
Michael: 1 of the first articles
I saw and it was by Braintree
499
0:25:53,86 --> 0:25:57,34
and I think they reported an average
of about 10% on some of
500
0:25:57,34 --> 0:25:58,44
their largest tables.
501
0:25:58,44 --> 0:25:59,0
Nikolay: It depends.
502
0:25:59,76 --> 0:26:2,48
Michael: Obviously it completely
depends but that was quite surprising
503
0:26:2,48 --> 0:26:2,68
to me.
504
0:26:2,68 --> 0:26:5,5
I know you mentioned an extreme
case where you saw 30%, but I
505
0:26:5,5 --> 0:26:9,96
was quite surprised that in an
actual production case, it was
506
0:26:9,96 --> 0:26:11,0
as high as 10%.
507
0:26:11,0 --> 0:26:13,68
And when you're talking about hundreds
of gigabytes or terabytes,
508
0:26:15,06 --> 0:26:16,36
That's actually a decent amount.
509
0:26:17,32 --> 0:26:18,3
Nikolay: Yeah, yeah, exactly.
510
0:26:18,52 --> 0:26:21,88
The problem also, of course, in
existing projects which were
511
0:26:21,88 --> 0:26:25,96
developed during many years, usually
it's not so simple because
512
0:26:25,96 --> 0:26:30,32
the schema is evolving during many
years and you cannot insert
513
0:26:30,32 --> 0:26:31,94
your new column in the middle.
514
0:26:32,04 --> 0:26:34,22
It's always, as you said, at the
end.
515
0:26:34,44 --> 0:26:38,9
So sometimes we do need to, for
example, to change the order
516
0:26:39,24 --> 0:26:42,08
using some redesign thing.
517
0:26:42,14 --> 0:26:48,78
By the way, have you heard that
Postgres 19 will have repack
518
0:26:48,84 --> 0:26:52,58
and also repack concurrently, which
is basically vacuum concurrently.
519
0:26:53,14 --> 0:26:55,4
We had an episode with author of
pg_squeeze.
520
0:26:56,2 --> 0:26:58,02
It's a vacuum full concurrently.
521
0:26:58,78 --> 0:26:59,76
Exactly, exactly.
522
0:26:59,76 --> 0:27:1,02
Not vacuum concurrently.
523
0:27:1,02 --> 0:27:4,78
But the decision was made to name
it Repack concurrently.
524
0:27:5,02 --> 0:27:7,78
Michael: I think that's sensible,
getting away from people thinking
525
0:27:7,78 --> 0:27:9,4
it's vacuum related, yeah.
526
0:27:9,86 --> 0:27:13,94
Nikolay: I haven't looked into
details, but naturally this is
527
0:27:13,94 --> 0:27:17,64
the point where you could reorder
columns, because it's based
528
0:27:17,64 --> 0:27:18,76
on logical replication.
529
0:27:19,74 --> 0:27:23,16
Michael: Yeah, I suspect if they
do that, it will be a different
530
0:27:23,16 --> 0:27:24,1
keyword again.
531
0:27:24,72 --> 0:27:25,46
Nikolay: Who knows?
532
0:27:25,6 --> 0:27:27,38
Again, I haven't looked into details.
533
0:27:27,44 --> 0:27:30,84
There are long discussions on pgsql-hackers
mailing list.
534
0:27:31,04 --> 0:27:34,54
It just feels naturally, because
it's based on logical replication.
535
0:27:34,54 --> 0:27:37,86
This is where you can reorder columns
and play Tetris for existing
536
0:27:37,86 --> 0:27:39,14
large tables.
537
0:27:39,62 --> 0:27:41,78
I hope it will be implemented at
some point.
538
0:27:41,78 --> 0:27:43,94
Maybe it's already partially implemented
or something.
539
0:27:43,94 --> 0:27:44,74
I don't know.
540
0:27:45,16 --> 0:27:49,54
You basically, before you start
filling the new table, you can
541
0:27:49,54 --> 0:27:51,52
reorder it, because it's logical,
right?
542
0:27:52,08 --> 0:27:53,22
It should be possible.
543
0:27:53,94 --> 0:27:56,4
Anyway, this is content, it's an
entertaining topic.
544
0:27:56,4 --> 0:28:0,68
Let's move on to a more serious
topic called indexes, index chaos.
545
0:28:1,34 --> 0:28:6,04
If you just use AI and if you don't
spend enough time for things
546
0:28:6,04 --> 0:28:11,3
as I said like experimenting with
real data benchmarks you might
547
0:28:11,38 --> 0:28:15,08
end up with under-indexed situation
or over-indexed situation
548
0:28:16,06 --> 0:28:18,98
And usually people are scared to
have under-indexed.
549
0:28:19,46 --> 0:28:22,16
In my practice, I see over-indexed
very often.
550
0:28:22,54 --> 0:28:23,74
Sometimes to extreme.
551
0:28:25,3 --> 0:28:28,3
Michael: I guess for new projects,
I would be surprised to see
552
0:28:28,3 --> 0:28:29,16
them over-indexed.
553
0:28:30,08 --> 0:28:30,76
But for- it's
554
0:28:30,76 --> 0:28:32,58
Nikolay: possible, it's possible
still.
555
0:28:32,78 --> 0:28:36,1
Michael: Is that like as a result
of AI or something else?
556
0:28:36,1 --> 0:28:39,62
Nikolay: Well, the problem here
is that, again, everything depends
557
0:28:39,62 --> 0:28:43,38
on your prompts, depends on angles.
558
0:28:43,68 --> 0:28:50,24
If you say we will need to order
by arbitrary column, right?
559
0:28:50,34 --> 0:28:53,92
Remember our episode with CTO Gadget,
Harry, right?
560
0:28:54,38 --> 0:28:56,42
So any column we can reorder.
561
0:28:57,44 --> 0:29:2,06
Naturally, AI might decide to put
index on every column, right?
562
0:29:2,86 --> 0:29:3,84
But is it really
563
0:29:3,84 --> 0:29:4,54
Michael: what you want?
564
0:29:4,54 --> 0:29:7,72
But that's what Gadget decided
to do as well, right?
565
0:29:7,8 --> 0:29:11,66
Nikolay: Right, but with understanding
consequences, right?
566
0:29:11,98 --> 0:29:17,68
But in reality, will it be used
If 1 year later you see that
567
0:29:17,68 --> 0:29:21,58
90% of those indexes are unused
and the gadget has special case
568
0:29:21,58 --> 0:29:25,84
They have many applications to
support if you design your own
569
0:29:25,84 --> 0:29:29,96
only 1 application This is where
I would just stop and think
570
0:29:29,96 --> 0:29:34,3
with AI again with some experiments
I would ask questions like
571
0:29:34,3 --> 0:29:36,46
this all depends on workload, right?
572
0:29:36,46 --> 0:29:39,66
Which queries we will have, right?
573
0:29:40,08 --> 0:29:44,34
And I would just think, which queries
we really need and which
574
0:29:44,34 --> 0:29:49,5
we won't need, and then choose
a proper minimal set of indexes.
575
0:29:51,6 --> 0:29:52,52
Michael: I like that.
576
0:29:52,64 --> 0:29:56,8
I think there's also like some
rules of thumb we can give people
577
0:29:56,8 --> 0:30:0,32
in terms of like how many is where
it starts to get dangerous
578
0:30:0,32 --> 0:30:1,2
for other reasons.
579
0:30:1,88 --> 0:30:6,72
But the kind of 1 place where it's
not even and it depends is
580
0:30:6,96 --> 0:30:11,4
overlapping and I see more and
more where people are adding indexes
581
0:30:11,58 --> 0:30:13,24
based on AI suggestions.
582
0:30:13,78 --> 0:30:16,72
They're adding indexes they've
effectively already got or they're
583
0:30:16,72 --> 0:30:20,8
adding ones that overlap with indexes
they've already got, and
584
0:30:20,8 --> 0:30:23,34
therefore only 1 of them is needed.
585
0:30:24,14 --> 0:30:26,52
And by the way, it won't always
show up in your stats, it won't
586
0:30:26,52 --> 0:30:29,8
always show up as an unused index,
because it is still using
587
0:30:29,8 --> 0:30:30,92
the smaller index.
588
0:30:31,56 --> 0:30:35,98
Nikolay: It will show up in PostgreSQL
stats because it's called
589
0:30:35,98 --> 0:30:39,52
in our case redundant index in
Postgres monitoring systems and
590
0:30:39,52 --> 0:30:40,02
CheckUp.
591
0:30:40,46 --> 0:30:42,42
I never heard the term overlapping.
592
0:30:43,38 --> 0:30:46,64
I see people talk about duplicate
indexes, which is like trivial
593
0:30:46,64 --> 0:30:48,9
case, exact same definition.
594
0:30:50,28 --> 0:30:52,2
But we call it redundant indexes.
595
0:30:52,2 --> 0:30:57,18
It's not an easy topic as 1 might
think, but we solved it in
596
0:30:57,18 --> 0:30:57,8
many parts.
597
0:30:57,8 --> 0:31:3,22
Over years, we have quite a good
detection approach, how to reliably
598
0:31:3,58 --> 0:31:5,14
detect redundant indexes.
599
0:31:5,86 --> 0:31:10,64
And as you say, this is true, in
many cases all of those indexes
600
0:31:10,64 --> 0:31:11,36
are used.
601
0:31:11,82 --> 0:31:16,56
And it's quite like, there is a
big fear, oh, I'm going to drop
602
0:31:16,56 --> 0:31:18,98
this, it's used, should I drop
it?
603
0:31:19,28 --> 0:31:23,82
Yeah, but the reports we have quite
reliable and of course I
604
0:31:23,82 --> 0:31:27,84
wish we had very native standard
algorithm to disable indexes
605
0:31:27,84 --> 0:31:32,32
for quite for some time like without
involving HypoPG and hypothetical
606
0:31:32,5 --> 0:31:35,46
indexes, which currently supports
it.
607
0:31:35,82 --> 0:31:38,14
Michael: Our last episode was on
what's missing in Postgres,
608
0:31:38,14 --> 0:31:41,5
and I think we got a nice comment,
I've forgotten who from, but
609
0:31:41,52 --> 0:31:45,26
saying 1 of their features would
be make this
610
0:31:45,26 --> 0:31:45,32
Nikolay: invisible.
611
0:31:45,32 --> 0:31:48,26
Temporally hide index from the
planner.
612
0:31:48,48 --> 0:31:52,54
There are extensions for it, HypoPG
supports it, but we want
613
0:31:52,54 --> 0:31:58,04
some mechanism beyond index setting,
and index is valid to false.
614
0:31:58,38 --> 0:31:59,14
We want this.
615
0:31:59,14 --> 0:32:0,66
Yeah, yeah, yeah, I agree.
616
0:32:0,96 --> 0:32:5,14
And this is a good thing to probably
work on for those who want
617
0:32:5,24 --> 0:32:9,8
to start hacking Postgres so it's
a good thing to officially implement
618
0:32:9,8 --> 0:32:10,3
something.
619
0:32:10,68 --> 0:32:15,94
Anyway, questions to ask is, they
all start here, should start
620
0:32:15,94 --> 0:32:17,36
from understanding workload.
621
0:32:17,86 --> 0:32:22,18
So we cannot think about indexes
without understanding workload.
622
0:32:22,28 --> 0:32:26,14
So first we need to generate some
fake data and then start thinking
623
0:32:26,38 --> 0:32:28,04
about usage patterns.
624
0:32:28,7 --> 0:32:30,92
And without AI It's really hard
work.
625
0:32:30,92 --> 0:32:32,06
It's a lot of time.
626
0:32:32,72 --> 0:32:34,8
With AI it becomes easier.
627
0:32:37,0 --> 0:32:38,7
If you have user stories defined
already, you understand what
628
0:32:38,7 --> 0:32:42,38
kind of patterns we will have,
and you can start imagining.
629
0:32:43,38 --> 0:32:47,78
It will not be 100% accurate, but
it will be good enough, much
630
0:32:47,78 --> 0:32:51,82
better than without it or with
manual work.
631
0:32:51,82 --> 0:32:56,4
So you can iterate here and see,
and then with queries in hand
632
0:32:56,84 --> 0:33:2,04
and some like lab already developed,
I see it should take 1 or
633
0:33:2,04 --> 0:33:7,08
2 hours for like medium-sized project
I mean in terms of complexity
634
0:33:7,42 --> 0:33:10,58
and then you can start iterating
collecting plans and think which
635
0:33:10,58 --> 0:33:12,32
indexes you can have playing
636
0:33:13,02 --> 0:33:15,58
Michael: But also this whilst this
might be the most important
637
0:33:15,58 --> 0:33:19,2
in terms of performance that we've
talked about so far it's also
638
0:33:19,2 --> 0:33:24,52
the the easiest to change later
right we can yeah yeah we can
639
0:33:24,52 --> 0:33:27,88
set up a certain set of indexes
and later decide we want a slightly
640
0:33:27,88 --> 0:33:30,92
different set of indexes and that's
not super painful in a lot
641
0:33:30,92 --> 0:33:34,54
in most cases especially if there's
no partitioning involved
642
0:33:35,28 --> 0:33:40,2
so that for me feels like yeah
it's important it's really important
643
0:33:40,58 --> 0:33:44,34
But if we don't get it right on
day 1, it's much, much easier
644
0:33:44,34 --> 0:33:47,82
to fix than the primary key being
the wrong data type or not
645
0:33:47,82 --> 0:33:50,34
having the right constraints in
place, that kind of thing.
646
0:33:50,72 --> 0:33:52,28
Or even the column order, yeah.
647
0:33:52,72 --> 0:33:56,82
Nikolay: So the point is you should
build like wind tunnel for
648
0:33:56,82 --> 0:33:58,04
your project, right?
649
0:33:58,86 --> 0:34:1,58
Like, you know, like, Test lab,
yeah.
650
0:34:1,86 --> 0:34:2,98
Database lab.
651
0:34:3,42 --> 0:34:7,42
And then you should put this workload like wind and see like
652
0:34:7,42 --> 0:34:11,94
how your profile of your database behaves under this wind.
653
0:34:12,28 --> 0:34:14,84
Michael: Yeah, a lot of people do that with production.
654
0:34:15,14 --> 0:34:17,7
They just do it with their slow queries.
655
0:34:18,94 --> 0:34:22,84
Nikolay: But with AI it's easier to have massive experiments
656
0:34:23,0 --> 0:34:25,06
before you finalize all the decisions.
657
0:34:25,38 --> 0:34:27,28
So my point is do it.
658
0:34:27,28 --> 0:34:30,72
And then questions to ask, are there any indexes we don't need
659
0:34:30,72 --> 0:34:33,9
in terms of they are redundant or unused or what indexes are
660
0:34:33,9 --> 0:34:36,72
missing because we have bad plans and this again like pgMustard
661
0:34:37,2 --> 0:34:40,62
will visualize it and so on and you have API already, right?
662
0:34:40,88 --> 0:34:42,94
Michael: Yeah Yeah, it's quite nice
663
0:34:43,14 --> 0:34:46,3
Nikolay: You can connect your AI to pgMustard for example and
664
0:34:46,3 --> 0:34:49,44
ask to use pgMustard to explain what's happening and find bad
665
0:34:49,44 --> 0:34:53,5
problems and missing indexes with this lab environment.
666
0:34:54,64 --> 0:34:55,12
Great?
667
0:34:55,12 --> 0:34:55,78
Michael: Yeah, OK.
668
0:34:57,1 --> 0:35:1,72
Going just like to the extreme, How many indexes on a table would
669
0:35:1,72 --> 0:35:5,7
you automatically just think, oh wow, that's like a lot?
670
0:35:6,22 --> 0:35:10,54
Maybe it's justified, but I'm already thinking that's too many.
671
0:35:10,92 --> 0:35:13,34
Nikolay: These rule of thumbs are quite weak in my opinion, but
672
0:35:13,34 --> 0:35:14,44
we have them anyway.
673
0:35:14,54 --> 0:35:17,44
Like for example, if the volume of indexes exceeds the volume
674
0:35:17,44 --> 0:35:20,88
of data, it's already some bell is ringing.
675
0:35:23,22 --> 0:35:27,72
Or before Postgres 18 with this nasty lightweight lock manager
676
0:35:27,72 --> 0:35:31,22
problem means I don't want more than 15 indexes per table.
677
0:35:31,96 --> 0:35:34,62
Michael: Because of the 16 relation limit.
678
0:35:34,94 --> 0:35:38,76
Nikolay: So simple, primary key lookup which quite likely will
679
0:35:38,76 --> 0:35:45,22
be very, will have quite high QPS will suffer, and because of
680
0:35:45,22 --> 0:35:45,72
locking.
681
0:35:46,26 --> 0:35:49,7
So there are a couple of kind of these rules, right, but they
682
0:35:49,7 --> 0:35:54,64
are not strict and again I say that I consider them weak, but
683
0:35:54,72 --> 0:35:55,22
maybe...
684
0:35:55,44 --> 0:35:57,78
Michael: But I think it's still helpful, right, if you've got
685
0:35:57,78 --> 0:36:2,56
a schema designed by AI and it's got 20 indexes on 1 table, maybe
686
0:36:2,56 --> 0:36:4,18
consider is that smart?
687
0:36:6,8 --> 0:36:11,3
Nikolay: But it happens, index data exceeding heap data it happens.
688
0:36:12,88 --> 0:36:15,6
Michael: Especially with some, especially like if you're doing
689
0:36:15,6 --> 0:36:16,34
gin indexes.
690
0:36:16,64 --> 0:36:17,34
Nikolay: Oh yes.
691
0:36:17,4 --> 0:36:19,54
Michael: If it's just B-trees, yeah.
692
0:36:20,38 --> 0:36:22,46
Nikolay: Yeah, okay, great.
693
0:36:22,54 --> 0:36:25,88
So the final topic, we couldn't decide on which 1 to choose.
694
0:36:25,88 --> 0:36:27,6
We had 2 ideas.
695
0:36:27,8 --> 0:36:30,7
1 is RLS and another is partitioning.
696
0:36:32,4 --> 0:36:33,6
Let's touch both maybe.
697
0:36:33,6 --> 0:36:34,4
Michael: Yeah I think so.
698
0:36:34,4 --> 0:36:39,3
So from an AI perspective, like when you're asking it for a new
699
0:36:39,3 --> 0:36:43,3
schema, do you see partitioning come up?
700
0:36:43,38 --> 0:36:44,4
Does it over partition?
701
0:36:44,4 --> 0:36:48,64
Nikolay: It won't come up Unless
you start saying I'm going to
702
0:36:48,64 --> 0:36:54,72
have a lot of data here and I want
good performance, I want billions
703
0:36:54,72 --> 0:36:58,04
of rows stored in this table, it
should be maintainable.
704
0:36:58,26 --> 0:37:1,3
It will naturally come to the idea
of having partitioning.
705
0:37:3,26 --> 0:37:7,32
Partitioning will be interesting
to join with UUID version 7 or
706
0:37:7,36 --> 0:37:8,9
8, it doesn't matter.
707
0:37:8,92 --> 0:37:13,12
It's possible, again, I have a
recipe for this in my set of how-tos.
708
0:37:14,06 --> 0:37:18,46
And it can be also the question,
like partitioning you cannot
709
0:37:18,46 --> 0:37:20,96
develop without understanding workload
again.
710
0:37:21,26 --> 0:37:24,62
It's similar to indexes, like it
will be something, but how it
711
0:37:24,62 --> 0:37:25,84
will behave, who knows?
712
0:37:25,84 --> 0:37:29,36
Maybe you will end up, your queries
will need to scan all partitions,
713
0:37:29,38 --> 0:37:31,36
which is terrible in most cases.
714
0:37:32,1 --> 0:37:34,24
And then plan behavior.
715
0:37:34,46 --> 0:37:37,7
Anyway, the question is to ask,
like you said requirements, I
716
0:37:37,7 --> 0:37:41,94
want a lot of rows and I want well
maintainability of this.
717
0:37:43,08 --> 0:37:43,82
Help me.
718
0:37:43,94 --> 0:37:46,2
Michael: Like deleting old data,
are you thinking?
719
0:37:46,72 --> 0:37:49,04
Nikolay: No, creating indexes,
for example, or reindexing.
720
0:37:49,12 --> 0:37:50,02
Michael: OK, yeah, sure.
721
0:37:50,02 --> 0:37:50,52
Vacuum.
722
0:37:50,9 --> 0:37:51,9
Nikolay: Vacuum, yeah, yeah.
723
0:37:51,9 --> 0:37:56,72
These are problems biting us much
more often and badly than just,
724
0:37:56,72 --> 0:37:59,76
I don't know, like, we talk about
this a lot, as well, right?
725
0:37:59,76 --> 0:38:0,7
Michael: I know, I know.
726
0:38:0,7 --> 0:38:1,2
Nikolay: Yeah.
727
0:38:2,38 --> 0:38:5,98
Direct performance benefits from
partitioning are good, but also
728
0:38:5,98 --> 0:38:13,32
the problem when creation of index
or full table vacuum, it will
729
0:38:13,32 --> 0:38:17,22
take hours or half a day, it's
already so painful.
730
0:38:17,24 --> 0:38:22,4
Especially if it's to prevent transaction
wraparound, or if it's,
731
0:38:22,7 --> 0:38:25,16
again, indexing means that
xmin horizon...
732
0:38:25,2 --> 0:38:27,42
Like anyway, let's not go too deep.
733
0:38:27,44 --> 0:38:31,12
The question to ask, will we survive
n number of rows because
734
0:38:31,12 --> 0:38:32,46
we expect all the data.
735
0:38:33,84 --> 0:38:34,52
I want-
736
0:38:34,54 --> 0:38:38,96
Michael: And just out of interest,
is it pretty much always doing
737
0:38:38,96 --> 0:38:41,7
range partitioning based on timestamp
type?
738
0:38:41,77 --> 0:38:42,52
Is that
739
0:38:42,52 --> 0:38:44,56
Nikolay: like- Again, I'm the wrong
person to ask.
740
0:38:44,56 --> 0:38:45,1
Michael: Okay, yeah, fair
741
0:38:45,1 --> 0:38:45,6
Nikolay: enough.
742
0:38:45,86 --> 0:38:49,74
In many cases, I just use to still
use TimescaleDB if it's time-based.
743
0:38:50,74 --> 0:38:51,54
Michael: Makes sense.
744
0:38:52,74 --> 0:38:55,16
Nikolay: And I'm happy because
there is compression there, it's
745
0:38:55,16 --> 0:38:55,66
great.
746
0:38:55,9 --> 0:39:0,08
But I also saw cases when it was
decided, not AI, but it was
747
0:39:0,08 --> 0:39:3,06
decided, like this partitioning
and full control over partitions,
748
0:39:3,42 --> 0:39:4,7
special table created.
749
0:39:4,9 --> 0:39:8,86
It was also working well and served
needs, but on surface, you
750
0:39:8,86 --> 0:39:13,02
should think about workload and
then push your AI or something
751
0:39:13,04 --> 0:39:17,06
to think about how it will behave
if you have that much data.
752
0:39:17,96 --> 0:39:19,86
Partitioning is inevitable there.
753
0:39:19,86 --> 0:39:21,14
And then experiments again.
754
0:39:21,28 --> 0:39:24,24
Even experiment with 1000000000
rows, it won't take a lot, just
755
0:39:24,24 --> 0:39:25,84
allocate machine and do it.
756
0:39:25,84 --> 0:39:28,78
It's like just so easy, right?
757
0:39:28,86 --> 0:39:33,04
Half an hour of wait to generate
it, or more like even if it's
758
0:39:33,04 --> 0:39:36,0
1 hour why I don't understand why
people don't do it all the
759
0:39:36,0 --> 0:39:40,58
time They come to they come to
us with questions, which I'm grateful.
760
0:39:40,58 --> 0:39:44,84
Thank you with money and so on
But it's so easy just to experiment
761
0:39:44,86 --> 0:39:46,2
more often these days
762
0:39:47,32 --> 0:39:50,0
Michael: Yeah, even when it even
leave it running overnight
763
0:39:51,0 --> 0:39:51,5
Nikolay: Yeah
764
0:39:53,98 --> 0:39:58,02
Michael: Yeah, I'm still I'm still
in the fence I love partitioning
765
0:39:58,04 --> 0:40:2,76
for the reasons you mentioned,
But I think so many projects won't
766
0:40:2,76 --> 0:40:6,96
ever need it that I understand
not doing it by default with a
767
0:40:6,96 --> 0:40:9,92
new project unless you know, unless
it's like a new feature for
768
0:40:9,92 --> 0:40:12,5
an existing system that you know
is going to get heavy amounts
769
0:40:12,5 --> 0:40:13,76
of data really quickly.
770
0:40:14,1 --> 0:40:17,22
Just there are so many trade-offs
like that it's still a little
771
0:40:17,22 --> 0:40:17,64
bit like...
772
0:40:17,64 --> 0:40:18,4
Nikolay: Which trade-offs?
773
0:40:20,18 --> 0:40:24,18
Michael: So for example indexes,
being able to create indexes
774
0:40:24,18 --> 0:40:26,9
concurrently, delete, drop indexes
concurrently.
775
0:40:27,98 --> 0:40:30,72
Nikolay: You can concurrently create
index on each partition
776
0:40:30,76 --> 0:40:34,04
and then yeah it exists on each
partition you can create this
777
0:40:34,04 --> 0:40:39,44
these things can be automated and
of course I like to be living
778
0:40:39,44 --> 0:40:42,94
much more better automated there
is a big potential here but
779
0:40:42,94 --> 0:40:47,78
also every Every Postgres version
gets a lot of improvements
780
0:40:47,86 --> 0:40:50,1
in this area, last maybe 10 years
already.
781
0:40:50,14 --> 0:40:53,94
So it's not like frozen.
782
0:40:54,66 --> 0:40:56,4
Yeah, but also make like,
783
0:40:57,34 --> 0:41:1,12
Michael: just being extra careful
that every high frequency query
784
0:41:1,12 --> 0:41:6,3
you have contains the partition
key and is getting pruned at
785
0:41:6,6 --> 0:41:9,5
planning time like there are a
few gotchas
786
0:41:9,64 --> 0:41:12,18
Nikolay: that hit you worse.
787
0:41:12,34 --> 0:41:12,5332
Michael: Yeah there are,
788
0:41:12,5332 --> 0:41:13,98
Nikolay: I have a series of articles
about this and I went quite
789
0:41:13,98 --> 0:41:15,92
deep there and it was crazy yeah.
790
0:41:16,42 --> 0:41:18,16
Michael: But that's my, they're
my hesitations.
791
0:41:19,2 --> 0:41:19,7
Nikolay: Creation
792
0:41:19,76 --> 0:41:20,72
Michael: of foreign keys.
793
0:41:22,42 --> 0:41:23,04
Nikolay: That's why I'm hesitant
for example working with timescale
794
0:41:23,04 --> 0:41:27,44
timescale DB something like often
we just Abandoned idea of having
795
0:41:27,44 --> 0:41:31,72
foreign key because yeah, it's
important If you do partitioning
796
0:41:31,72 --> 0:41:35,8
yourself, you will deal with, if
you have large tables, adding
797
0:41:35,8 --> 0:41:37,16
foreign key between them.
798
0:41:37,96 --> 0:41:41,06
If partitioning is involved, it's
an art, I would say.
799
0:41:41,14 --> 0:41:42,52
And GitLab mastered it.
800
0:41:42,52 --> 0:41:45,08
They have Migration Helpers RB.
801
0:41:45,48 --> 0:41:49,94
This is a great source of experience
of many people involved
802
0:41:49,94 --> 0:41:53,44
and I can't stop recommending how
great it is because it's open
803
0:41:53,44 --> 0:41:53,94
source.
804
0:41:54,38 --> 0:41:57,62
It's a great place to look at and
also they have great documentation
805
0:41:57,88 --> 0:41:58,58
about it.
806
0:41:58,58 --> 0:42:1,56
But anyway, it's a thing to decide.
807
0:42:1,56 --> 0:42:5,1
If you don't expect billions of
rows, don't do it, of course.
808
0:42:6,66 --> 0:42:11,26
At the same time, we have also
very sad cases when people come
809
0:42:11,26 --> 0:42:12,6
to us for consulting.
810
0:42:12,7 --> 0:42:15,6
We identify problems, we say partitioning
is needed.
811
0:42:16,22 --> 0:42:19,74
Sometimes we go and spend a lot
of effort because the project
812
0:42:19,74 --> 0:42:23,32
is huge or there are many databases
to take care of.
813
0:42:23,5 --> 0:42:26,5
And then finally it's implemented
great.
814
0:42:27,94 --> 0:42:31,26
There are many hidden dangers to
explore.
815
0:42:31,62 --> 0:42:36,04
Mid-journey, for example, they
came to us, helped, forgot about
816
0:42:36,04 --> 0:42:39,14
lightweight lock, well, not forgot,
we didn't know about that
817
0:42:39,14 --> 0:42:42,78
at that time, and they hit it,
lightweight LockManager problem
818
0:42:43,28 --> 0:42:47,72
was hit badly, and there are articles
about it.
819
0:42:48,1 --> 0:42:51,6
But then some projects just say,
okay, they evaluate how much
820
0:42:51,6 --> 0:42:54,78
effort it is to deal with partitioning
when tables are huge,
821
0:42:55,12 --> 0:42:58,06
and start talking, okay, maybe
we should migrate to my MongoDB.
822
0:42:59,72 --> 0:43:1,82
Michael: Yeah, Or even sharding,
right?
823
0:43:1,82 --> 0:43:6,82
Like instead, I think when we talked
to Notion, Arka, he mentioned
824
0:43:6,82 --> 0:43:10,44
that because they keep their shards
relatively small, they've
825
0:43:10,44 --> 0:43:12,68
opted to never partition at all.
826
0:43:12,74 --> 0:43:14,48
Nikolay: Yeah, the need for partitioning
diminishes.
827
0:43:15,04 --> 0:43:17,86
I agree with this, but not vanishes
completely.
828
0:43:18,96 --> 0:43:19,78
Michael: What, maybe?
829
0:43:20,34 --> 0:43:20,64
Nikolay: Not
830
0:43:20,64 --> 0:43:21,14
Michael: maybe.
831
0:43:21,28 --> 0:43:25,22
You mentioned, it depends how small
you keep your shards, right?
832
0:43:26,12 --> 0:43:27,68
Nikolay: Yeah, yeah.
833
0:43:28,08 --> 0:43:30,52
Michael: Like Sugu, for example,
Multigres, he talked about
834
0:43:30,52 --> 0:43:33,3
having lots of smaller databases
being much easier.
835
0:43:33,4 --> 0:43:34,42
Nikolay: Yeah, yeah, yeah, yeah.
836
0:43:34,64 --> 0:43:35,56
Maybe you're right.
837
0:43:36,34 --> 0:43:36,5
Michael: Okay.
838
0:43:36,5 --> 0:43:37,4
What about RLS?
839
0:43:37,54 --> 0:43:38,72
You mentioned you wanted to touch
on
840
0:43:38,72 --> 0:43:39,22
Nikolay: that.
841
0:43:39,38 --> 0:43:43,68
Yeah, finally, RLS, if you, for
example, I just noticed it's
842
0:43:43,68 --> 0:43:47,44
not like Supabase is very heavily
promoting RLS.
843
0:43:47,44 --> 0:43:51,02
They have a lot of articles including
how to avoid problems.
844
0:43:51,22 --> 0:43:56,6
But if you just use AI, I noticed
it often adds RLS.
845
0:43:57,18 --> 0:43:57,9
Michael: Oh, really?
846
0:43:58,26 --> 0:43:59,16
Nikolay: Yeah, I just...
847
0:43:59,16 --> 0:43:59,66
Interesting.
848
0:44:0,06 --> 0:44:0,92
This is how it works.
849
0:44:0,92 --> 0:44:4,86
I just noticed I had a couple of
cases where suddenly it brought
850
0:44:4,9 --> 0:44:6,9
like, we are going to use RLS.
851
0:44:6,9 --> 0:44:8,74
It's multi-tenancy here, let's
do it.
852
0:44:8,74 --> 0:44:12,58
Okay, let's do it, but let's also
pause and think about performance
853
0:44:13,38 --> 0:44:19,24
and avoid problems like current_setting
inside a loop and you
854
0:44:19,54 --> 0:44:23,2
SELECT count of million rows suddenly
becomes super slow.
855
0:44:23,42 --> 0:44:28,4
So the question to ask AI, again,
benchmark is ideal here.
856
0:44:30,06 --> 0:44:34,06
With RLS, without RLS, what is
the overhead of having RLS in
857
0:44:34,06 --> 0:44:34,92
our particular case?
858
0:44:34,92 --> 0:44:36,18
Let's benchmark it.
859
0:44:36,22 --> 0:44:39,72
And if you see it's not okay, then
let's optimize it, because
860
0:44:39,72 --> 0:44:41,82
there are tricks how to optimize
it.
861
0:44:43,66 --> 0:44:48,16
So if I notice an AI-generated
schema with RLS, I would ask question
862
0:44:48,16 --> 0:44:50,14
like, what will be performance
impact?
863
0:44:50,98 --> 0:44:53,74
Let's benchmark it, and if it's
bad, let's optimize.
864
0:44:53,8 --> 0:44:54,38
That's it.
865
0:44:54,38 --> 0:44:54,88
Yeah.
866
0:44:55,84 --> 0:44:56,34
Great.
867
0:44:57,22 --> 0:44:57,72
Great.
868
0:44:58,44 --> 0:44:59,32
That's it, actually.
869
0:44:59,38 --> 0:44:59,64
I think
870
0:44:59,64 --> 0:45:1,2
Michael: that's a useful checklist,
actually.
871
0:45:1,2 --> 0:45:5,72
Nikolay: 5 or 6 things to ask your
AI when you design schema.
872
0:45:7,66 --> 0:45:9,3
Michael: Yeah or your colleague
even.
873
0:45:10,24 --> 0:45:13,08
Nikolay: Yeah also or your own
schema which you have already
874
0:45:13,08 --> 0:45:14,06
10 years, why not.
875
0:45:14,06 --> 0:45:16,96
Michael: Yeah exactly or even a
new table if you're doing a new
876
0:45:16,96 --> 0:45:19,96
feature it still makes sense that
all of these make sense.
877
0:45:20,58 --> 0:45:21,24
Nikolay: I agree.
878
0:45:21,42 --> 0:45:23,5
Cool, thank you for listening.
879
0:45:24,16 --> 0:45:25,9
Yeah, see you soon.
0:0:0,06 --> 0:0:2,06
Nikolay: Hello, hello, this is
PostgresFM.
2
0:0:2,12 --> 0:0:7,24
My name is Nik, PostgresAI, and
as usual with me, Michael, pgMustard.
3
0:0:7,5 --> 0:0:8,299999
Hi, Michael.
4
0:0:9,28 --> 0:0:10,12
Michael: Hi, Nik.
How are you doing?
5
0:0:11,24 --> 0:0:12,139999
Nikolay: So the topic...
6
0:0:12,7 --> 0:0:14,28
I'm doing great, how are
you?
7
0:0:14,599999 --> 0:0:15,86
Michael: I'm good, Thank you.
8
0:0:15,94 --> 0:0:16,86
What's the topic?
9
0:0:17,26 --> 0:0:18,119999
Nikolay: What's the topic?
10
0:0:18,119999 --> 0:0:21,66
The topic I chose, and everyone
already saw it on cover you created,
11
0:0:21,66 --> 0:0:22,06
right?
12
0:0:22,06 --> 0:0:29,86
So the topic is 5 or so, maybe
6 things you should check when
13
0:0:29,86 --> 0:0:32,98
you start new schema or you already
have some schema and your
14
0:0:32,98 --> 0:0:38,32
project is growing and you want
to be better prepared for bigger
15
0:0:38,32 --> 0:0:40,02
scale and performance wise.
16
0:0:40,6 --> 0:0:45,32
So what, especially if you use
AI to design and improve your
17
0:0:45,32 --> 0:0:48,48
schema and your project is growing.
18
0:0:48,76 --> 0:0:55,02
So what should you look at to check
your schema health?
19
0:0:55,38 --> 0:0:56,14
Michael: Makes sense.
20
0:0:56,32 --> 0:1:0,2
Just on the topic in general, I
do think there are some things
21
0:1:0,2 --> 0:1:3,58
that are better suited to asking
for help from AI and not?
22
0:1:3,68 --> 0:1:7,58
Do you think like schema design
is 1 of them or actually is it
23
0:1:7,58 --> 0:1:11,32
1 of those ones that maybe we should
still do it a little bit
24
0:1:11,32 --> 0:1:13,12
more ourselves at the moment?
25
0:1:13,66 --> 0:1:18,04
Nikolay: Well as you know I'm very
very very intensive user of
26
0:1:18,04 --> 0:1:18,54
AI.
27
0:1:18,86 --> 0:1:19,36
Yeah.
28
0:1:20,28 --> 0:1:23,4
Honestly, like I do everything
through AI right now.
29
0:1:24,44 --> 0:1:29,24
And I'm 100% schema design is a
topic where AI can help a lot.
30
0:1:29,24 --> 0:1:32,36
But it also matters which questions
you ask.
31
0:1:32,98 --> 0:1:37,06
If you just say, I want this and
don't pay attention, you will
32
0:1:37,06 --> 0:1:39,64
have low quality schema in many
cases still.
33
0:1:40,44 --> 0:1:44,34
That's why I said, let's have this
episode because it might be
34
0:1:44,34 --> 0:1:48,74
quite basic for many advanced users
of Postgres, But this checklist
35
0:1:48,84 --> 0:1:53,08
we are going to present today,
it can be good thing to keep in
36
0:1:53,08 --> 0:1:56,82
mind always when you design schema,
just to ask right questions
37
0:1:56,94 --> 0:1:57,66
and revisit.
38
0:1:58,26 --> 0:2:1,4
Also, I'm not sure how exactly
you use AI.
39
0:2:1,4 --> 0:2:6,16
I use it always like I work with
people, developers.
40
0:2:7,4 --> 0:2:12,58
When you just ask something and
think those engineers, humans
41
0:2:12,58 --> 0:2:16,46
or AI, will solve all your problems
and make all the decisions,
42
0:2:17,1 --> 0:2:20,28
you're offloading full responsibility
to their shoulders, so
43
0:2:20,28 --> 0:2:24,36
then you have big negative surprises
always, right?
44
0:2:24,6 --> 0:2:29,64
So it's better to keep requests
as detailed as possible when
45
0:2:29,64 --> 0:2:34,48
you work with AI, and also make
some iterations to review and
46
0:2:34,48 --> 0:2:34,98
improve.
47
0:2:35,54 --> 0:2:38,92
And when making iterations, like
first, like I want some schema,
48
0:2:39,02 --> 0:2:44,12
like, I don't know, like LinkedIn
social media schema or something,
49
0:2:44,12 --> 0:2:44,62
right?
50
0:2:45,04 --> 0:2:48,98
It will give you immediately something,
but then you want some
51
0:2:49,22 --> 0:2:50,52
details to be checked.
52
0:2:50,54 --> 0:2:53,76
And this is exactly when this checklist can be very helpful.
53
0:2:54,02 --> 0:2:58,82
So you can revisit and think and imagine when you will have gigabytes
54
0:2:58,86 --> 0:3:1,58
and terabytes of data, what will happen.
55
0:3:1,86 --> 0:3:7,64
So to avoid painful refactoring in the future and outages maybe
56
0:3:7,64 --> 0:3:12,16
or performance degradation, the price you can pay in the beginning
57
0:3:12,16 --> 0:3:14,98
is much lower than the price you pay later.
58
0:3:16,96 --> 0:3:21,86
So I never work with AI in 1 shot, I always make iterations to
59
0:3:21,86 --> 0:3:25,08
improve in any question and to revisit and sometimes combine
60
0:3:25,08 --> 0:3:26,18
multiple LLMs.
61
0:3:28,26 --> 0:3:31,36
This is exactly when you can use this list of questions.
62
0:3:31,36 --> 0:3:35,58
So first topic is the data type of primary key.
63
0:3:36,68 --> 0:3:39,56
Michael: Yeah, and I feel like the answer to this has changed,
64
0:3:39,64 --> 0:3:43,9
or my opinion on what the best default for this has changed,
65
0:3:43,9 --> 0:3:49,2
actually, in the last few years, just off the back of learning
66
0:3:49,2 --> 0:3:53,4
more about timestamp ordered UUIDs, right?
67
0:3:53,56 --> 0:3:57,88
This used to be an age-old debate, right, UUID versus integer
68
0:3:57,88 --> 0:3:59,34
or big integer really.
69
0:4:0,06 --> 0:4:3,84
And most of the kind of blog posts I would see are like trying
70
0:4:3,84 --> 0:4:6,3
to just encourage people to use BIGINT over int.
71
0:4:7,06 --> 0:4:12,48
But now that we have first-class support for time-ordered UUIDs,
72
0:4:13,48 --> 0:4:15,92
I don't see many reasons not to go with that.
73
0:4:15,92 --> 0:4:16,8
What do you think?
74
0:4:16,8 --> 0:4:20,78
Nikolay: Well, the size, 8, is double.
75
0:4:21,04 --> 0:4:21,54
Yeah.
76
0:4:21,58 --> 0:4:24,94
Yeah, your ID is 16 bytes, so it's quite a big price to pay.
77
0:4:25,2 --> 0:4:29,44
Each row, you need to have 8 more bytes.
78
0:4:30,14 --> 0:4:31,015
It's not that much, is it?
79
0:4:31,015 --> 0:4:32,02
In many cases, it's fine.
80
0:4:32,2 --> 0:4:34,34
Well, indexes will be also bigger.
81
0:4:34,82 --> 0:4:36,3
Indexes double, yeah.
82
0:4:37,12 --> 0:4:39,98
It might make sense if volumes are huge.
83
0:4:40,12 --> 0:4:44,44
But anyway, to keep things simple, a rule to remember, red flags,
84
0:4:45,06 --> 0:4:49,5
If you are prepared for really large volumes of data, it must
85
0:4:49,5 --> 0:4:55,16
not be int4, it should be int8.
86
0:4:55,44 --> 0:5:2,24
And in terms of UUID path, data type is always UUID for both
87
0:5:2,24 --> 0:5:4,18
version 4 and version 7.
88
0:5:4,6 --> 0:5:8,7
But you can check default how it's generated.
89
0:5:8,94 --> 0:5:13,78
And in modern Postgres, there is a function, I think in 18, only
90
0:5:13,78 --> 0:5:14,6
in the latest version.
91
0:5:14,6 --> 0:5:15,02
I think
92
0:5:15,02 --> 0:5:15,26
Michael: so.
93
0:5:15,26 --> 0:5:17,44
It didn't quite make 17, did it?
94
0:5:17,44 --> 0:5:21,76
Nikolay: So if you're on Postgres 18, so it's better to use UUIDv7
95
0:5:21,76 --> 0:5:25,86
but also remembering trade-offs, because there is
96
0:5:25,86 --> 0:5:29,52
timestamp there, and the values will be ordered, like with integer
97
0:5:30,06 --> 0:5:31,08
or big integer.
98
0:5:31,4 --> 0:5:33,18
So there is a nuance here.
99
0:5:33,58 --> 0:5:37,12
But performance-wise it's much better than UUIDv4.
100
0:5:37,9 --> 0:5:41,54
There are many articles about it and you just need to check,
101
0:5:41,54 --> 0:5:44,36
okay, are we going to go with integer?
102
0:5:45,04 --> 0:5:48,7
Then it's int8 or BIGINT, right?
103
0:5:48,96 --> 0:5:50,64
Are we going to go with UUID?
104
0:5:51,06 --> 0:5:56,2
Then default generation, default
should be version 7 for Postgres
105
0:5:56,2 --> 0:5:56,58
18.
106
0:5:56,58 --> 0:5:59,92
For older version, it's also not
an excuse to stick with version
107
0:5:59,92 --> 0:6:0,28
4.
108
0:6:0,28 --> 0:6:2,0
You can write your own function.
109
0:6:2,9 --> 0:6:5,42
And you ask AI to write your own
function.
110
0:6:6,04 --> 0:6:8,1
Michael: Yes, or generate them
outside of the database.
111
0:6:8,3 --> 0:6:12,62
Nikolay: I slightly prefer this
option less because if you have
112
0:6:12,62 --> 0:6:17,08
multiple application nodes, Who
knows what will happen with their
113
0:6:17,08 --> 0:6:17,58
clocks?
114
0:6:18,58 --> 0:6:25,4
And since UUIDv7 includes
timestamp inside it, order might
115
0:6:25,4 --> 0:6:29,9
be not, I don't know, like I prefer
to leave it on database shoulders
116
0:6:29,9 --> 0:6:32,8
to have like on the primary single
source of true.
117
0:6:32,8 --> 0:6:37,4
On the other side, UUID by design
is generated, it serves purpose
118
0:6:37,44 --> 0:6:40,46
to work on multiple, like on distributed
systems, right?
119
0:6:40,58 --> 0:6:42,18
So maybe it's not a big deal.
120
0:6:42,18 --> 0:6:43,82
Generate on client side.
121
0:6:44,38 --> 0:6:47,68
Michael: All I mean is if you're
on an old Postgres and it saves
122
0:6:47,68 --> 0:6:49,78
you creating your own function
or whatever.
123
0:6:50,28 --> 0:6:55,36
Nikolay: Postgres waited a couple
of years to bring UUIDv7 officially.
124
0:6:56,0 --> 0:6:59,1
Although again, you can create
even SQL function, even not PL/pgSQL
125
0:6:59,1 --> 0:7:0,8
function, simple SQL function.
126
0:7:0,8 --> 0:7:4,64
And if you ask your AI to search
for example, probably it will
127
0:7:4,64 --> 0:7:8,8
find my page how to do it, but
or other people's pages.
128
0:7:9,44 --> 0:7:13,68
But in other libraries from Google,
from Node.js library, everything,
129
0:7:14,24 --> 0:7:17,74
they have it for a couple of years
already, so for longer.
130
0:7:18,12 --> 0:7:20,74
They implemented before standard
was finalized.
131
0:7:21,62 --> 0:7:24,92
Michael: Yeah, so huge red flags
to look out for if it's using
132
0:7:24,92 --> 0:7:28,64
the integer data type, which is
the same as int4, or serial.
133
0:7:29,02 --> 0:7:31,92
I think AI sometimes uses, it will
define it as serial.
134
0:7:31,92 --> 0:7:35,46
So if you just see that word without
bigserial, you might hit
135
0:7:35,46 --> 0:7:38,48
that limit at 2000000000 or whatever
the, I can't remember the
136
0:7:38,48 --> 0:7:39,52
exact upper limit.
137
0:7:39,56 --> 0:7:44,08
Nikolay: Yeah, and avoid UUID version
4 if you want good performance
138
0:7:44,1 --> 0:7:45,4
on large volumes of data.
139
0:7:45,4 --> 0:7:46,56
Yeah, that's it actually.
140
0:7:46,56 --> 0:7:50,04
Let's keep it simple because we
could discuss more advanced situations.
141
0:7:50,74 --> 0:7:54,44
Michael: I think we should do a
whole episode someday on migrations,
142
0:7:54,72 --> 0:7:57,34
like for people that are getting
close to that 2 billion
143
0:7:57,34 --> 0:7:58,26
Nikolay: number, that
144
0:7:58,26 --> 0:7:59,44
Michael: would be a good topic.
145
0:7:59,44 --> 0:8:2,12
Nikolay: I can share, I had a lot
of experience with different
146
0:8:2,12 --> 0:8:2,62
approaches.
147
0:8:3,66 --> 0:8:4,28
Let's do that.
148
0:8:4,28 --> 0:8:4,78
Good.
149
0:8:5,66 --> 0:8:6,98
So how to define primary key?
150
0:8:6,98 --> 0:8:10,46
I can actually have an article
about it, how to...
151
0:8:11,28 --> 0:8:11,78
Cool.
152
0:8:12,1 --> 0:8:12,74
Michael: Let's do that.
153
0:8:12,74 --> 0:8:15,58
Nikolay: Covering multiple paths so we could talk about this.
154
0:8:16,38 --> 0:8:18,28
Number 2 is constraints.
155
0:8:18,62 --> 0:8:19,7
Topic is constraints.
156
0:8:21,38 --> 0:8:22,32
Michael: Other constraints.
157
0:8:22,54 --> 0:8:22,94
Nikolay: Yeah, yeah.
158
0:8:22,94 --> 0:8:23,6
Yeah, other.
159
0:8:23,94 --> 0:8:25,32
Beyond primary key.
160
0:8:25,58 --> 0:8:25,9
Yeah.
161
0:8:25,9 --> 0:8:31,68
So I would just ask, first thing, are we using constraints well?
162
0:8:31,78 --> 0:8:33,84
Like to myself or AI and so on.
163
0:8:33,84 --> 0:8:38,26
And because Postgres has 6 types of constraints and it's quite
164
0:8:38,26 --> 0:8:45,56
rich tooling, and it's much more convenient to have them earlier,
165
0:8:46,08 --> 0:8:48,48
not because adding them later is a big pain.
166
0:8:48,48 --> 0:8:54,44
Right now we have quite good support of two-step constraint definitions.
167
0:8:55,4 --> 0:9:0,48
I think in Postgres 19 it will be possible also if not null constraints,
168
0:9:0,48 --> 0:9:1,36
which is interesting.
169
0:9:1,36 --> 0:9:5,14
But anyway, check constraints, foreign keys, unique constraints,
170
0:9:5,14 --> 0:9:10,02
it's possible to create first in almost finished state and then
171
0:9:10,02 --> 0:9:13,38
finalize it to avoid the long-lasting locks.
172
0:9:13,38 --> 0:9:17,96
But this is not the reason why I prefer to think about it in
173
0:9:17,96 --> 0:9:18,82
the very beginning.
174
0:9:18,96 --> 0:9:21,8
The main reason is the quality of data.
175
0:9:21,98 --> 0:9:24,82
This is why constraints exist in the first place, right?
176
0:9:25,44 --> 0:9:29,62
So if you introduce constraints early in your project, data quality
177
0:9:29,62 --> 0:9:30,3
is good.
178
0:9:31,84 --> 0:9:32,52
Introducing them
179
0:9:32,52 --> 0:9:33,32
Michael: later probably will...
180
0:9:33,32 --> 0:9:36,42
Yeah, and database side as well.
181
0:9:36,42 --> 0:9:37,4
I mean, it just...
182
0:9:37,64 --> 0:9:38,6
Nikolay: Oh yeah, yeah.
183
0:9:38,94 --> 0:9:40,78
I don't trust constraints outside.
184
0:9:43,26 --> 0:9:45,54
With age, I became less radical.
185
0:9:45,66 --> 0:9:49,18
My points of view became less radical with it.
186
0:9:49,18 --> 0:9:53,54
So I just observe without fighting, like with backend engineers
187
0:9:53,94 --> 0:9:57,74
who try to implement constraints on application side.
188
0:9:57,74 --> 0:10:1,16
I just have a note, like they are not reliable because who knows
189
0:10:1,44 --> 0:10:5,08
which other layers of types of applications, especially these
190
0:10:5,08 --> 0:10:9,72
days with AI, it's so easy to add something else, implement it
191
0:10:9,72 --> 0:10:12,56
in a different framework or even a language, it's super easy
192
0:10:12,56 --> 0:10:13,24
right now.
193
0:10:13,38 --> 0:10:16,5
And you will need to reimplement those constraints, or you will
194
0:10:16,5 --> 0:10:19,46
just forget and miss them and data quality will suffer.
195
0:10:21,04 --> 0:10:25,64
Michael: So specifically, something I think I'd like to see more
196
0:10:25,64 --> 0:10:30,26
of or at least I see a lot of people not doing is columns that
197
0:10:30,26 --> 0:10:32,8
you're not expecting to put any null values in, or you don't
198
0:10:32,8 --> 0:10:36,22
want to have any null values in, actually explicitly making them
199
0:10:36,22 --> 0:10:37,0
not null.
200
0:10:37,06 --> 0:10:40,96
Like, if you don't specify, then columns are nullable.
201
0:10:41,76 --> 0:10:45,84
And I think most people, or at
least most AIs I see, are creating,
202
0:10:46,1 --> 0:10:49,58
like by default, they don't put
NOT NULL on every column.
203
0:10:49,84 --> 0:10:52,7
Nikolay: Yeah, there's an opinion
that NOT NULL should be default.
204
0:10:53,2 --> 0:10:56,2
Anyway, it's hard to have a default,
but inside your project
205
0:10:56,2 --> 0:11:0,1
you can say, okay, unless it's
explicitly specified, let's make
206
0:11:0,1 --> 0:11:1,28
columns NOT NULL.
207
0:11:1,62 --> 0:11:2,12
Exactly.
208
0:11:3,16 --> 0:11:6,82
And avoid troubles with NULL we
talked about in a special episode
209
0:11:6,82 --> 0:11:8,08
about NULLs, right?
210
0:11:8,26 --> 0:11:12,72
Michael: Yeah, well and I guess
in this specific case there is
211
0:11:12,72 --> 0:11:17,58
also, well with some constraints,
mostly we care about, we're
212
0:11:17,58 --> 0:11:20,52
doing it for quality reasons, but
there is, there is like a tiny
213
0:11:20,52 --> 0:11:23,56
amount of performance optimization
that comes out of them.
214
0:11:23,56 --> 0:11:28,48
I guess a bit more for unique constraints,
like if you, if the
215
0:11:28,48 --> 0:11:31,32
database knows for sure this is
going to be, this is unique,
216
0:11:31,32 --> 0:11:32,58
it can do some optimizations.
217
0:11:33,32 --> 0:11:36,02
So yeah, anyway, but yeah, good
point about, I like that about
218
0:11:36,02 --> 0:11:36,52
NOT NULL.
219
0:11:37,84 --> 0:11:39,82
Nikolay: Yeah, yeah, unique is
interesting stuff.
220
0:11:39,96 --> 0:11:44,08
And also, I don't think foreign
keys are underused in Postgres
221
0:11:44,12 --> 0:11:44,62
ecosystem.
222
0:11:45,3 --> 0:11:49,34
Days when everyone suggested that
foreign keys are super bad,
223
0:11:49,54 --> 0:11:52,8
for me it's 20 years ago, I remember
these topics, let's avoid
224
0:11:52,8 --> 0:11:53,76
foreign keys completely.
225
0:11:54,52 --> 0:11:57,5
Although we know that foreign keys
can bite badly, we had an
226
0:11:57,5 --> 0:12:4,14
episode with Metronome company
about issues basically with MultiXact
227
0:12:4,76 --> 0:12:8,56
related issues which can be caused
when you use foreign keys
228
0:12:8,56 --> 0:12:9,06
a lot.
229
0:12:9,06 --> 0:12:12,68
But anyway, I still think foreign
keys is a good thing to have,
230
0:12:12,8 --> 0:12:13,54
data quality.
231
0:12:13,86 --> 0:12:18,3
But I think what is usually underused
is check constraints.
232
0:12:18,84 --> 0:12:19,7
They are great.
233
0:12:20,38 --> 0:12:22,72
They could be used much more often.
234
0:12:23,1 --> 0:12:25,9
And for example, a classic example,
when I say, like, when I
235
0:12:25,9 --> 0:12:31,08
see in schema ENUM data type,
I think, okay, later, If we want
236
0:12:31,08 --> 0:12:36,36
to change this list of possible
values, how will our migration?
237
0:12:37,36 --> 0:12:38,94
Michael: Especially removing any.
238
0:12:39,74 --> 0:12:40,24
Yeah,
239
0:12:40,52 --> 0:12:43,76
Nikolay: so check constraints are
slightly more flexible.
240
0:12:46,58 --> 0:12:49,44
Well, Also removing is a topic,
right?
241
0:12:49,44 --> 0:12:52,18
But check constraint you can introduce
in 2 stages.
242
0:12:52,92 --> 0:12:57,04
Again, you can drop the old 1 and
you can first create a new
243
0:12:57,04 --> 0:13:0,6
1 in NOT VALID state and then validate
it in a separate transaction.
244
0:13:1,56 --> 0:13:7,12
But I just see it like quite useful,
quite convenient tool for
245
0:13:7,12 --> 0:13:11,04
me and you see it in schema right
away like this this column
246
0:13:11,04 --> 0:13:15,36
should be like for example in this
range or it should be this
247
0:13:15,36 --> 0:13:16,36
list or something.
248
0:13:17,02 --> 0:13:20,7
So I wish it was used more often.
249
0:13:21,48 --> 0:13:21,98
Michael: Yeah.
250
0:13:22,82 --> 0:13:24,3
What do you see in...
251
0:13:24,32 --> 0:13:28,9
Going back to foreign keys quickly, do you see a lot of the LLMs
252
0:13:29,32 --> 0:13:33,8
doing a lot of things on update cascade, on delete set NULL,
253
0:13:33,8 --> 0:13:34,76
things like that.
254
0:13:34,76 --> 0:13:38,68
Do you see any cascade stuff popping in there by default or not
255
0:13:38,68 --> 0:13:39,97
when it should be or when you think it
256
0:13:39,97 --> 0:13:40,08
Nikolay: should be?
257
0:13:40,08 --> 0:13:41,06
It's a good question.
258
0:13:41,18 --> 0:13:44,94
I actually don't remember having issues with this question.
259
0:13:45,12 --> 0:13:51,26
I know deferred constraints can might be an issue.
260
0:13:51,46 --> 0:13:55,12
I remember a case when pg_repack, it was a company called Miro,
261
0:13:55,12 --> 0:13:56,42
maybe you know it, right?
262
0:13:56,72 --> 0:13:59,7
They had issues with pg_repack and deferred constraints.
263
0:13:59,7 --> 0:14:3,42
There is an article, old article from them about this.
264
0:14:4,12 --> 0:14:8,72
But I don't remember particular issues here, and I think this
265
0:14:8,72 --> 0:14:12,48
leads us actually to the question for our particular projects,
266
0:14:12,8 --> 0:14:13,64
what's better?
267
0:14:14,68 --> 0:14:19,14
And If I had any doubts with AI, I would just make some experiments.
268
0:14:19,9 --> 0:14:24,96
Based on the product we are developing, I would say, okay, for
269
0:14:24,96 --> 0:14:30,58
example, in 1 year, how many of entities in which entity table
270
0:14:30,58 --> 0:14:31,4
we expect?
271
0:14:32,32 --> 0:14:37,46
All other tables, let's fill it with fake data, run vacuum Analyze
272
0:14:37,58 --> 0:14:41,76
to have good state, and then just explore plans with pgMustard,
273
0:14:41,98 --> 0:14:42,68
for example.
274
0:14:44,16 --> 0:14:47,96
Let's think what kind of workload we might have according to
275
0:14:47,96 --> 0:14:51,4
user stories in our specification of the product and so on.
276
0:14:51,58 --> 0:14:54,3
And then collect plans and see, right?
277
0:14:54,52 --> 0:14:59,44
And then include also modifying queries and collect those plans
278
0:14:59,44 --> 0:15:0,92
and think how it will work.
279
0:15:1,02 --> 0:15:5,74
It's so easy these days to make these experiments before we finalize
280
0:15:5,74 --> 0:15:9,38
all the decisions and then you will see it in action and it's
281
0:15:9,38 --> 0:15:9,88
great.
282
0:15:10,26 --> 0:15:15,22
So collect all the plans, find like weak spots where we have
283
0:15:15,38 --> 0:15:18,84
suboptimal plans and how we can deal with this.
284
0:15:18,84 --> 0:15:23,94
If, for example, we have entity and another, and 1 too many,
285
0:15:24,14 --> 0:15:26,14
and it's many, so many, right?
286
0:15:26,18 --> 0:15:30,14
And then we want Delete to be supported in UI, where people,
287
0:15:30,14 --> 0:15:33,58
as we know, expect 200 milliseconds, less than 1 second, let's
288
0:15:33,58 --> 0:15:34,26
say, right?
289
0:15:34,3 --> 0:15:38,48
And when this Delete of 1 row triggers propagating according
290
0:15:38,48 --> 0:15:43,62
to foreign key triggers, deletion of 1000000 rows, it cannot
291
0:15:43,62 --> 0:15:45,08
meet our requirements, right?
292
0:15:45,08 --> 0:15:49,64
Now we need to think about asynchronous deletion, maybe using
293
0:15:49,64 --> 0:15:52,66
some event queue system or something, right?
294
0:15:52,8 --> 0:15:53,3
Yeah.
295
0:15:53,6 --> 0:15:57,78
And this will pop up if you prepare a good experiment here, right?
296
0:15:57,78 --> 0:15:59,8
Without experiment, it's hard to say.
297
0:15:59,9 --> 0:16:1,3
You need to answer questions.
298
0:16:2,08 --> 0:16:5,64
How big are the tables and how this relationship between tables
299
0:16:5,64 --> 0:16:8,98
on foreign keys will be in the worst case, for example.
300
0:16:8,98 --> 0:16:13,52
Can user create like million of entities and then we need to
301
0:16:13,52 --> 0:16:14,96
delete this user, for example.
302
0:16:14,96 --> 0:16:18,16
This is a great question and with AI you can iterate so fast
303
0:16:18,16 --> 0:16:18,9
these days.
304
0:16:18,9 --> 0:16:19,62
So great.
305
0:16:20,28 --> 0:16:22,16
Collect plans and then think and decide.
306
0:16:23,5 --> 0:16:28,28
Okay, it's a good question, but I don't see it as a simple question.
307
0:16:28,78 --> 0:16:29,38
Michael: No, no, no.
308
0:16:29,38 --> 0:16:32,68
It was more, I just asked in case you'd seen there was a chance
309
0:16:32,68 --> 0:16:37,54
that LLMs were throwing it in on every foreign key and That was
310
0:16:37,54 --> 0:16:40,64
maybe a bad like bad deal Oh, but it sounds like they're not
311
0:16:40,64 --> 0:16:43,26
Nikolay: maybe a wrong person to ask about this because I don't
312
0:16:43,26 --> 0:16:46,24
just I design sometimes these days I have a few applications
313
0:16:46,36 --> 0:16:49,9
designed Yeah created from scratch with AI but I mostly deal
314
0:16:49,9 --> 0:16:53,26
with things which other people already created, and I just see
315
0:16:53,26 --> 0:16:54,9
how AI can help.
316
0:16:55,32 --> 0:17:0,12
So I'm not sure will this propagation of deletion, for example,
317
0:17:0,12 --> 0:17:6,5
will be supported in how exactly it will be implemented.
318
0:17:6,5 --> 0:17:10,02
But it's definitely worth thinking in the context of latencies
319
0:17:10,44 --> 0:17:13,44
and what should be offloaded and what tools we'll be using to
320
0:17:13,44 --> 0:17:15,78
offload in this, to background jobs, for example.
321
0:17:18,48 --> 0:17:24,72
It's just a thing that you need to, instead of thinking, looking
322
0:17:24,72 --> 0:17:30,08
at code, I would think looking at dynamic results, experiments.
323
0:17:31,64 --> 0:17:35,04
Michael: Yeah, and I'm thinking, like if we're talking specifically
324
0:17:35,4 --> 0:17:40,44
about schema design when you expect to hit scale, I might default
325
0:17:40,64 --> 0:17:47,56
to not using ON X CASCADE just by default because of those, because
326
0:17:47,56 --> 0:17:51,14
at scale it can become problematic so that would probably be,
327
0:17:51,34 --> 0:17:54,62
if you had to come up with a best practice for if you're designing
328
0:17:54,62 --> 0:17:56,58
at scale, maybe lean in that direction.
329
0:17:56,58 --> 0:18:0,22
Nikolay: That's interesting, that's interesting because This
330
0:18:0,22 --> 0:18:5,22
was my position for a long time, but it's not about AI building
331
0:18:5,22 --> 0:18:5,9
or something.
332
0:18:5,94 --> 0:18:6,92
This was my position.
333
0:18:7,04 --> 0:18:10,52
Let's, like, it's dangerous because who knows how many rows we
334
0:18:10,52 --> 0:18:11,46
will need to delete.
335
0:18:11,46 --> 0:18:13,98
Like, it's like it's unbounded delete, right?
336
0:18:14,06 --> 0:18:16,26
Unlimited delete, This is how it feels.
337
0:18:17,4 --> 0:18:18,8
Let's design without it.
338
0:18:18,8 --> 0:18:24,86
But then I see in my consulting practice, I see cases where really
339
0:18:24,86 --> 0:18:28,02
large projects have it and it's just not 1 project.
340
0:18:28,08 --> 0:18:31,96
Many projects I see and they keep it and somehow live with it.
341
0:18:31,96 --> 0:18:35,14
They at some point implement non-cascaded, like asynchronous
342
0:18:35,22 --> 0:18:39,22
deletion, but by default they still rely on cascaded deletes
343
0:18:39,22 --> 0:18:40,74
somehow and they are fine.
344
0:18:40,9 --> 0:18:41,68
This is interesting.
345
0:18:41,76 --> 0:18:44,28
So reality shows it's not that bad.
346
0:18:44,44 --> 0:18:48,34
But I agree with your way of thinking and this is my default
347
0:18:48,34 --> 0:18:49,66
way of thinking as well.
348
0:18:51,04 --> 0:18:55,6
Postgres can survive very terrible things these days much better
349
0:18:55,6 --> 0:18:57,1
than 20 years ago.
350
0:18:57,78 --> 0:18:58,78
Including the revolution
351
0:18:59,7 --> 0:19:0,06
of 100 years...
352
0:19:0,06 --> 0:19:0,48
Michael: The only
353
0:19:0,48 --> 0:19:4,44
time I see that being
a horrendous issue, even in small
354
0:19:4,44 --> 0:19:8,04
to medium-sized projects, is if
people haven't indexed the
355
0:19:8,48 --> 0:19:9,76
Nikolay: second part of foreign
key.
356
0:19:9,76 --> 0:19:11,3
Michael: Exactly, the referencing
column.
357
0:19:11,38 --> 0:19:12,88
Nikolay: There are 2 ends of foreign
key.
358
0:19:12,88 --> 0:19:18,42
1 is always indexed because it's
primary key, the other 1, it's
359
0:19:18,42 --> 0:19:20,04
your job to index it, right?
360
0:19:20,38 --> 0:19:21,48
To not forget.
361
0:19:22,06 --> 0:19:23,2
Yeah, yeah, yeah.
362
0:19:23,2 --> 0:19:25,52
We have now a checkup, old checkup,
actually.
363
0:19:25,52 --> 0:19:29,2
We have a special report just for
this case, yeah.
364
0:19:30,04 --> 0:19:35,06
Good, but it's all good questions,
but I feel we can spend a
365
0:19:35,06 --> 0:19:37,12
lot in the Constraints area.
366
0:19:37,12 --> 0:19:38,68
I think we had Constraints episodes.
367
0:19:38,68 --> 0:19:39,0
We did
368
0:19:39,0 --> 0:19:39,96
Michael: have an episode, yeah.
369
0:19:39,96 --> 0:19:41,74
Let's link that up and move on.
370
0:19:41,98 --> 0:19:42,86
Nikolay: Yeah, let's move on.
371
0:19:42,86 --> 0:19:47,18
Some entertaining topic, Column
Tetris, which is sometimes it
372
0:19:47,18 --> 0:19:51,3
might bring some quite interesting
and good savings.
373
0:19:52,12 --> 0:19:55,66
In many cases it won't, but I just
see people enjoy this topic,
374
0:19:55,94 --> 0:19:58,02
and I decided to include it.
375
0:19:58,02 --> 0:20:3,24
So the idea is, when you design
a new Schema, the price to pay
376
0:20:3,26 --> 0:20:5,16
to reorder Columns is very low.
377
0:20:5,16 --> 0:20:7,38
You can reorder it easily.
378
0:20:9,22 --> 0:20:13,48
The problem is that for Postgres,
physically, the Column order
379
0:20:13,48 --> 0:20:13,98
matters.
380
0:20:15,16 --> 0:20:19,92
Due to padding alignment, if int2, for example, is followed
381
0:20:19,92 --> 0:20:26,64
by int8, you will see a gap
of 6 bytes of zeros in every
382
0:20:26,64 --> 0:20:27,14
row.
383
0:20:27,34 --> 0:20:28,34
And this is bad.
384
0:20:28,58 --> 0:20:33,34
It's like a waste of storage and
also memory, which is not only
385
0:20:33,34 --> 0:20:36,54
disk, it will pollute with those
zeros, it will pollute memory.
386
0:20:36,58 --> 0:20:41,34
And sometimes you can be so unlucky,
I saw cases like 30% of
387
0:20:41,74 --> 0:20:43,48
each row is like zeros.
388
0:20:44,54 --> 0:20:48,96
It's, And Postgres doesn't have
automatic reordering of Columns.
389
0:20:48,96 --> 0:20:51,24
It could, actually, but it doesn't
have it.
390
0:20:51,9 --> 0:20:54,76
Michael: So the order you define
it in, let's say you do create
391
0:20:54,76 --> 0:20:58,78
table, the order you put those
Columns in, that's the order they
392
0:20:58,78 --> 0:20:59,76
end up on disk.
393
0:20:59,76 --> 0:21:3,14
And every new Column you add goes
at the end.
394
0:21:4,08 --> 0:21:5,28
That makes sense, right?
395
0:21:5,28 --> 0:21:8,68
But even if it could be slotted
in, let's say you add a new Boolean
396
0:21:8,68 --> 0:21:11,54
type, and it could, I don't actually
know if that would work,
397
0:21:11,54 --> 0:21:13,26
but it always goes on the end.
398
0:21:13,26 --> 0:21:14,044
Nikolay: And there's no way to
sum up.
399
0:21:14,044 --> 0:21:14,64
Boolean is 1 byte.
400
0:21:15,28 --> 0:21:17,64
Michael: Yeah, but I wonder if
that would work.
401
0:21:17,64 --> 0:21:19,58
Nikolay: In Boolean, 7 bits are
wasted already.
402
0:21:21,04 --> 0:21:22,96
If you zoom in like under my
403
0:21:22,96 --> 0:21:24,96
Michael: let's put brilliant brilliant
brilliant next to each
404
0:21:24,96 --> 0:21:25,66
other right
405
0:21:25,84 --> 0:21:28,18
Nikolay: no no no no no Boolean
406
0:21:28,18 --> 0:21:28,52
Michael: really
407
0:21:28,52 --> 0:21:29,74
Nikolay: is 1 bit.
408
0:21:30,16 --> 0:21:30,7
Michael: Oh bit.
409
0:21:30,7 --> 0:21:32,44
Yeah, good point good point, so
yeah,
410
0:21:32,64 --> 0:21:34,66
Nikolay: But it's stored as a 1
byte.
411
0:21:35,2 --> 0:21:37,36
7 bits already wasted.
412
0:21:37,8 --> 0:21:43,48
But if you have Boolean and then
timestamp, for example.
413
0:21:44,34 --> 0:21:48,58
For example, first column, as we
all agree, should be id, the
414
0:21:48,58 --> 0:21:49,56
primary key.
415
0:21:49,64 --> 0:21:52,56
I'm joking, it's not always, but
it's very popular.
416
0:21:52,9 --> 0:21:57,16
Id is the first column, int8, occupies the whole 8-byte
417
0:21:57,18 --> 0:21:57,68
word.
418
0:21:58,46 --> 0:22:0,38
And then we have Boolean and then
timestamp.
419
0:22:1,1 --> 0:22:5,98
Not only you wasted 7 bits inside
Boolean, it's already by design,
420
0:22:5,98 --> 0:22:9,12
like Postgres doesn't have less
than 1 byte.
421
0:22:10,02 --> 0:22:15,6
But then you waste 7 bytes, so
7 bits first and 7 bytes because
422
0:22:15,6 --> 0:22:17,06
of this alignment padding.
423
0:22:17,54 --> 0:22:23,0
And your Boolean, which is like,
by sense, it's only 1 bit, suddenly
424
0:22:23,0 --> 0:22:24,06
occupies 8 bytes.
425
0:22:24,06 --> 0:22:27,38
It's a huge waste of resources,
right?
426
0:22:27,72 --> 0:22:33,18
But the bigger picture here is
that people usually realize that
427
0:22:33,18 --> 0:22:37,48
waste is significant only when
it's a huge table, like 100 million
428
0:22:37,48 --> 0:22:38,2
rows, right?
429
0:22:38,44 --> 0:22:42,7
And then, oh, so many spaces wasted
because we didn't play this
430
0:22:42,7 --> 0:22:43,94
column Tetris initially.
431
0:22:45,06 --> 0:22:49,62
But this is like chicken or egg
problem, maybe inverted.
432
0:22:49,84 --> 0:22:54,02
Anyway, so you realize it later,
but you could do it earlier,
433
0:22:54,1 --> 0:22:56,96
but you didn't think about it because
when you have only 1,000
434
0:22:56,96 --> 0:22:59,32
rows, for example, it costs you
nothing.
435
0:23:0,18 --> 0:23:2,46
But again, with AI, it's so easy.
436
0:23:2,9 --> 0:23:5,3
You just say, let's play Column
Tetris in Postgres.
437
0:23:5,84 --> 0:23:7,04
AI should know it.
438
0:23:7,04 --> 0:23:9,88
I haven't tried, but I'm pretty
sure it should know it, because
439
0:23:9,88 --> 0:23:11,84
there are many articles about it.
440
0:23:12,12 --> 0:23:13,54
Michael: Oh, and we did an episode.
441
0:23:14,32 --> 0:23:17,08
Nikolay: Yeah, starting from Stack
Overflow, where this topic
442
0:23:17,08 --> 0:23:18,9
was very well described.
443
0:23:19,4 --> 0:23:22,16
So AI should know it, this topic.
444
0:23:22,66 --> 0:23:25,84
When you create a new table, you
think many rows will be stored,
445
0:23:25,84 --> 0:23:28,22
just let's apply column Tetris
to this.
446
0:23:28,48 --> 0:23:28,98
Boom.
447
0:23:29,38 --> 0:23:30,74
It will reorder columns.
448
0:23:32,2 --> 0:23:32,88
Never try it again.
449
0:23:32,88 --> 0:23:36,1
Michael: Yeah, what do you think,
like I feel like there's a...
450
0:23:37,58 --> 0:23:40,52
I don't know if I'd still want
to have, I'd still want to do
451
0:23:40,52 --> 0:23:46,32
it myself just because I feel like
there's also a natural order
452
0:23:46,32 --> 0:23:46,92
for columns.
453
0:23:46,92 --> 0:23:51,68
So I would want to put all the
8-byte ones together and 16-byte
454
0:23:51,76 --> 0:23:55,44
ones in to make sure they're all
with similar and then all of
455
0:23:55,44 --> 0:23:55,94
the...
456
0:23:56,54 --> 0:23:57,16
Yeah, exactly.
457
0:23:57,16 --> 0:23:58,04
Nikolay: Ah, similar.
458
0:23:59,06 --> 0:24:2,82
Michael: But equally, like, imagine
if it's like a users table
459
0:24:3,2 --> 0:24:8,72
and I could put the I could put
certain information about that
460
0:24:8,72 --> 0:24:12,08
user that makes sense to go together
together and then information
461
0:24:12,22 --> 0:24:15,92
about something like maybe like
there all the IDs that maybe
462
0:24:15,92 --> 0:24:19,24
the foreign keys like their team
ID or their organization, all
463
0:24:19,24 --> 0:24:22,58
those kind of like referencing
columns, put those together.
464
0:24:22,58 --> 0:24:25,78
So I know the order doesn't matter.
465
0:24:25,84 --> 0:24:28,86
I know you could just specify column
order, but you know when
466
0:24:28,86 --> 0:24:30,72
you do SELECT * or you just...
467
0:24:30,72 --> 0:24:34,34
Nikolay: This is what I was trying
to say, like, are you trying
468
0:24:34,34 --> 0:24:37,06
to tell me that you're using SELECT *?
469
0:24:38,04 --> 0:24:39,1
And here we go.
470
0:24:39,34 --> 0:24:41,28
So I'm joking, actually.
471
0:24:41,28 --> 0:24:43,16
I'm using SELECT * all the time.
472
0:24:44,34 --> 0:24:48,54
Also all the time I see articles
how bad it is to use SELECT *.
473
0:24:49,24 --> 0:24:49,74
Right?
474
0:24:50,14 --> 0:24:56,5
It's convenient, I know, yeah,
but also if you manually work,
475
0:24:56,98 --> 0:25:1,78
it's inconvenient to have order
and SELECT * doesn't work well.
476
0:25:1,84 --> 0:25:4,2
But applications shouldn't use
SELECT *.
477
0:25:4,2 --> 0:25:4,98
Michael: You're right.
478
0:25:5,08 --> 0:25:8,48
And I'm even thinking about examples
like the system views in
479
0:25:8,48 --> 0:25:8,86
Postgres.
480
0:25:8,86 --> 0:25:11,92
Like you do SELECT * FROM pg_stat_statements or something.
481
0:25:12,62 --> 0:25:14,12
But that's not even the table,
right?
482
0:25:14,12 --> 0:25:14,92
That's the view.
483
0:25:14,92 --> 0:25:16,4
And then they can define the order.
484
0:25:16,4 --> 0:25:18,3
So it doesn't actually matter.
485
0:25:19,12 --> 0:25:20,52
Nikolay: Yeah, it's an interesting
topic.
486
0:25:21,04 --> 0:25:24,66
I played Column Tetris a few times
heavily, and then it was really
487
0:25:24,66 --> 0:25:25,16
inconvenient.
488
0:25:25,68 --> 0:25:27,22
Oh, this is an ugly table.
489
0:25:27,6 --> 0:25:28,54
It looks ugly.
490
0:25:28,78 --> 0:25:30,4
Like, this order is ugly.
491
0:25:30,4 --> 0:25:33,34
But at the same time, it's your
problem that because you use
492
0:25:33,34 --> 0:25:36,1
SELECT *, which is considered
like not good practice.
493
0:25:36,28 --> 0:25:38,58
It's practice for exploring and
so on.
494
0:25:39,14 --> 0:25:42,8
Okay, explore with AI, ask to provide
better order or something.
495
0:25:42,8 --> 0:25:43,64
I don't know.
496
0:25:44,24 --> 0:25:47,84
But anyway, if you talk about,
if you focus on performance, again
497
0:25:47,84 --> 0:25:51,24
I saw cases where it was significant
waste.
498
0:25:51,42 --> 0:25:53,86
Michael: 1 of the first articles
I saw and it was by Braintree
499
0:25:53,86 --> 0:25:57,34
and I think they reported an average
of about 10% on some of
500
0:25:57,34 --> 0:25:58,44
their largest tables.
501
0:25:58,44 --> 0:25:59,0
Nikolay: It depends.
502
0:25:59,76 --> 0:26:2,48
Michael: Obviously it completely
depends but that was quite surprising
503
0:26:2,48 --> 0:26:2,68
to me.
504
0:26:2,68 --> 0:26:5,5
I know you mentioned an extreme
case where you saw 30%, but I
505
0:26:5,5 --> 0:26:9,96
was quite surprised that in an
actual production case, it was
506
0:26:9,96 --> 0:26:11,0
as high as 10%.
507
0:26:11,0 --> 0:26:13,68
And when you're talking about hundreds
of gigabytes or terabytes,
508
0:26:15,06 --> 0:26:16,36
That's actually a decent amount.
509
0:26:17,32 --> 0:26:18,3
Nikolay: Yeah, yeah, exactly.
510
0:26:18,52 --> 0:26:21,88
The problem also, of course, in
existing projects which were
511
0:26:21,88 --> 0:26:25,96
developed during many years, usually
it's not so simple because
512
0:26:25,96 --> 0:26:30,32
the schema is evolving during many
years and you cannot insert
513
0:26:30,32 --> 0:26:31,94
your new column in the middle.
514
0:26:32,04 --> 0:26:34,22
It's always, as you said, at the
end.
515
0:26:34,44 --> 0:26:38,9
So sometimes we do need to, for
example, to change the order
516
0:26:39,24 --> 0:26:42,08
using some redesign thing.
517
0:26:42,14 --> 0:26:48,78
By the way, have you heard that
Postgres 19 will have repack
518
0:26:48,84 --> 0:26:52,58
and also repack concurrently, which
is basically vacuum concurrently.
519
0:26:53,14 --> 0:26:55,4
We had an episode with author of
pg_squeeze.
520
0:26:56,2 --> 0:26:58,02
It's a vacuum full concurrently.
521
0:26:58,78 --> 0:26:59,76
Exactly, exactly.
522
0:26:59,76 --> 0:27:1,02
Not vacuum concurrently.
523
0:27:1,02 --> 0:27:4,78
But the decision was made to name
it Repack concurrently.
524
0:27:5,02 --> 0:27:7,78
Michael: I think that's sensible,
getting away from people thinking
525
0:27:7,78 --> 0:27:9,4
it's vacuum related, yeah.
526
0:27:9,86 --> 0:27:13,94
Nikolay: I haven't looked into
details, but naturally this is
527
0:27:13,94 --> 0:27:17,64
the point where you could reorder
columns, because it's based
528
0:27:17,64 --> 0:27:18,76
on logical replication.
529
0:27:19,74 --> 0:27:23,16
Michael: Yeah, I suspect if they
do that, it will be a different
530
0:27:23,16 --> 0:27:24,1
keyword again.
531
0:27:24,72 --> 0:27:25,46
Nikolay: Who knows?
532
0:27:25,6 --> 0:27:27,38
Again, I haven't looked into details.
533
0:27:27,44 --> 0:27:30,84
There are long discussions on pgsql-hackers
mailing list.
534
0:27:31,04 --> 0:27:34,54
It just feels naturally, because
it's based on logical replication.
535
0:27:34,54 --> 0:27:37,86
This is where you can reorder columns
and play Tetris for existing
536
0:27:37,86 --> 0:27:39,14
large tables.
537
0:27:39,62 --> 0:27:41,78
I hope it will be implemented at
some point.
538
0:27:41,78 --> 0:27:43,94
Maybe it's already partially implemented
or something.
539
0:27:43,94 --> 0:27:44,74
I don't know.
540
0:27:45,16 --> 0:27:49,54
You basically, before you start
filling the new table, you can
541
0:27:49,54 --> 0:27:51,52
reorder it, because it's logical,
right?
542
0:27:52,08 --> 0:27:53,22
It should be possible.
543
0:27:53,94 --> 0:27:56,4
Anyway, this is content, it's an
entertaining topic.
544
0:27:56,4 --> 0:28:0,68
Let's move on to a more serious
topic called indexes, index chaos.
545
0:28:1,34 --> 0:28:6,04
If you just use AI and if you don't
spend enough time for things
546
0:28:6,04 --> 0:28:11,3
as I said like experimenting with
real data benchmarks you might
547
0:28:11,38 --> 0:28:15,08
end up with under-indexed situation
or over-indexed situation
548
0:28:16,06 --> 0:28:18,98
And usually people are scared to
have under-indexed.
549
0:28:19,46 --> 0:28:22,16
In my practice, I see over-indexed
very often.
550
0:28:22,54 --> 0:28:23,74
Sometimes to extreme.
551
0:28:25,3 --> 0:28:28,3
Michael: I guess for new projects,
I would be surprised to see
552
0:28:28,3 --> 0:28:29,16
them over-indexed.
553
0:28:30,08 --> 0:28:30,76
But for- it's
554
0:28:30,76 --> 0:28:32,58
Nikolay: possible, it's possible
still.
555
0:28:32,78 --> 0:28:36,1
Michael: Is that like as a result
of AI or something else?
556
0:28:36,1 --> 0:28:39,62
Nikolay: Well, the problem here
is that, again, everything depends
557
0:28:39,62 --> 0:28:43,38
on your prompts, depends on angles.
558
0:28:43,68 --> 0:28:50,24
If you say we will need to order
by arbitrary column, right?
559
0:28:50,34 --> 0:28:53,92
Remember our episode with CTO Gadget,
Harry, right?
560
0:28:54,38 --> 0:28:56,42
So any column we can reorder.
561
0:28:57,44 --> 0:29:2,06
Naturally, AI might decide to put
index on every column, right?
562
0:29:2,86 --> 0:29:3,84
But is it really
563
0:29:3,84 --> 0:29:4,54
Michael: what you want?
564
0:29:4,54 --> 0:29:7,72
But that's what Gadget decided
to do as well, right?
565
0:29:7,8 --> 0:29:11,66
Nikolay: Right, but with understanding
consequences, right?
566
0:29:11,98 --> 0:29:17,68
But in reality, will it be used
If 1 year later you see that
567
0:29:17,68 --> 0:29:21,58
90% of those indexes are unused
and the gadget has special case
568
0:29:21,58 --> 0:29:25,84
They have many applications to
support if you design your own
569
0:29:25,84 --> 0:29:29,96
only 1 application This is where
I would just stop and think
570
0:29:29,96 --> 0:29:34,3
with AI again with some experiments
I would ask questions like
571
0:29:34,3 --> 0:29:36,46
this all depends on workload, right?
572
0:29:36,46 --> 0:29:39,66
Which queries we will have, right?
573
0:29:40,08 --> 0:29:44,34
And I would just think, which queries
we really need and which
574
0:29:44,34 --> 0:29:49,5
we won't need, and then choose
a proper minimal set of indexes.
575
0:29:51,6 --> 0:29:52,52
Michael: I like that.
576
0:29:52,64 --> 0:29:56,8
I think there's also like some
rules of thumb we can give people
577
0:29:56,8 --> 0:30:0,32
in terms of like how many is where
it starts to get dangerous
578
0:30:0,32 --> 0:30:1,2
for other reasons.
579
0:30:1,88 --> 0:30:6,72
But the kind of 1 place where it's
not even and it depends is
580
0:30:6,96 --> 0:30:11,4
overlapping and I see more and
more where people are adding indexes
581
0:30:11,58 --> 0:30:13,24
based on AI suggestions.
582
0:30:13,78 --> 0:30:16,72
They're adding indexes they've
effectively already got or they're
583
0:30:16,72 --> 0:30:20,8
adding ones that overlap with indexes
they've already got, and
584
0:30:20,8 --> 0:30:23,34
therefore only 1 of them is needed.
585
0:30:24,14 --> 0:30:26,52
And by the way, it won't always
show up in your stats, it won't
586
0:30:26,52 --> 0:30:29,8
always show up as an unused index,
because it is still using
587
0:30:29,8 --> 0:30:30,92
the smaller index.
588
0:30:31,56 --> 0:30:35,98
Nikolay: It will show up in PostgreSQL
stats because it's called
589
0:30:35,98 --> 0:30:39,52
in our case redundant index in
Postgres monitoring systems and
590
0:30:39,52 --> 0:30:40,02
CheckUp.
591
0:30:40,46 --> 0:30:42,42
I never heard the term overlapping.
592
0:30:43,38 --> 0:30:46,64
I see people talk about duplicate
indexes, which is like trivial
593
0:30:46,64 --> 0:30:48,9
case, exact same definition.
594
0:30:50,28 --> 0:30:52,2
But we call it redundant indexes.
595
0:30:52,2 --> 0:30:57,18
It's not an easy topic as 1 might
think, but we solved it in
596
0:30:57,18 --> 0:30:57,8
many parts.
597
0:30:57,8 --> 0:31:3,22
Over years, we have quite a good
detection approach, how to reliably
598
0:31:3,58 --> 0:31:5,14
detect redundant indexes.
599
0:31:5,86 --> 0:31:10,64
And as you say, this is true, in
many cases all of those indexes
600
0:31:10,64 --> 0:31:11,36
are used.
601
0:31:11,82 --> 0:31:16,56
And it's quite like, there is a
big fear, oh, I'm going to drop
602
0:31:16,56 --> 0:31:18,98
this, it's used, should I drop
it?
603
0:31:19,28 --> 0:31:23,82
Yeah, but the reports we have quite
reliable and of course I
604
0:31:23,82 --> 0:31:27,84
wish we had very native standard
algorithm to disable indexes
605
0:31:27,84 --> 0:31:32,32
for quite for some time like without
involving HypoPG and hypothetical
606
0:31:32,5 --> 0:31:35,46
indexes, which currently supports
it.
607
0:31:35,82 --> 0:31:38,14
Michael: Our last episode was on
what's missing in Postgres,
608
0:31:38,14 --> 0:31:41,5
and I think we got a nice comment,
I've forgotten who from, but
609
0:31:41,52 --> 0:31:45,26
saying 1 of their features would
be make this
610
0:31:45,26 --> 0:31:45,32
Nikolay: invisible.
611
0:31:45,32 --> 0:31:48,26
Temporally hide index from the
planner.
612
0:31:48,48 --> 0:31:52,54
There are extensions for it, HypoPG
supports it, but we want
613
0:31:52,54 --> 0:31:58,04
some mechanism beyond index setting,
and index is valid to false.
614
0:31:58,38 --> 0:31:59,14
We want this.
615
0:31:59,14 --> 0:32:0,66
Yeah, yeah, yeah, I agree.
616
0:32:0,96 --> 0:32:5,14
And this is a good thing to probably
work on for those who want
617
0:32:5,24 --> 0:32:9,8
to start hacking Postgres so it's
a good thing to officially implement
618
0:32:9,8 --> 0:32:10,3
something.
619
0:32:10,68 --> 0:32:15,94
Anyway, questions to ask is, they
all start here, should start
620
0:32:15,94 --> 0:32:17,36
from understanding workload.
621
0:32:17,86 --> 0:32:22,18
So we cannot think about indexes
without understanding workload.
622
0:32:22,28 --> 0:32:26,14
So first we need to generate some
fake data and then start thinking
623
0:32:26,38 --> 0:32:28,04
about usage patterns.
624
0:32:28,7 --> 0:32:30,92
And without AI It's really hard
work.
625
0:32:30,92 --> 0:32:32,06
It's a lot of time.
626
0:32:32,72 --> 0:32:34,8
With AI it becomes easier.
627
0:32:37,0 --> 0:32:38,7
If you have user stories defined
already, you understand what
628
0:32:38,7 --> 0:32:42,38
kind of patterns we will have,
and you can start imagining.
629
0:32:43,38 --> 0:32:47,78
It will not be 100% accurate, but
it will be good enough, much
630
0:32:47,78 --> 0:32:51,82
better than without it or with
manual work.
631
0:32:51,82 --> 0:32:56,4
So you can iterate here and see,
and then with queries in hand
632
0:32:56,84 --> 0:33:2,04
and some like lab already developed,
I see it should take 1 or
633
0:33:2,04 --> 0:33:7,08
2 hours for like medium-sized project
I mean in terms of complexity
634
0:33:7,42 --> 0:33:10,58
and then you can start iterating
collecting plans and think which
635
0:33:10,58 --> 0:33:12,32
indexes you can have playing
636
0:33:13,02 --> 0:33:15,58
Michael: But also this whilst this
might be the most important
637
0:33:15,58 --> 0:33:19,2
in terms of performance that we've
talked about so far it's also
638
0:33:19,2 --> 0:33:24,52
the the easiest to change later
right we can yeah yeah we can
639
0:33:24,52 --> 0:33:27,88
set up a certain set of indexes
and later decide we want a slightly
640
0:33:27,88 --> 0:33:30,92
different set of indexes and that's
not super painful in a lot
641
0:33:30,92 --> 0:33:34,54
in most cases especially if there's
no partitioning involved
642
0:33:35,28 --> 0:33:40,2
so that for me feels like yeah
it's important it's really important
643
0:33:40,58 --> 0:33:44,34
But if we don't get it right on
day 1, it's much, much easier
644
0:33:44,34 --> 0:33:47,82
to fix than the primary key being
the wrong data type or not
645
0:33:47,82 --> 0:33:50,34
having the right constraints in
place, that kind of thing.
646
0:33:50,72 --> 0:33:52,28
Or even the column order, yeah.
647
0:33:52,72 --> 0:33:56,82
Nikolay: So the point is you should
build like wind tunnel for
648
0:33:56,82 --> 0:33:58,04
your project, right?
649
0:33:58,86 --> 0:34:1,58
Like, you know, like, Test lab,
yeah.
650
0:34:1,86 --> 0:34:2,98
Database lab.
651
0:34:3,42 --> 0:34:7,42
And then you should put this workload like wind and see like
652
0:34:7,42 --> 0:34:11,94
how your profile of your database behaves under this wind.
653
0:34:12,28 --> 0:34:14,84
Michael: Yeah, a lot of people do that with production.
654
0:34:15,14 --> 0:34:17,7
They just do it with their slow queries.
655
0:34:18,94 --> 0:34:22,84
Nikolay: But with AI it's easier to have massive experiments
656
0:34:23,0 --> 0:34:25,06
before you finalize all the decisions.
657
0:34:25,38 --> 0:34:27,28
So my point is do it.
658
0:34:27,28 --> 0:34:30,72
And then questions to ask, are there any indexes we don't need
659
0:34:30,72 --> 0:34:33,9
in terms of they are redundant or unused or what indexes are
660
0:34:33,9 --> 0:34:36,72
missing because we have bad plans and this again like pgMustard
661
0:34:37,2 --> 0:34:40,62
will visualize it and so on and you have API already, right?
662
0:34:40,88 --> 0:34:42,94
Michael: Yeah Yeah, it's quite nice
663
0:34:43,14 --> 0:34:46,3
Nikolay: You can connect your AI to pgMustard for example and
664
0:34:46,3 --> 0:34:49,44
ask to use pgMustard to explain what's happening and find bad
665
0:34:49,44 --> 0:34:53,5
problems and missing indexes with this lab environment.
666
0:34:54,64 --> 0:34:55,12
Great?
667
0:34:55,12 --> 0:34:55,78
Michael: Yeah, OK.
668
0:34:57,1 --> 0:35:1,72
Going just like to the extreme, How many indexes on a table would
669
0:35:1,72 --> 0:35:5,7
you automatically just think, oh wow, that's like a lot?
670
0:35:6,22 --> 0:35:10,54
Maybe it's justified, but I'm already thinking that's too many.
671
0:35:10,92 --> 0:35:13,34
Nikolay: These rule of thumbs are quite weak in my opinion, but
672
0:35:13,34 --> 0:35:14,44
we have them anyway.
673
0:35:14,54 --> 0:35:17,44
Like for example, if the volume of indexes exceeds the volume
674
0:35:17,44 --> 0:35:20,88
of data, it's already some bell is ringing.
675
0:35:23,22 --> 0:35:27,72
Or before Postgres 18 with this nasty lightweight lock manager
676
0:35:27,72 --> 0:35:31,22
problem means I don't want more than 15 indexes per table.
677
0:35:31,96 --> 0:35:34,62
Michael: Because of the 16 relation limit.
678
0:35:34,94 --> 0:35:38,76
Nikolay: So simple, primary key lookup which quite likely will
679
0:35:38,76 --> 0:35:45,22
be very, will have quite high QPS will suffer, and because of
680
0:35:45,22 --> 0:35:45,72
locking.
681
0:35:46,26 --> 0:35:49,7
So there are a couple of kind of these rules, right, but they
682
0:35:49,7 --> 0:35:54,64
are not strict and again I say that I consider them weak, but
683
0:35:54,72 --> 0:35:55,22
maybe...
684
0:35:55,44 --> 0:35:57,78
Michael: But I think it's still helpful, right, if you've got
685
0:35:57,78 --> 0:36:2,56
a schema designed by AI and it's got 20 indexes on 1 table, maybe
686
0:36:2,56 --> 0:36:4,18
consider is that smart?
687
0:36:6,8 --> 0:36:11,3
Nikolay: But it happens, index data exceeding heap data it happens.
688
0:36:12,88 --> 0:36:15,6
Michael: Especially with some, especially like if you're doing
689
0:36:15,6 --> 0:36:16,34
gin indexes.
690
0:36:16,64 --> 0:36:17,34
Nikolay: Oh yes.
691
0:36:17,4 --> 0:36:19,54
Michael: If it's just B-trees, yeah.
692
0:36:20,38 --> 0:36:22,46
Nikolay: Yeah, okay, great.
693
0:36:22,54 --> 0:36:25,88
So the final topic, we couldn't decide on which 1 to choose.
694
0:36:25,88 --> 0:36:27,6
We had 2 ideas.
695
0:36:27,8 --> 0:36:30,7
1 is RLS and another is partitioning.
696
0:36:32,4 --> 0:36:33,6
Let's touch both maybe.
697
0:36:33,6 --> 0:36:34,4
Michael: Yeah I think so.
698
0:36:34,4 --> 0:36:39,3
So from an AI perspective, like when you're asking it for a new
699
0:36:39,3 --> 0:36:43,3
schema, do you see partitioning come up?
700
0:36:43,38 --> 0:36:44,4
Does it over partition?
701
0:36:44,4 --> 0:36:48,64
Nikolay: It won't come up Unless
you start saying I'm going to
702
0:36:48,64 --> 0:36:54,72
have a lot of data here and I want
good performance, I want billions
703
0:36:54,72 --> 0:36:58,04
of rows stored in this table, it
should be maintainable.
704
0:36:58,26 --> 0:37:1,3
It will naturally come to the idea
of having partitioning.
705
0:37:3,26 --> 0:37:7,32
Partitioning will be interesting
to join with UUID version 7 or
706
0:37:7,36 --> 0:37:8,9
8, it doesn't matter.
707
0:37:8,92 --> 0:37:13,12
It's possible, again, I have a
recipe for this in my set of how-tos.
708
0:37:14,06 --> 0:37:18,46
And it can be also the question,
like partitioning you cannot
709
0:37:18,46 --> 0:37:20,96
develop without understanding workload
again.
710
0:37:21,26 --> 0:37:24,62
It's similar to indexes, like it
will be something, but how it
711
0:37:24,62 --> 0:37:25,84
will behave, who knows?
712
0:37:25,84 --> 0:37:29,36
Maybe you will end up, your queries
will need to scan all partitions,
713
0:37:29,38 --> 0:37:31,36
which is terrible in most cases.
714
0:37:32,1 --> 0:37:34,24
And then plan behavior.
715
0:37:34,46 --> 0:37:37,7
Anyway, the question is to ask,
like you said requirements, I
716
0:37:37,7 --> 0:37:41,94
want a lot of rows and I want well
maintainability of this.
717
0:37:43,08 --> 0:37:43,82
Help me.
718
0:37:43,94 --> 0:37:46,2
Michael: Like deleting old data,
are you thinking?
719
0:37:46,72 --> 0:37:49,04
Nikolay: No, creating indexes,
for example, or reindexing.
720
0:37:49,12 --> 0:37:50,02
Michael: OK, yeah, sure.
721
0:37:50,02 --> 0:37:50,52
Vacuum.
722
0:37:50,9 --> 0:37:51,9
Nikolay: Vacuum, yeah, yeah.
723
0:37:51,9 --> 0:37:56,72
These are problems biting us much
more often and badly than just,
724
0:37:56,72 --> 0:37:59,76
I don't know, like, we talk about
this a lot, as well, right?
725
0:37:59,76 --> 0:38:0,7
Michael: I know, I know.
726
0:38:0,7 --> 0:38:1,2
Nikolay: Yeah.
727
0:38:2,38 --> 0:38:5,98
Direct performance benefits from
partitioning are good, but also
728
0:38:5,98 --> 0:38:13,32
the problem when creation of index
or full table vacuum, it will
729
0:38:13,32 --> 0:38:17,22
take hours or half a day, it's
already so painful.
730
0:38:17,24 --> 0:38:22,4
Especially if it's to prevent transaction
wraparound, or if it's,
731
0:38:22,7 --> 0:38:25,16
again, indexing means that
xmin horizon...
732
0:38:25,2 --> 0:38:27,42
Like anyway, let's not go too deep.
733
0:38:27,44 --> 0:38:31,12
The question to ask, will we survive
n number of rows because
734
0:38:31,12 --> 0:38:32,46
we expect all the data.
735
0:38:33,84 --> 0:38:34,52
I want-
736
0:38:34,54 --> 0:38:38,96
Michael: And just out of interest,
is it pretty much always doing
737
0:38:38,96 --> 0:38:41,7
range partitioning based on timestamp
type?
738
0:38:41,77 --> 0:38:42,52
Is that
739
0:38:42,52 --> 0:38:44,56
Nikolay: like- Again, I'm the wrong
person to ask.
740
0:38:44,56 --> 0:38:45,1
Michael: Okay, yeah, fair
741
0:38:45,1 --> 0:38:45,6
Nikolay: enough.
742
0:38:45,86 --> 0:38:49,74
In many cases, I just use to still
use TimescaleDB if it's time-based.
743
0:38:50,74 --> 0:38:51,54
Michael: Makes sense.
744
0:38:52,74 --> 0:38:55,16
Nikolay: And I'm happy because
there is compression there, it's
745
0:38:55,16 --> 0:38:55,66
great.
746
0:38:55,9 --> 0:39:0,08
But I also saw cases when it was
decided, not AI, but it was
747
0:39:0,08 --> 0:39:3,06
decided, like this partitioning
and full control over partitions,
748
0:39:3,42 --> 0:39:4,7
special table created.
749
0:39:4,9 --> 0:39:8,86
It was also working well and served
needs, but on surface, you
750
0:39:8,86 --> 0:39:13,02
should think about workload and
then push your AI or something
751
0:39:13,04 --> 0:39:17,06
to think about how it will behave
if you have that much data.
752
0:39:17,96 --> 0:39:19,86
Partitioning is inevitable there.
753
0:39:19,86 --> 0:39:21,14
And then experiments again.
754
0:39:21,28 --> 0:39:24,24
Even experiment with 1000000000
rows, it won't take a lot, just
755
0:39:24,24 --> 0:39:25,84
allocate machine and do it.
756
0:39:25,84 --> 0:39:28,78
It's like just so easy, right?
757
0:39:28,86 --> 0:39:33,04
Half an hour of wait to generate
it, or more like even if it's
758
0:39:33,04 --> 0:39:36,0
1 hour why I don't understand why
people don't do it all the
759
0:39:36,0 --> 0:39:40,58
time They come to they come to
us with questions, which I'm grateful.
760
0:39:40,58 --> 0:39:44,84
Thank you with money and so on
But it's so easy just to experiment
761
0:39:44,86 --> 0:39:46,2
more often these days
762
0:39:47,32 --> 0:39:50,0
Michael: Yeah, even when it even
leave it running overnight
763
0:39:51,0 --> 0:39:51,5
Nikolay: Yeah
764
0:39:53,98 --> 0:39:58,02
Michael: Yeah, I'm still I'm still
in the fence I love partitioning
765
0:39:58,04 --> 0:40:2,76
for the reasons you mentioned,
But I think so many projects won't
766
0:40:2,76 --> 0:40:6,96
ever need it that I understand
not doing it by default with a
767
0:40:6,96 --> 0:40:9,92
new project unless you know, unless
it's like a new feature for
768
0:40:9,92 --> 0:40:12,5
an existing system that you know
is going to get heavy amounts
769
0:40:12,5 --> 0:40:13,76
of data really quickly.
770
0:40:14,1 --> 0:40:17,22
Just there are so many trade-offs
like that it's still a little
771
0:40:17,22 --> 0:40:17,64
bit like...
772
0:40:17,64 --> 0:40:18,4
Nikolay: Which trade-offs?
773
0:40:20,18 --> 0:40:24,18
Michael: So for example indexes,
being able to create indexes
774
0:40:24,18 --> 0:40:26,9
concurrently, delete, drop indexes
concurrently.
775
0:40:27,98 --> 0:40:30,72
Nikolay: You can concurrently create
index on each partition
776
0:40:30,76 --> 0:40:34,04
and then yeah it exists on each
partition you can create this
777
0:40:34,04 --> 0:40:39,44
these things can be automated and
of course I like to be living
778
0:40:39,44 --> 0:40:42,94
much more better automated there
is a big potential here but
779
0:40:42,94 --> 0:40:47,78
also every Every Postgres version
gets a lot of improvements
780
0:40:47,86 --> 0:40:50,1
in this area, last maybe 10 years
already.
781
0:40:50,14 --> 0:40:53,94
So it's not like frozen.
782
0:40:54,66 --> 0:40:56,4
Yeah, but also make like,
783
0:40:57,34 --> 0:41:1,12
Michael: just being extra careful
that every high frequency query
784
0:41:1,12 --> 0:41:6,3
you have contains the partition
key and is getting pruned at
785
0:41:6,6 --> 0:41:9,5
planning time like there are a
few gotchas
786
0:41:9,64 --> 0:41:12,18
Nikolay: that hit you worse.
787
0:41:12,34 --> 0:41:12,5332
Michael: Yeah there are,
788
0:41:12,5332 --> 0:41:13,98
Nikolay: I have a series of articles
about this and I went quite
789
0:41:13,98 --> 0:41:15,92
deep there and it was crazy yeah.
790
0:41:16,42 --> 0:41:18,16
Michael: But that's my, they're
my hesitations.
791
0:41:19,2 --> 0:41:19,7
Nikolay: Creation
792
0:41:19,76 --> 0:41:20,72
Michael: of foreign keys.
793
0:41:22,42 --> 0:41:23,04
Nikolay: That's why I'm hesitant
for example working with timescale
794
0:41:23,04 --> 0:41:27,44
timescale DB something like often
we just Abandoned idea of having
795
0:41:27,44 --> 0:41:31,72
foreign key because yeah, it's
important If you do partitioning
796
0:41:31,72 --> 0:41:35,8
yourself, you will deal with, if
you have large tables, adding
797
0:41:35,8 --> 0:41:37,16
foreign key between them.
798
0:41:37,96 --> 0:41:41,06
If partitioning is involved, it's
an art, I would say.
799
0:41:41,14 --> 0:41:42,52
And GitLab mastered it.
800
0:41:42,52 --> 0:41:45,08
They have Migration Helpers RB.
801
0:41:45,48 --> 0:41:49,94
This is a great source of experience
of many people involved
802
0:41:49,94 --> 0:41:53,44
and I can't stop recommending how
great it is because it's open
803
0:41:53,44 --> 0:41:53,94
source.
804
0:41:54,38 --> 0:41:57,62
It's a great place to look at and
also they have great documentation
805
0:41:57,88 --> 0:41:58,58
about it.
806
0:41:58,58 --> 0:42:1,56
But anyway, it's a thing to decide.
807
0:42:1,56 --> 0:42:5,1
If you don't expect billions of
rows, don't do it, of course.
808
0:42:6,66 --> 0:42:11,26
At the same time, we have also
very sad cases when people come
809
0:42:11,26 --> 0:42:12,6
to us for consulting.
810
0:42:12,7 --> 0:42:15,6
We identify problems, we say partitioning
is needed.
811
0:42:16,22 --> 0:42:19,74
Sometimes we go and spend a lot
of effort because the project
812
0:42:19,74 --> 0:42:23,32
is huge or there are many databases
to take care of.
813
0:42:23,5 --> 0:42:26,5
And then finally it's implemented
great.
814
0:42:27,94 --> 0:42:31,26
There are many hidden dangers to
explore.
815
0:42:31,62 --> 0:42:36,04
Mid-journey, for example, they
came to us, helped, forgot about
816
0:42:36,04 --> 0:42:39,14
lightweight lock, well, not forgot,
we didn't know about that
817
0:42:39,14 --> 0:42:42,78
at that time, and they hit it,
lightweight LockManager problem
818
0:42:43,28 --> 0:42:47,72
was hit badly, and there are articles
about it.
819
0:42:48,1 --> 0:42:51,6
But then some projects just say,
okay, they evaluate how much
820
0:42:51,6 --> 0:42:54,78
effort it is to deal with partitioning
when tables are huge,
821
0:42:55,12 --> 0:42:58,06
and start talking, okay, maybe
we should migrate to my MongoDB.
822
0:42:59,72 --> 0:43:1,82
Michael: Yeah, Or even sharding,
right?
823
0:43:1,82 --> 0:43:6,82
Like instead, I think when we talked
to Notion, Arka, he mentioned
824
0:43:6,82 --> 0:43:10,44
that because they keep their shards
relatively small, they've
825
0:43:10,44 --> 0:43:12,68
opted to never partition at all.
826
0:43:12,74 --> 0:43:14,48
Nikolay: Yeah, the need for partitioning
diminishes.
827
0:43:15,04 --> 0:43:17,86
I agree with this, but not vanishes
completely.
828
0:43:18,96 --> 0:43:19,78
Michael: What, maybe?
829
0:43:20,34 --> 0:43:20,64
Nikolay: Not
830
0:43:20,64 --> 0:43:21,14
Michael: maybe.
831
0:43:21,28 --> 0:43:25,22
You mentioned, it depends how small
you keep your shards, right?
832
0:43:26,12 --> 0:43:27,68
Nikolay: Yeah, yeah.
833
0:43:28,08 --> 0:43:30,52
Michael: Like Sugu, for example,
Multigres, he talked about
834
0:43:30,52 --> 0:43:33,3
having lots of smaller databases
being much easier.
835
0:43:33,4 --> 0:43:34,42
Nikolay: Yeah, yeah, yeah, yeah.
836
0:43:34,64 --> 0:43:35,56
Maybe you're right.
837
0:43:36,34 --> 0:43:36,5
Michael: Okay.
838
0:43:36,5 --> 0:43:37,4
What about RLS?
839
0:43:37,54 --> 0:43:38,72
You mentioned you wanted to touch
on
840
0:43:38,72 --> 0:43:39,22
Nikolay: that.
841
0:43:39,38 --> 0:43:43,68
Yeah, finally, RLS, if you, for
example, I just noticed it's
842
0:43:43,68 --> 0:43:47,44
not like Supabase is very heavily
promoting RLS.
843
0:43:47,44 --> 0:43:51,02
They have a lot of articles including
how to avoid problems.
844
0:43:51,22 --> 0:43:56,6
But if you just use AI, I noticed
it often adds RLS.
845
0:43:57,18 --> 0:43:57,9
Michael: Oh, really?
846
0:43:58,26 --> 0:43:59,16
Nikolay: Yeah, I just...
847
0:43:59,16 --> 0:43:59,66
Interesting.
848
0:44:0,06 --> 0:44:0,92
This is how it works.
849
0:44:0,92 --> 0:44:4,86
I just noticed I had a couple of
cases where suddenly it brought
850
0:44:4,9 --> 0:44:6,9
like, we are going to use RLS.
851
0:44:6,9 --> 0:44:8,74
It's multi-tenancy here, let's
do it.
852
0:44:8,74 --> 0:44:12,58
Okay, let's do it, but let's also
pause and think about performance
853
0:44:13,38 --> 0:44:19,24
and avoid problems like current_setting
inside a loop and you
854
0:44:19,54 --> 0:44:23,2
SELECT count of million rows suddenly
becomes super slow.
855
0:44:23,42 --> 0:44:28,4
So the question to ask AI, again,
benchmark is ideal here.
856
0:44:30,06 --> 0:44:34,06
With RLS, without RLS, what is
the overhead of having RLS in
857
0:44:34,06 --> 0:44:34,92
our particular case?
858
0:44:34,92 --> 0:44:36,18
Let's benchmark it.
859
0:44:36,22 --> 0:44:39,72
And if you see it's not okay, then
let's optimize it, because
860
0:44:39,72 --> 0:44:41,82
there are tricks how to optimize
it.
861
0:44:43,66 --> 0:44:48,16
So if I notice an AI-generated
schema with RLS, I would ask question
862
0:44:48,16 --> 0:44:50,14
like, what will be performance
impact?
863
0:44:50,98 --> 0:44:53,74
Let's benchmark it, and if it's
bad, let's optimize.
864
0:44:53,8 --> 0:44:54,38
That's it.
865
0:44:54,38 --> 0:44:54,88
Yeah.
866
0:44:55,84 --> 0:44:56,34
Great.
867
0:44:57,22 --> 0:44:57,72
Great.
868
0:44:58,44 --> 0:44:59,32
That's it, actually.
869
0:44:59,38 --> 0:44:59,64
I think
870
0:44:59,64 --> 0:45:1,2
Michael: that's a useful checklist,
actually.
871
0:45:1,2 --> 0:45:5,72
Nikolay: 5 or 6 things to ask your
AI when you design schema.
872
0:45:7,66 --> 0:45:9,3
Michael: Yeah or your colleague
even.
873
0:45:10,24 --> 0:45:13,08
Nikolay: Yeah also or your own
schema which you have already
874
0:45:13,08 --> 0:45:14,06
10 years, why not.
875
0:45:14,06 --> 0:45:16,96
Michael: Yeah exactly or even a
new table if you're doing a new
876
0:45:16,96 --> 0:45:19,96
feature it still makes sense that
all of these make sense.
877
0:45:20,58 --> 0:45:21,24
Nikolay: I agree.
878
0:45:21,42 --> 0:45:23,5
Cool, thank you for listening.
879
0:45:24,16 --> 0:45:25,9
Yeah, see you soon.