1
00:00:00,049 --> 00:00:03,739
Michael: Hello and welcome to Postgres.FM,
a weekly show about all things PostgreSQL.

2
00:00:03,799 --> 00:00:05,250
I am Michael, founder of pgMustard.

3
00:00:05,259 --> 00:00:08,389
I'm joined as always by
Nik, founder of Postgres.ai.

4
00:00:08,529 --> 00:00:08,859
Hey, Nik

5
00:00:09,658 --> 00:00:10,228
Nik: Hi, Michael

6
00:00:10,697 --> 00:00:13,417
Michael: And we have two
special guests with us today.

7
00:00:13,617 --> 00:00:19,227
We have Qian, who is CEO and
co-founder of DBOS, and Peter,

8
00:00:19,537 --> 00:00:21,387
CTO and co-founder of DBOS.

9
00:00:21,777 --> 00:00:22,297
Welcome both

10
00:00:24,267 --> 00:00:24,667
Qian: Thank you

11
00:00:25,693 --> 00:00:30,453
Michael: I wonder if one of you could
give us a bit of the backstory of DBOS.

12
00:00:30,603 --> 00:00:31,743
I think it's quite a cool story

13
00:00:32,443 --> 00:00:32,683
Qian: Yeah.

14
00:00:32,743 --> 00:00:35,723
So we started DBOS as a research project.

15
00:00:36,153 --> 00:00:42,137
I think it was started quite in the middle
of pandemic in summer 2020, when Professor

16
00:00:42,137 --> 00:00:48,957
Mike Stonebraker heard a talk from Matei
Zaharia, the profes- our advisors and

17
00:00:48,957 --> 00:00:53,727
also co-founder of Databricks, where they
were talking about how Databricks were u-

18
00:00:53,767 --> 00:00:59,117
was using Postgres to do some scheduling
work because, distributed scheduling

19
00:00:59,137 --> 00:01:01,217
and cluster management was a mess.

20
00:01:01,537 --> 00:01:04,847
And they were talking about how to
use a database to simplify that.

21
00:01:05,297 --> 00:01:10,477
And then I think during that summer we
talked and discussed, like, how we can

22
00:01:10,667 --> 00:01:17,027
u- extend that idea to make database
more helpful to build, reliable systems

23
00:01:17,407 --> 00:01:19,407
to even replace the operating system.

24
00:01:19,717 --> 00:01:23,920
So that's how we started the original
research project called DBOS.

25
00:01:24,700 --> 00:01:30,150
And then Peter and I were the co-leads PhD
students working on that research project.

26
00:01:30,200 --> 00:01:33,730
We published several
papers, did a bunch of work.

27
00:01:34,160 --> 00:01:39,490
And then what we found was that, the most
interesting thing was actually to use the

28
00:01:39,500 --> 00:01:44,550
database as a foundation for distributed
systems, to build distributed systems.

29
00:01:44,850 --> 00:01:50,710
And that's why when we both graduated
in 2023 we decided to do, to

30
00:01:50,710 --> 00:01:52,990
commercialize this idea, to do a startup.

31
00:01:53,190 --> 00:01:57,080
And now we are working on
DBOS Inc., the company.

32
00:01:57,778 --> 00:02:03,314
Nik: that's cool And I must admit
I observed queues in Postgres

33
00:02:03,474 --> 00:02:06,134
for so long, like 20 plus years.

34
00:02:06,814 --> 00:02:12,676
And somehow when DBOS DBOS was
announced, of course it it attracted

35
00:02:12,676 --> 00:02:17,508
my attention, but I didn't quite get
it like Because like w-w-workflow,

36
00:02:17,518 --> 00:02:19,158
like what i-what is it, right?

37
00:02:19,768 --> 00:02:24,118
But then I started to realize,
actually, to realize that in any

38
00:02:24,118 --> 00:02:30,588
business there are workflows, and to
keep the state and all the history and

39
00:02:30,588 --> 00:02:32,748
so on in database, it's very natural.

40
00:02:33,398 --> 00:02:34,958
So this is a very good idea.

41
00:02:35,458 --> 00:02:40,748
And I think it's, it's-- it revives
the idea that queue in that database

42
00:02:40,748 --> 00:02:43,858
is not only possible, but it's
possible in quite efficient way.

43
00:02:44,328 --> 00:02:47,868
But you need to… With Postgres,
you need to be very careful, right?

44
00:02:48,228 --> 00:02:53,388
Because of some, some things that
might happen if you implement

45
00:02:53,428 --> 00:02:55,188
queues and workflows in naive way.

46
00:02:55,478 --> 00:02:58,610
But  I don't quite get it
still why operational system.

47
00:02:59,230 --> 00:03:05,090
I understand when Andrei Karpaty
talks about like AI as a CPU in

48
00:03:05,090 --> 00:03:10,140
the center, like memory, like every
p-peripheral devices, everything.

49
00:03:10,530 --> 00:03:14,660
I understand this analogy, but here
I don't quite get the analogy why

50
00:03:14,700 --> 00:03:19,470
is there is some intent or idea to
implement whole works operational

51
00:03:19,470 --> 00:03:21,580
system in, in da-database.

52
00:03:22,130 --> 00:03:23,680
Like what's your view?

53
00:03:24,264 --> 00:03:24,604
Peter: Yeah.

54
00:03:24,624 --> 00:03:30,174
So what we're doing as a company is
using the database as a foundation

55
00:03:30,184 --> 00:03:33,914
for distributed workflows, and then
for things around the distributed

56
00:03:33,964 --> 00:03:37,794
workflows like queues, like messaging,
like notifications, and so on.

57
00:03:38,461 --> 00:03:45,221
I think the core idea here is that
as your program runs, you regularly

58
00:03:45,231 --> 00:03:50,951
checkpoint the state of that program
in a Postgres database so that if the

59
00:03:50,951 --> 00:03:55,211
program ever fails, if anything happens
to it, if it crashes, if it's interrupted,

60
00:03:55,541 --> 00:03:58,751
it can reload those checkpoints to
recover from exactly where it left off.

61
00:03:59,111 --> 00:04:03,647
And that, durable workflows idea backed
by Postgres is the core idea of DBOS,

62
00:04:03,777 --> 00:04:10,397
using Postgres as an orchestrator to
make durable workflows that run really

63
00:04:10,547 --> 00:04:13,047
w- that run with high performance
and that are really simple because

64
00:04:13,047 --> 00:04:15,777
all they have is, all they have,
all they rely on is your database

65
00:04:17,344 --> 00:04:18,224
Nik: Yeah, this makes sense.

66
00:04:18,374 --> 00:04:19,414
I understand that.

67
00:04:19,804 --> 00:04:24,474
I'm just still processing the idea.

68
00:04:24,474 --> 00:04:25,904
It's like whole operational system.

69
00:04:26,682 --> 00:04:30,442
Qian: I think our goal is that when
we think about operating system,

70
00:04:30,492 --> 00:04:33,822
we should not be limited to the
operating system we're running

71
00:04:33,822 --> 00:04:36,032
our, on our laptop or desktop.

72
00:04:36,432 --> 00:04:40,052
Thinking like anything operational,
I think Kubernetes can be called

73
00:04:40,062 --> 00:04:42,282
as a distributed operating system.

74
00:04:42,712 --> 00:04:46,372
Thinking about like anything that
can run your application as like a

75
00:04:46,372 --> 00:04:49,112
meta name for like operating system.

76
00:04:49,492 --> 00:04:54,872
So I think nowadays, especially with
AI, operating system is like cool again.

77
00:04:54,902 --> 00:04:59,282
Everyone is calling themselves an AI
operating system or, like something

78
00:04:59,302 --> 00:05:03,562
AI-powered operating system for
engineers, for sales, for whatsoever.

79
00:05:03,872 --> 00:05:08,622
So I think operating system now, like
the idea is more about how to properly

80
00:05:08,902 --> 00:05:14,012
run your applications in any environment,
like in a distributed environment,

81
00:05:14,032 --> 00:05:15,832
in a highly available environment.

82
00:05:16,192 --> 00:05:20,802
It's about how to provide the
guarantee and provide kind of an

83
00:05:20,842 --> 00:05:26,002
illusion that you are ru- like normal,
like modern developers no longer

84
00:05:26,002 --> 00:05:30,672
directly write or interact with
low-level operating system primitives.

85
00:05:31,012 --> 00:05:36,342
They usually interact with some- something
like Kubernetes or cloud resources.

86
00:05:36,692 --> 00:05:40,042
So like you can think of the cloud
as an entire operating system.

87
00:05:40,052 --> 00:05:46,012
So like I think the name like DBOS right
now really focuses on how to operate

88
00:05:46,082 --> 00:05:49,062
those, applications at scale and reliably.

89
00:05:49,372 --> 00:05:54,251
So it's I would say it's eventually
at a grand vision, we hope everything

90
00:05:54,251 --> 00:05:56,851
will be backed by a database,

91
00:05:57,377 --> 00:05:57,737
Nik: Yeah.

92
00:05:58,421 --> 00:06:01,401
Qian: then that will be like
a super operating system.

93
00:06:02,431 --> 00:06:02,681
Yeah.

94
00:06:02,731 --> 00:06:06,881
Nik: Yeah this makes sense in, in
broader meaning operating system.

95
00:06:06,901 --> 00:06:07,091
Yeah.

96
00:06:07,121 --> 00:06:09,331
Not just like Linux or Windows

97
00:06:09,381 --> 00:06:10,021
or something, Right.

98
00:06:10,031 --> 00:06:10,631
I get this.

99
00:06:10,631 --> 00:06:14,163
And also like there are even broader
systems like there, there are companies

100
00:06:14,163 --> 00:06:18,137
like which are building like an
operational-- operating system for

101
00:06:18,137 --> 00:06:20,187
businesses in broader meaning, right?

102
00:06:20,647 --> 00:06:25,593
I also remember when Anthropic released
I think Ultracode with workflows.

103
00:06:26,083 --> 00:06:31,243
I remember on Hacker News
top both topics were present.

104
00:06:31,503 --> 00:06:34,965
One was about their workflows,
another was about some blog

105
00:06:34,965 --> 00:06:36,745
post from your blog, actually.

106
00:06:37,175 --> 00:06:37,765
I remember.

107
00:06:37,945 --> 00:06:40,515
It was like-- And for me, it was
like this is two different things.

108
00:06:40,815 --> 00:06:45,075
And I remember internal discussion in
our team actually, that we use these

109
00:06:45,075 --> 00:06:51,565
workflows in Claude Code, but workflows
which are like saving some state

110
00:06:51,565 --> 00:06:53,065
in database is something different.

111
00:06:53,065 --> 00:06:59,085
And then I remember an aha moment when
we realized we actually have agentic

112
00:06:59,085 --> 00:07:03,485
workflows ins-internally already
working, and we save state in Postgres.

113
00:07:04,035 --> 00:07:06,305
So I realized it's very close topics.

114
00:07:06,345 --> 00:07:11,875
I think you also use this resonating
moment when they released it and you like

115
00:07:11,895 --> 00:07:13,675
to discuss workflows in database, right?

116
00:07:14,689 --> 00:07:19,005
So I have one specific technical question
though because Peter, you mentioned

117
00:07:19,015 --> 00:07:21,685
that if something crashes, right?

118
00:07:22,425 --> 00:07:27,055
And ch- this checkpoint and database were
all good, like we ha- we have stayed safe.

119
00:07:27,135 --> 00:07:30,525
I don't like fully understand why
checkpoint is needed because we always

120
00:07:30,525 --> 00:07:32,295
can replay more after checkpoint.

121
00:07:32,315 --> 00:07:33,525
There are WALs and so on, right?

122
00:07:33,765 --> 00:07:34,355
But anyway, we

123
00:07:34,641 --> 00:07:35,181
Michael: checkpoint

124
00:07:35,745 --> 00:07:37,325
Nik: Ah, okay, a checkpoint there.

125
00:07:37,555 --> 00:07:41,525
But what wonders me, if, for example,
we se-send an email but we don't

126
00:07:41,585 --> 00:07:44,635
know the result of that yet, right?

127
00:07:44,635 --> 00:07:50,135
I'm like moving us slowly to the topic
of two-phase commit or something, right?

128
00:07:50,665 --> 00:07:51,625
How do you handle that?

129
00:07:52,557 --> 00:07:52,937
Peter: Yeah.

130
00:07:53,047 --> 00:07:58,057
So if your step is taking an action
in an external system and it fails

131
00:07:58,057 --> 00:08:01,527
in the middle of the step, then we're
going to have to retry that step, and

132
00:08:01,527 --> 00:08:05,337
that, that's unavoidable in any kind
of workflow or durability system.

133
00:08:05,707 --> 00:08:09,987
So our recommendation then is for
individual steps that take-- have

134
00:08:09,987 --> 00:08:11,817
side effects, external systems
to make those steps idempotent.

135
00:08:12,917 --> 00:08:14,537
And, we provide help for that.

136
00:08:14,547 --> 00:08:17,407
Every workflow has a unique ID
that you can use as an idempotency

137
00:08:17,407 --> 00:08:19,160
key in systems that support that.

138
00:08:19,170 --> 00:08:21,510
Many email systems do
support idempotency keys.

139
00:08:22,090 --> 00:08:24,670
Some problems are fundamental
to distributed systems.

140
00:08:24,850 --> 00:08:28,820
If you're taking a side effect
somewhere else and it fails and you

141
00:08:28,820 --> 00:08:30,460
don't know whether it succeeded,
you're gonna have to retry it.

142
00:08:30,750 --> 00:08:33,140
And so you should really try to make
sure that, that action's idempotent

143
00:08:34,749 --> 00:08:38,619
Michael: And another thing I didn't
fully appreciate till recently, I kept

144
00:08:38,619 --> 00:08:42,069
focusing on the word durable I see
durable workflows mentioned quite often.

145
00:08:42,069 --> 00:08:45,209
I'm thinking about reliability and
retries, and that all makes sense.

146
00:08:45,219 --> 00:08:48,559
The thing I didn't really
appreciate was actions that

147
00:08:48,559 --> 00:08:50,029
quite-- could be quite costly.

148
00:08:50,109 --> 00:08:55,659
If you've got a five-step workflow and
you've spent a lot of tokens or a lot

149
00:08:55,749 --> 00:09:00,539
of money, or you've sent physical goods
or something like that in steps one,

150
00:09:00,539 --> 00:09:04,469
two, and three, and it fails at step
four, you can avoid the c- the cost

151
00:09:04,469 --> 00:09:06,399
of redoing steps one, two, and three.

152
00:09:06,409 --> 00:09:11,299
Like it, the-- that hadn't really clicked
for me, that sometimes it's about other

153
00:09:11,359 --> 00:09:14,459
side effects, like might just be cost
or it might be safety or it might be

154
00:09:14,469 --> 00:09:19,212
something else that you don't have to risk
re-replaying is that a big part of it at

155
00:09:19,222 --> 00:09:22,772
the moment in terms of… or are there
parts of that might be helpful to explain?

156
00:09:24,352 --> 00:09:25,202
Qian: Yeah, definitely.

157
00:09:25,252 --> 00:09:28,502
I think especially nowadays,
everyone's building AI agents

158
00:09:28,872 --> 00:09:32,282
and those new models are becom-
becoming more and more expensive.

159
00:09:32,542 --> 00:09:36,902
So if you have already asked the
model about something, you want to

160
00:09:37,362 --> 00:09:39,632
use that output as much as possible.

161
00:09:40,072 --> 00:09:44,462
And if something crashes, you want
to resume from where it left off.

162
00:09:44,472 --> 00:09:47,572
Say, like you already asked the
model what to do next, then you

163
00:09:47,572 --> 00:09:50,662
should continue from that step
instead of asking the models again.

164
00:09:50,992 --> 00:09:55,919
Because otherwise you have to burn all the
tokens and this will quickly become too

165
00:09:55,919 --> 00:10:00,452
costly if you're having like processing
thousands of documents and then you

166
00:10:00,452 --> 00:10:02,972
want to do it in a more efficient way.

167
00:10:03,022 --> 00:10:06,652
That's where DBOS is used a
lot, like building agents and

168
00:10:06,662 --> 00:10:08,801
building AI-powered pipelines.

169
00:10:09,211 --> 00:10:12,441
I think cost saving is
definitely… It's like becoming

170
00:10:12,441 --> 00:10:13,731
more and more important, I'd say.

171
00:10:14,571 --> 00:10:14,771
Yeah.

172
00:10:15,814 --> 00:10:16,414
Michael: Makes sense.

173
00:10:16,454 --> 00:10:16,934
Interesting

174
00:10:17,092 --> 00:10:18,642
Qian: And also compute resources, right?

175
00:10:18,652 --> 00:10:22,532
Even if you don't have to pay those,
Even for some people who will self-host

176
00:10:22,532 --> 00:10:26,542
their GPUs and - self-host their model
inference, then you don't want to

177
00:10:26,552 --> 00:10:28,482
burn all the compute resources again.

178
00:10:28,492 --> 00:10:30,252
It's all about efficiency.

179
00:10:30,822 --> 00:10:36,212
I think yeah, durability itself
sounds like not, doesn't sound like

180
00:10:36,212 --> 00:10:41,512
a very useful word, but it indicates
a lot about cost savings and also

181
00:10:41,512 --> 00:10:44,728
in- indicates about after you've done
something, you don't repeat it again.

182
00:10:45,068 --> 00:10:48,448
So it's indicates about
correctness as well.

183
00:10:49,058 --> 00:10:49,198
Yeah

184
00:10:49,750 --> 00:10:53,070
Nik: I have a question about why
should it be inside Postgres?

185
00:10:53,070 --> 00:10:56,200
We know there is-- there are systems
which do the same outside, right?

186
00:10:56,240 --> 00:10:59,950
Temporal is one of the
most popular systems.

187
00:11:00,500 --> 00:11:04,160
I even from my personal experience,
I even had a moment when one of our

188
00:11:04,160 --> 00:11:09,467
clients reimplemented our Postgres
major upgrade workflow in Temporal.

189
00:11:09,587 --> 00:11:10,457
it was, It was Gadget

190
00:11:10,657 --> 00:11:12,194
Michael: They spoke
about it on the episode.

191
00:11:12,330 --> 00:11:14,720
Nik: Yeah, we had an episode
about that, and it was like  we

192
00:11:14,720 --> 00:11:16,730
always used Ansible and so on.

193
00:11:17,000 --> 00:11:18,670
Of course it's very different, right?

194
00:11:18,700 --> 00:11:22,300
When you have workflow, you
save everything, it's great.

195
00:11:22,670 --> 00:11:26,220
But you move further, let's bring
the whole thing inside Postgres.

196
00:11:26,240 --> 00:11:29,736
What are pros and cons and, why do
you think this is a winning strategy?

197
00:11:30,540 --> 00:11:30,770
Peter: Yeah.

198
00:11:30,770 --> 00:11:32,800
So I'd, what I'd ask is why not Postgres?

199
00:11:33,320 --> 00:11:40,350
The temporal model is that you are
taking… is that you're outsourcing

200
00:11:40,560 --> 00:11:45,960
the control flow of your program to
an external orchestrator, and that's

201
00:11:45,960 --> 00:11:47,210
a lot of additional infrastructure.

202
00:11:47,840 --> 00:11:52,830
You can either self-host it, in which case
to run your workflows, you have to spin

203
00:11:52,830 --> 00:11:57,227
up highly available temporal cluster, a
database for it, which is Cassan- and its

204
00:11:57,227 --> 00:11:59,127
database is Cassandra and Elasticsearch.

205
00:11:59,417 --> 00:12:02,417
And now you're spinning up three
new distributed systems to run your

206
00:12:02,417 --> 00:12:06,067
workflows, and all three of those systems
are totally on your critical path.

207
00:12:06,077 --> 00:12:07,787
If anything happens to
them, Nothing will work.

208
00:12:07,947 --> 00:12:09,027
Your workflows will not function.

209
00:12:09,027 --> 00:12:10,087
Your business grinds to a halt.

210
00:12:10,557 --> 00:12:13,367
So you're putting three complex
distributed systems on your critical path.

211
00:12:13,697 --> 00:12:14,997
Or - you use their cloud service.

212
00:12:14,997 --> 00:12:17,317
Now you're putting an external
SaaS on your critical path.

213
00:12:17,967 --> 00:12:18,707
You don't need to do that.

214
00:12:19,727 --> 00:12:21,007
You just need a database.

215
00:12:21,967 --> 00:12:26,131
And what we're trying to do in DBOS is
totally replace all of those distributed

216
00:12:26,131 --> 00:12:30,561
systems, all those external orchestrators
with your Postgres database so that you

217
00:12:30,571 --> 00:12:32,541
can use your database as an orchestrator.

218
00:12:32,761 --> 00:12:34,261
It's already on your critical path.

219
00:12:34,271 --> 00:12:36,541
It's already something that you're
making sure is highly available.

220
00:12:36,551 --> 00:12:38,331
It's already critical for
your application to work.

221
00:12:38,861 --> 00:12:42,641
Why not also use it as your workflow
orchestrator so that you have

222
00:12:42,641 --> 00:12:45,691
less infrastructure, so that your
operations are simpler, so that

223
00:12:45,691 --> 00:12:48,451
there are fewer things that can break
and bring down everything you have

224
00:12:49,647 --> 00:12:50,477
Nik: Two things here.

225
00:12:51,007 --> 00:12:56,767
One is who will-- like Postgres
doesn't have scheduler, right?

226
00:12:57,287 --> 00:13:00,357
You can use pg_cron or something,
or you need to still need

227
00:13:00,357 --> 00:13:02,237
external help to tick, right?

228
00:13:02,287 --> 00:13:03,047
To move forward.

229
00:13:03,117 --> 00:13:07,597
And another thing is that if it's all
running on the primary and primary CPU,

230
00:13:07,597 --> 00:13:10,177
first of all, is not infinite, right?

231
00:13:10,177 --> 00:13:15,377
So you need to avoid heavy things
and have them outside still, right?

232
00:13:16,135 --> 00:13:17,465
Peter: Yeah, and that's
the reason Postgres is

233
00:13:19,805 --> 00:13:19,855
the orchestrator.

234
00:13:19,855 --> 00:13:20,665
The actual business logic
of your workflows is still

235
00:13:20,665 --> 00:13:21,585
running in your application.

236
00:13:21,595 --> 00:13:24,165
The scheduler is running in your
application, for example, and

237
00:13:24,165 --> 00:13:25,465
then Postgres is the orchestrator.

238
00:13:25,845 --> 00:13:29,065
So your applications are running
a scheduler, and your applications

239
00:13:29,065 --> 00:13:32,785
will enqueue when the sched- when
the cron fires, your, one of your

240
00:13:32,785 --> 00:13:36,775
application servers will enqueue a
scheduled workflow into Postgres.

241
00:13:37,205 --> 00:13:38,985
And then Postgres there
does orchestration.

242
00:13:39,165 --> 00:13:43,245
It coordinates multiple servers to
make sure that only one of those

243
00:13:43,245 --> 00:13:46,605
servers actually executes the scheduled
workflow when its cron tick fires.

244
00:13:47,165 --> 00:13:50,275
So your applications your regular
servers are doing the work, and

245
00:13:50,275 --> 00:13:52,865
Postgres is orchestrating them to
make sure that every, all the work

246
00:13:52,865 --> 00:13:56,095
is being done totally reliably, that
everything happens exactly once.

247
00:13:56,705 --> 00:13:57,365
Nik: Makes sense.

248
00:13:57,695 --> 00:14:02,495
So this is when you say critical
path, you don't include things

249
00:14:04,085 --> 00:14:07,555
that like sending email can be done
from multiple servers and they are

250
00:14:07,555 --> 00:14:10,225
replaceable like stateless and so on.

251
00:14:10,245 --> 00:14:11,585
Yeah, I get that.

252
00:14:11,845 --> 00:14:19,388
It feels slightly awkward for
me to try to criticize the idea.

253
00:14:19,428 --> 00:14:22,508
I'm a big fan of myself, like
maybe Michael should join

254
00:14:22,508 --> 00:14:24,498
and find some other questions

255
00:14:25,177 --> 00:14:27,717
Michael: I was actually gonna
ask a question that probably

256
00:14:27,717 --> 00:14:29,647
goes even more positive.

257
00:14:29,697 --> 00:14:33,327
M- or maybe not, but I would expect
there to be performance benefits to

258
00:14:33,327 --> 00:14:39,237
using your already h- used database for
this rather than an external service.

259
00:14:39,527 --> 00:14:44,058
So I was only gonna say What are, what are
the performance trade-offs and what do you

260
00:14:44,058 --> 00:14:48,038
do to minimize the impact you're having
on primary CPU and that kind of thing?

261
00:14:49,898 --> 00:14:50,708
Peter: Yeah, absolutely.

262
00:14:50,708 --> 00:14:55,058
So latency in particular gets a lot better
because now the latency of executing

263
00:14:55,058 --> 00:14:59,928
a step is just one write to your local
Postgres as opposed to a async dispatch

264
00:14:59,938 --> 00:15:01,448
from somebody else's cloud service.

265
00:15:02,938 --> 00:15:05,098
So definitely latency improves.

266
00:15:05,648 --> 00:15:07,438
Now throughput then
becomes database bounded.

267
00:15:07,768 --> 00:15:11,508
You can process as many
workflows per second as, as your

268
00:15:11,508 --> 00:15:12,568
Postgres allows if you want.

269
00:15:12,618 --> 00:15:15,882
And if you need to scale beyond
that,  you need to shard across

270
00:15:15,882 --> 00:15:17,242
multiple Postgres databases.

271
00:15:17,742 --> 00:15:20,172
The good news is that these
operations are fairly light.

272
00:15:20,592 --> 00:15:25,712
So our rule of thumb is that four Postgres
cores can handle 1,000 actions per second.

273
00:15:26,322 --> 00:15:30,362
So for a large database, that means
you can vertically scale for a large

274
00:15:30,382 --> 00:15:33,902
Postgres database up to… we've
benchmarked around 40,000 actions per

275
00:15:33,902 --> 00:15:35,632
second on a single Postgres database.

276
00:15:36,092 --> 00:15:38,152
And then of course, you need more
than that, you can shard or you can

277
00:15:38,162 --> 00:15:39,512
use multiple Postgres databases.

278
00:15:39,662 --> 00:15:42,322
So your throughput does become
bounded by your database, but the

279
00:15:42,322 --> 00:15:44,742
good news is that your database
can handle a lot of throughput

280
00:15:46,250 --> 00:15:46,530
Nik: Yeah.

281
00:15:46,710 --> 00:15:47,050
Yeah.

282
00:15:47,364 --> 00:15:47,654
Michael: and we're

283
00:15:47,654 --> 00:15:51,228
talking… so let's say we've
got a workflow with six steps.

284
00:15:51,658 --> 00:15:56,458
We've got one write at the beginning, one
write per step, and one write at the end.

285
00:15:56,458 --> 00:16:01,198
So we're still only talking eight writes
total for a si- and this workflow's having

286
00:16:01,198 --> 00:16:04,848
to do things in the meantime, it's not
gonna be running through them instantly.

287
00:16:05,178 --> 00:16:06,458
So yeah, great.

288
00:16:06,488 --> 00:16:07,238
Okay, cool.

289
00:16:07,459 --> 00:16:10,769
Peter: And you can handle 40,000 of
those actions per second on one database

290
00:16:11,313 --> 00:16:12,173
Nik: Yeah, I'm curious.

291
00:16:12,193 --> 00:16:15,833
I researched a little bit and I noticed
y-that you started with repeatable

292
00:16:15,893 --> 00:16:21,333
read isolation l-level and then reduced
it to default read committed, right?

293
00:16:21,373 --> 00:16:23,423
I'm very curious what was behind that.

294
00:16:24,293 --> 00:16:26,603
Is it the throughput
question or anything else?

295
00:16:27,969 --> 00:16:30,689
Peter: So we use isolation levels
there's a lot of different parts

296
00:16:30,689 --> 00:16:32,729
of our system that use isolation
levels for different reason.

297
00:16:33,406 --> 00:16:39,446
we've designed the system so that
almost everything is a single statement.

298
00:16:40,376 --> 00:16:43,396
So it can just be a, it can just
execute a recommitted, and that

299
00:16:43,396 --> 00:16:44,666
makes things far more efficient.

300
00:16:44,986 --> 00:16:47,456
As many things as possible execute
as a single Postgres statement,

301
00:16:47,466 --> 00:16:49,936
so they can just be atomic and you
don't have to worry about isolation.

302
00:16:49,946 --> 00:16:51,116
You set it to default recommitted.

303
00:16:52,126 --> 00:16:56,626
The one thing that we can't do
that for is the main DQ query.

304
00:16:57,326 --> 00:17:00,406
this is probably the single most
important s- query in the entire system,

305
00:17:00,866 --> 00:17:07,856
and it's the query that every worker
runs periodically on every queue to

306
00:17:08,156 --> 00:17:13,160
dequeue the latest work off the queue
And that's a very complex query we've

307
00:17:13,160 --> 00:17:14,960
put a ton of effort in optimizing.

308
00:17:16,590 --> 00:17:21,110
That query has a, actually has a
conditional isolation level, s- depending

309
00:17:21,110 --> 00:17:23,440
on what, how you've configured your queue.

310
00:17:24,490 --> 00:17:30,680
So if your queue uses global flow control
primitives, if your queue, for example,

311
00:17:30,680 --> 00:17:35,180
has a global concurrency limit or a global
rate limit, so you say, for example,

312
00:17:35,180 --> 00:17:40,440
that this queue can only execute one
operation at a time across every worker,

313
00:17:40,960 --> 00:17:45,230
then that requires global coordination,
which means we have to use a high

314
00:17:45,230 --> 00:17:47,040
isolation level like repeatable read.

315
00:17:48,640 --> 00:17:54,170
And that has coordination costs, but
if you have those global limits, you're

316
00:17:54,170 --> 00:17:57,080
probably fine paying the cost because
you're only executing one workflow

317
00:17:57,080 --> 00:17:58,490
at a time across all your servers.

318
00:17:58,785 --> 00:18:02,030
th- your throughput was limited
anyways in practice though, what we

319
00:18:02,030 --> 00:18:05,359
see is that high throughput queues
usually only use local limits.

320
00:18:05,629 --> 00:18:08,619
They might say only execute 10
workflows at a time per worker.

321
00:18:11,459 --> 00:18:13,629
And those local limits don't
require global coordination.

322
00:18:14,319 --> 00:18:19,389
And that means that it's perfectly safe
to run the queues query at a very low

323
00:18:19,389 --> 00:18:21,039
isolation level at at read committed.

324
00:18:21,599 --> 00:18:26,409
And that way you can have this query
running across hundreds of servers at the

325
00:18:26,409 --> 00:18:28,919
same time, or thousands of servers at the
same time without them conflicting because

326
00:18:28,919 --> 00:18:29,829
they're all running read committed.

327
00:18:30,309 --> 00:18:35,449
And that lets you get this thirty, forty
thousand dequeues, dequeued workflows per

328
00:18:35,449 --> 00:18:37,129
second, actions per second throughput.

329
00:18:37,469 --> 00:18:41,359
And it really caps out at thirty
thousand dequeues per second on a single

330
00:18:41,359 --> 00:18:44,139
Postgres across thousands of workers
because we've optimized this query

331
00:18:45,169 --> 00:18:48,299
Nik: And in case of sharding
solution re-repeat-repeatable read

332
00:18:48,319 --> 00:18:49,729
wouldn't be available anyway, right?

333
00:18:50,259 --> 00:18:53,919
So because like global
limits it's… Yeah.

334
00:18:53,949 --> 00:18:54,149
You

335
00:18:54,457 --> 00:18:57,997
Peter: But in practice, global limits tend
to go with relatively low throughputs.

336
00:18:58,326 --> 00:18:59,686
Nik: And it's flexible now, right?

337
00:18:59,686 --> 00:19:03,146
So u-u-user can control it
and decide what, what's in.

338
00:19:03,156 --> 00:19:03,676
That's great.

339
00:19:04,316 --> 00:19:07,386
And I also noticed listen to
file was recently added, right?

340
00:19:07,556 --> 00:19:09,756
Or not recently, maybe I'm wrong.

341
00:19:09,814 --> 00:19:14,774
Qian: We've been having listen notify,
but recently we've been optimizing it

342
00:19:15,306 --> 00:19:15,696
Nik: yeah.

343
00:19:15,966 --> 00:19:20,736
We ha-- Yeah, I think it was last year,
Recall AI they discovered this awful

344
00:19:20,806 --> 00:19:23,806
global lock on commit for Notifier, right?

345
00:19:23,836 --> 00:19:26,076
What's your deal with this right now?

346
00:19:26,816 --> 00:19:31,386
Peter: Listen/notify is really powerful
but has multiple performance issues.

347
00:19:32,016 --> 00:19:33,706
There was that issue that… I forget.

348
00:19:33,716 --> 00:19:35,626
There was that issue that reached
top of Hacker News last year.

349
00:19:35,966 --> 00:19:37,876
That was actually not
the issue we ran into.

350
00:19:37,896 --> 00:19:39,386
That issue was solved in Postgres 19.

351
00:19:39,386 --> 00:19:40,446
That is not the issue we ran into.

352
00:19:40,778 --> 00:19:44,048
that issue is that notify had
terrible perform- notify's broadcast

353
00:19:44,048 --> 00:19:44,478
mode had terrible performance for
large numbers of sparse readers.

354
00:19:44,478 --> 00:19:44,488
Uh,

355
00:19:48,668 --> 00:19:49,678
that's actually an unrelated issue.

356
00:19:49,678 --> 00:19:50,558
We do not have that.

357
00:19:50,558 --> 00:19:51,458
We have dense readers.

358
00:19:51,458 --> 00:19:52,378
We don't have sparse readers.

359
00:19:53,011 --> 00:19:55,571
Nik: Can you explain please the
sparse readers versus dense readers?

360
00:19:55,831 --> 00:19:56,191
Peter: Oh, sorry.

361
00:19:56,991 --> 00:20:00,091
The problem that everybody was talking
about last year was that if you're

362
00:20:00,091 --> 00:20:05,081
broadcasting to a huge number of
readers, each of which is only reading

363
00:20:05,091 --> 00:20:10,091
a small fraction of all messages,
that had terrible performance.

364
00:20:11,751 --> 00:20:14,389
So that's actually not
the issue we ran into.

365
00:20:14,509 --> 00:20:17,609
The way DBOS is set up, pretty much
all workers are listening to all

366
00:20:17,619 --> 00:20:20,749
messages and that performs fine.

367
00:20:21,149 --> 00:20:23,399
The issue we ran into was
the global lock at notify.

368
00:20:24,549 --> 00:20:30,429
When you commit a transaction that
performs a notify, it has to acquire a

369
00:20:30,429 --> 00:20:34,629
global lock for the entire duration of
the commit, and that includes the fsync.

370
00:20:35,949 --> 00:20:40,089
And what that means is that these
transactions can't be group committed.

371
00:20:40,449 --> 00:20:45,719
They have to commit sequentially, which
essentially bounds the throughput of

372
00:20:46,759 --> 00:20:48,759
any operations that perform notify.

373
00:20:48,759 --> 00:20:50,869
You can't perform more than a
certain number of them per second

374
00:20:50,869 --> 00:20:52,009
because you can't group commit.

375
00:20:52,239 --> 00:20:54,299
So you're stuck waiting out the
entire duration of the fsync.

376
00:20:55,456 --> 00:20:58,286
the optimization that we settled on
there to make listen notify, because

377
00:20:58,286 --> 00:20:59,416
we really want to use listen notify.

378
00:20:59,436 --> 00:21:02,886
It provides incredibly low
latency, and when it works, it

379
00:21:02,886 --> 00:21:03,866
provides incredibly low latency.

380
00:21:04,226 --> 00:21:07,386
So w- the optimization we
settled on was notify batching.

381
00:21:07,956 --> 00:21:14,639
So instead of sending a notify,
originally, we were sending notifies

382
00:21:14,649 --> 00:21:17,949
in a trigger every single time you,
for example, wrote to a stream.

383
00:21:19,689 --> 00:21:23,419
And the optimization was instead it
was removing the trigger and instead

384
00:21:23,419 --> 00:21:26,239
buffering stream writes in a background
thread and then sending all the

385
00:21:26,239 --> 00:21:29,999
notifies in the batch and send that
batch e-every ten milliseconds or so.

386
00:21:30,729 --> 00:21:32,989
A huge batch of notifies is
actually very fast because you

387
00:21:32,999 --> 00:21:35,069
only have to hold the global lock
for the duration of that commit.

388
00:21:35,344 --> 00:21:36,054
that's fast.

389
00:21:36,314 --> 00:21:41,054
And what this does is this allows us to
use listen notify without re- running

390
00:21:41,054 --> 00:21:43,114
it, worrying about the global lock
because we're batching commits and

391
00:21:43,114 --> 00:21:46,574
the global lock's only required for
commits, and still getting the benefits

392
00:21:46,574 --> 00:21:49,995
of low latency because we can still
emit these every, every few milliseconds

393
00:21:51,329 --> 00:21:55,179
Qian: I think, the overall observation is
that we don't really need that global o-

394
00:21:55,269 --> 00:21:59,919
ordering when we're doing the application
level streaming, like streaming,

395
00:22:00,019 --> 00:22:02,039
for example, results to the client.

396
00:22:02,509 --> 00:22:07,889
Then what do you-- we're leveraging the
listen/notify to do is to notify like,

397
00:22:07,899 --> 00:22:09,619
"Hey, we have some results available."

398
00:22:09,679 --> 00:22:11,689
So we don't really need
that strict ordering.

399
00:22:12,059 --> 00:22:16,019
And I think that was the main aha moment
for us that, okay, we don't need to

400
00:22:16,019 --> 00:22:18,399
limit ourselves to that exclusive locks.

401
00:22:18,409 --> 00:22:19,489
We can do batching.

402
00:22:19,969 --> 00:22:20,149
Yeah.

403
00:22:22,123 --> 00:22:25,353
Nik: And yeah, and this is… okay.

404
00:22:25,353 --> 00:22:27,983
So and also the problem is happening.

405
00:22:28,073 --> 00:22:32,853
You, Peter mentioned that it was
fixed in 19, and it's happening when

406
00:22:33,783 --> 00:22:35,763
commit has a lot to write, right?

407
00:22:35,763 --> 00:22:37,443
So like it's this fsync.

408
00:22:38,903 --> 00:22:42,643
Basically, transaction is significant in
terms of how much it's writing, right?

409
00:22:42,643 --> 00:22:44,923
So th-this increases the problem.

410
00:22:44,933 --> 00:22:49,643
But in your case, you have only
like brief writes in transaction.

411
00:22:50,363 --> 00:22:51,203
Not a lot, right?

412
00:22:51,253 --> 00:22:56,273
Not like a lot of JSONs to be
written and fsynced during commit,

413
00:22:56,716 --> 00:23:00,936
So the problem shouldn't be huge
a-already if I'm not mistaken.

414
00:23:01,308 --> 00:23:02,368
and you decided to group it.

415
00:23:02,368 --> 00:23:03,678
This is interesting optimization.

416
00:23:04,328 --> 00:23:06,398
I, I wanted to raise
this question, of course.

417
00:23:06,458 --> 00:23:12,458
You use skip locked updates and
what's happening if we have some

418
00:23:12,468 --> 00:23:17,368
xmin horizon blockers, so long
transactions or lagging logical slots?

419
00:23:18,048 --> 00:23:21,308
Obviously, a lot of dead tuples
should be accumulated, right?

420
00:23:22,688 --> 00:23:28,758
Is it a requirement if you use DBOS
at, like at high rates, at a lot

421
00:23:28,758 --> 00:23:30,278
of events per second and so on?

422
00:23:30,278 --> 00:23:35,578
Is it a requirement I need to watch
closely what is my xmin horizon?

423
00:23:35,708 --> 00:23:40,358
Don't allow long-running transactions,
don't allow logical… lagging

424
00:23:40,358 --> 00:23:44,684
logical replication slots long
transactions, so on standbys with

425
00:23:44,864 --> 00:23:46,564
hot standby feedback on and so on.

426
00:23:46,624 --> 00:23:49,594
Is it a requirement or you solve
this somehow I don't know, like

427
00:23:49,594 --> 00:23:53,794
maybe mitigate-mitigating bloat with
repacking, reindexing and so on?

428
00:23:54,834 --> 00:23:55,194
Peter: Got it.

429
00:23:55,204 --> 00:24:00,764
So just to be clear, we're only using
skip locked on our own workflow status

430
00:24:00,784 --> 00:24:06,644
table, and that's typically isolated
within its own Postgres database.

431
00:24:07,504 --> 00:24:14,906
So our recommendation is Don't take
out ultra long-running transactions

432
00:24:15,526 --> 00:24:19,086
inside the DBOS database, and you
probably weren't doing that anyways

433
00:24:19,116 --> 00:24:20,696
because DBOS certainly doesn't do that.

434
00:24:21,016 --> 00:24:22,406
And then the rest should be fine.

435
00:24:23,396 --> 00:24:25,046
The DBOS operations are isolated here

436
00:24:25,558 --> 00:24:29,998
Nik: So you don't use skip locked
for like events t- tables where

437
00:24:29,998 --> 00:24:32,648
events or workflow data is stored?

438
00:24:33,260 --> 00:24:37,600
Peter: It's only used for deques off of
the main workflow status table, and that's

439
00:24:37,600 --> 00:24:39,440
usually pretty isolated from user data

440
00:24:40,282 --> 00:24:44,612
Nik: But still, it's you need a s- single
update on every status change, right?

441
00:24:45,996 --> 00:24:49,986
Peter: Yes, and that does lead
to dead tuple accumulation.

442
00:24:50,440 --> 00:24:53,960
but that's a separate issue that
y-yes, at ma- at massive, at

443
00:24:56,340 --> 00:24:59,890
really high throughputs, like when
we were doing these benchmarks at,

444
00:25:00,090 --> 00:25:08,224
40,000 workflows per second,  we
saw high CPU usage due to dead

445
00:25:08,234 --> 00:25:11,704
tuple cleanup and autovacuum because
we're turning on these indexes.

446
00:25:12,034 --> 00:25:16,024
And we've done a lot of work
to optimize indexes, so most,

447
00:25:16,294 --> 00:25:18,094
almost every index is partial.

448
00:25:18,384 --> 00:25:22,144
So most indexes are only being
maintained either for a small

449
00:25:22,144 --> 00:25:25,314
subset of workflows or only for a
small subset of workflow lifetime.

450
00:25:26,224 --> 00:25:33,684
But there's still a fair amount of index
churn on the order of… And this, of

451
00:25:33,684 --> 00:25:37,873
course, not as hot  a dead tuple, and it's
going to create a bunch of dead index.

452
00:25:37,873 --> 00:25:40,733
It's going to create two or
three dead index entries that are

453
00:25:40,733 --> 00:25:42,193
going to have to be cleaned up.

454
00:25:43,373 --> 00:25:47,773
At really high throughputs, this can
become the bottleneck but that sets in

455
00:25:47,783 --> 00:25:52,653
around 20 or 30,000 actions per second
is when we start seeing the tuple

456
00:25:52,653 --> 00:25:55,213
accumulation really becoming a problem,
because there really aren't that…

457
00:25:55,343 --> 00:25:57,373
Tuple accumulation become
a problem, because we have

458
00:25:57,373 --> 00:25:58,433
optimized this as much as we can.

459
00:25:59,238 --> 00:26:00,808
Nik: Yeah, I see it very differently.

460
00:26:01,668 --> 00:26:08,098
I see it like when you say 20,
30,000 per second, it means for me it

461
00:26:08,098 --> 00:26:14,108
becomes 2,000, 3,000 or 200, 300 per
second if we talk about really long

462
00:26:14,228 --> 00:26:19,008
transaction happening in parallel
because they block vacuum, uh, work.

463
00:26:19,558 --> 00:26:25,058
We cannot delete the tuples, and at
very high throughput with shorter

464
00:26:25,138 --> 00:26:29,798
transactions will be-become the same
if you have much lower throughput,

465
00:26:30,578 --> 00:26:33,168
Peter: Yeah, which is why
our recommendation is isolate

466
00:26:33,248 --> 00:26:35,048
the DBOS system database

467
00:26:35,466 --> 00:26:38,286
from the database we're using, we're
using long-running transactions.

468
00:26:38,536 --> 00:26:42,026
These don't have to be in the same, in
the… These don't have to be in the

469
00:26:42,026 --> 00:26:47,380
same Postgres database, and they probably
shouldn't be, especially for long-running

470
00:26:47,380 --> 00:26:48,920
transactions that could block Autovacuum

471
00:26:49,354 --> 00:26:52,174
Nik: Yeah, this is what my… Yeah,
th-this is exactly what I was asking.

472
00:26:52,224 --> 00:26:55,154
It's not like like requirement, but
it definitely if you have long-running

473
00:26:55,344 --> 00:26:59,664
transactions or any other explain
horizon blockers performance of

474
00:26:59,664 --> 00:27:02,244
DB-DBOS will, will suffer eventually.

475
00:27:02,674 --> 00:27:05,174
Peter: Yeah, but these don't, these
can be isolated from each other.

476
00:27:05,684 --> 00:27:09,034
They don't have to run in the same
place, and they probably shouldn't run

477
00:27:09,034 --> 00:27:11,234
in the same place, especially at scale

478
00:27:12,218 --> 00:27:13,098
Nik: This is interesting.

479
00:27:13,108 --> 00:27:14,518
We have a lot of common things.

480
00:27:14,568 --> 00:27:18,138
I agree with putting everything
to Postgres open source.

481
00:27:18,138 --> 00:27:21,628
I w- didn't mention that the thing
you're building is open source, which

482
00:27:21,628 --> 00:27:23,398
is great, and like this is super cool.

483
00:27:24,328 --> 00:27:28,978
And and like we don't need
extra things on critical path.

484
00:27:29,618 --> 00:27:30,298
That's great.

485
00:27:30,338 --> 00:27:33,298
Also I guess you have self-served
option as well, right?

486
00:27:33,348 --> 00:27:36,078
If it is open source, you
can self-host yourself.

487
00:27:36,188 --> 00:27:36,978
Like freedom, right?

488
00:27:36,978 --> 00:27:37,438
It's great.

489
00:27:38,108 --> 00:27:42,588
And you run also y-you
can run DBOS in, on RDS.

490
00:27:42,638 --> 00:27:43,478
Absolute fr-freedom.

491
00:27:43,798 --> 00:27:47,958
But when you say let's split it,
it's against the first principle.

492
00:27:47,958 --> 00:27:50,988
Like first principle, let's
have it everything in one place.

493
00:27:51,338 --> 00:27:56,511
Because if we have, this thing
and we have other tables, we can

494
00:27:56,631 --> 00:27:58,571
organize code right inside database.

495
00:27:58,971 --> 00:28:02,871
Sometimes it's a bad idea if
it's very long, if CPU-consuming.

496
00:28:03,601 --> 00:28:06,821
But if it's something simple, I
want to update everything inside,

497
00:28:06,831 --> 00:28:09,471
like using triggers, PostgreSQL
code functions, and so on.

498
00:28:09,471 --> 00:28:10,411
I don't want to split.

499
00:28:10,991 --> 00:28:15,951
But once I have database, say terabyte in
size, and I start some analytical queries

500
00:28:15,951 --> 00:28:20,161
right there because I'm not I'm not grown
enough to have ClickHouse or something.

501
00:28:20,161 --> 00:28:21,971
I have only Postgres, right?

502
00:28:22,221 --> 00:28:26,451
Those long-running queries
will take time, right?

503
00:28:26,981 --> 00:28:32,901
And in this case updates and data, MVCC
in Postgres actually is not a good thing

504
00:28:32,921 --> 00:28:35,281
to deal with, as we all know, right?

505
00:28:35,291 --> 00:28:38,911
This is I'm j- I'm asking because, like
you m- you know that I'm trying to revive

506
00:28:38,921 --> 00:28:43,011
this PGQ from Skype, which is bloat-free.

507
00:28:43,641 --> 00:28:45,481
No, almost no updates.

508
00:28:45,881 --> 00:28:48,361
For event table, there
are no updates there.

509
00:28:48,691 --> 00:28:52,511
But I don't see how it will… I
don't see clearly how it will be

510
00:28:52,511 --> 00:28:54,171
used to implement workflows yet.

511
00:28:54,261 --> 00:28:59,551
But for many tasks, it's great, and we
don't have problems with bloat there.

512
00:29:00,461 --> 00:29:04,471
And I'm trying to understand
how our operate properly.

513
00:29:04,471 --> 00:29:09,621
Like in, in case of updates, we
need to establish some rules.

514
00:29:09,701 --> 00:29:13,301
Like for example, we should stop
allowing long-running transactions.

515
00:29:13,871 --> 00:29:16,781
I'm I'm glad I participated in
building transaction timeout.

516
00:29:16,781 --> 00:29:20,031
So we set transaction
timeout, say like 10 minutes.

517
00:29:20,091 --> 00:29:20,591
That's it.

518
00:29:21,311 --> 00:29:26,171
And I'm very curious, like maybe
there is a way to define some

519
00:29:26,881 --> 00:29:32,401
boundaries and rules when it's time
to move away and to like to split to

520
00:29:32,811 --> 00:29:35,761
different Postgres cluster, right?

521
00:29:35,811 --> 00:29:36,881
To split these workloads.

522
00:29:37,261 --> 00:29:39,311
M- And there are no good rules yet.

523
00:29:39,351 --> 00:29:39,861
Th-This is a problem

524
00:29:40,839 --> 00:29:45,119
Peter: One is that you don't have to split
multiple Postgres clusters or servers.

525
00:29:45,129 --> 00:29:48,779
You can, if I remember how Autovacuum
works correctly, it is isolated across

526
00:29:48,779 --> 00:29:50,369
multiple databases on the same cluster.

527
00:29:50,441 --> 00:29:51,471
Nik: Logical databases.

528
00:29:51,709 --> 00:29:52,339
Peter: Yes.

529
00:29:52,689 --> 00:29:54,689
So like the xmin horizon
is set per database.

530
00:29:54,699 --> 00:29:57,526
So so you don't have to split
your physical infrastructure.

531
00:29:57,816 --> 00:29:59,166
You can share physical infrastructure

532
00:30:00,670 --> 00:30:01,720
Nik: Yeah that's a good point.

533
00:30:01,933 --> 00:30:02,383
I agree.

534
00:30:03,393 --> 00:30:07,253
Still, I feel like it would be great
to have some benchmarks or something to

535
00:30:07,253 --> 00:30:11,383
define when it's time, when it's time
to split, when it's fine, and which

536
00:30:11,383 --> 00:30:13,063
transactions should be disallowed.

537
00:30:13,923 --> 00:30:15,493
I think it-- you're very right.

538
00:30:15,533 --> 00:30:20,653
If you have 30,000 workflows per
second it-- your requirements become

539
00:30:20,663 --> 00:30:24,043
much stricter in terms of what means
long-running transaction, right?

540
00:30:24,423 --> 00:30:26,023
How much xmin horizon is lagging.

541
00:30:26,723 --> 00:30:29,413
Anyway, thank you for
diving into this topic.

542
00:30:29,413 --> 00:30:31,913
I'm also trying to learn this myself

543
00:30:32,629 --> 00:30:32,819
Qian: Yeah.

544
00:30:33,639 --> 00:30:37,129
I think that's actually a good, really
good point because you are right that

545
00:30:37,159 --> 00:30:42,429
if you put… Y- there are benefits
of co-locating the workflow tables

546
00:30:42,459 --> 00:30:44,369
with your actual application data.

547
00:30:44,789 --> 00:30:48,599
One thing we were talking about
in some of our blog posts is this

548
00:30:48,609 --> 00:30:50,639
transactional outbox pattern.

549
00:30:50,699 --> 00:30:55,749
If you say someone, like we updated
the customer data, now we want to

550
00:30:55,749 --> 00:30:59,709
kickstart some of the workflows,
then by co-locating them in the same

551
00:30:59,709 --> 00:31:01,749
database, you can do it in a trigger.

552
00:31:01,759 --> 00:31:05,699
Say, whenever you update this customer
data, automatically start or enqueue

553
00:31:05,739 --> 00:31:09,969
a DBOS workflow in the same database,
and that's really convenient and

554
00:31:09,969 --> 00:31:14,039
that, that's a way that you can
guarantee whenever there's updates

555
00:31:14,049 --> 00:31:17,699
in this table, you'll kickstart a
workflow that is durable, that will,

556
00:31:17,919 --> 00:31:19,719
that DBOS will guarantee will finish.

557
00:31:20,119 --> 00:31:23,209
And that's a pattern that is
really difficult to do with,

558
00:31:23,919 --> 00:31:27,159
say, like external orchestrator
because there will always be a gap.

559
00:31:27,539 --> 00:31:30,999
And by co-locating in the same database,
there's a way to close this gap.

560
00:31:31,489 --> 00:31:33,149
But everything has a trade-off.

561
00:31:33,229 --> 00:31:36,649
Like you said, if you put in the
same database, you have to be really

562
00:31:36,649 --> 00:31:40,409
careful about what you, what queries
you put in that database, like how

563
00:31:40,409 --> 00:31:44,089
to avoid head of line blocking, how
to avoid bloating in that database.

564
00:31:44,569 --> 00:31:48,809
So I think that's a topic we're
continuously, benchmarking and

565
00:31:48,809 --> 00:31:52,489
also by talking to users and see
what kind of workflows and what

566
00:31:52,489 --> 00:31:54,289
kind of workloads they're running.

567
00:31:54,589 --> 00:31:58,649
And we're, we are also trying to
categorize like a more detailed,

568
00:31:58,699 --> 00:32:00,009
guidelines for people as well.

569
00:32:00,807 --> 00:32:01,767
Nik: Yeah, interesting.

570
00:32:01,767 --> 00:32:05,597
But any-anyway I'm glad that this, this
topic is becoming more and more popular

571
00:32:05,597 --> 00:32:09,327
and other companies started to join
t- like M-Microsoft recently, right?

572
00:32:09,537 --> 00:32:10,347
With pgDurable.

573
00:32:10,347 --> 00:32:11,807
What do you think about that system?

574
00:32:12,720 --> 00:32:13,050
Qian: Yeah.

575
00:32:13,050 --> 00:32:14,180
So this is great.

576
00:32:14,270 --> 00:32:18,450
I think pgDurable is mostly
for pure SQLs, right?

577
00:32:18,490 --> 00:32:22,000
It's, it doesn't really work for
if you want to send a email or

578
00:32:22,000 --> 00:32:23,710
interact with external systems.

579
00:32:24,330 --> 00:32:28,673
So we'll definitely, take a look and
see if there are any ways to collaborate

580
00:32:28,673 --> 00:32:30,893
or borrow ideas from pgDurable.

581
00:32:31,143 --> 00:32:35,213
But I think in general it's
another validation of the Postgres

582
00:32:35,849 --> 00:32:36,169
Nik: yeah.

583
00:32:36,169 --> 00:32:36,389
yeah.

584
00:32:36,389 --> 00:32:39,109
Everything more and more people
start looking in the same direction.

585
00:32:39,109 --> 00:32:41,199
I think yeah, the topic is warming up.

586
00:32:41,199 --> 00:32:42,109
I agree with you here.

587
00:32:42,329 --> 00:32:45,939
Also, there was a system called
Absurd, which got my attention.

588
00:32:46,409 --> 00:32:47,719
Single SQL file as well.

589
00:32:47,719 --> 00:32:49,999
It… I think it was
released last November.

590
00:32:50,809 --> 00:32:51,519
And yeah.

591
00:32:51,935 --> 00:32:57,335
I noticed in pgdurable is that the
tendency to have a SQL PL, PostgreSQL

592
00:32:57,335 --> 00:32:59,495
code quite ef-ephemeral way.

593
00:33:00,225 --> 00:33:03,355
And this is w- like you're just
passing it as a parameter, like

594
00:33:03,355 --> 00:33:04,465
to lump the functions, right?

595
00:33:04,935 --> 00:33:09,555
And this concerns me because I want
my code still to be in Git, right?

596
00:33:09,595 --> 00:33:13,315
To be versionable, fix,
reviewable, and so on, right?

597
00:33:14,345 --> 00:33:20,315
So this leads me to question, what
are the best use cases, case studies

598
00:33:20,315 --> 00:33:25,270
you ha- you had already, you where
you observed debor… I'm sorry,

599
00:33:25,280 --> 00:33:27,260
like I pronounce it DBOS for myself.

600
00:33:27,260 --> 00:33:27,820
I don't know why.

601
00:33:28,260 --> 00:33:31,660
What are the best use case studies,
use cases you have, like where

602
00:33:31,660 --> 00:33:36,160
it shines in terms of how like,
like it suits really well, right?

603
00:33:36,620 --> 00:33:41,760
Sending emails or like processing orders
in e-commerce or something like this.

604
00:33:41,790 --> 00:33:43,220
What are the best cases?

605
00:33:45,223 --> 00:33:45,703
Peter: Yeah.

606
00:33:45,866 --> 00:33:48,356
the thing about workflows is that
they're really powerful f- they're

607
00:33:48,356 --> 00:33:49,536
really powerful for a lot of things.

608
00:33:49,546 --> 00:33:54,430
So of course, the, the really
hype new use case we see is AI

609
00:33:54,430 --> 00:33:55,860
agents and operations around them.

610
00:33:56,340 --> 00:34:01,360
So people using workflows both to
make their actual agents durable.

611
00:34:01,400 --> 00:34:04,690
This is particularly important for
long-running interactive agents that

612
00:34:04,690 --> 00:34:08,690
might, talk to a person or do multiple
rounds of conversation with a person

613
00:34:08,690 --> 00:34:12,230
or require approvals for a person
over the course of hours or days.

614
00:34:13,050 --> 00:34:17,320
Similarly, data pipelines like document
processing pipelines for agents often

615
00:34:17,320 --> 00:34:21,411
require durability, and, you know,
workflows can really help there.

616
00:34:21,914 --> 00:34:23,994
So those are kind of
the, the new use cases.

617
00:34:24,234 --> 00:34:28,834
And then there's this whole host of
traditional business workflows that

618
00:34:28,844 --> 00:34:32,184
have always needed durability and
where Postgres backed durability

619
00:34:32,184 --> 00:34:33,594
just makes it easier to provide it.

620
00:34:34,004 --> 00:34:36,054
And that's things like logistics.

621
00:34:36,224 --> 00:34:38,384
It's things like, like
a- like lab automation.

622
00:34:38,906 --> 00:34:42,693
It's things like new user registration,
making sure new users are properly

623
00:34:42,703 --> 00:34:45,923
updated in every single one of your
cor- of your corporation's databases.

624
00:34:47,263 --> 00:34:51,835
So we see this really broad spectrum
of use cases for workflows because

625
00:34:52,675 --> 00:34:59,819
really any important operation that
touches customer data, touches important

626
00:34:59,819 --> 00:35:03,699
data, it probably should be a workflow
if you're really operating at scale

627
00:35:03,699 --> 00:35:04,989
and you really care about reliability

628
00:35:06,020 --> 00:35:10,340
Qian: I think in summary it's we
really view Postgres as the central

629
00:35:10,360 --> 00:35:12,670
orchestrator to connect things together.

630
00:35:13,220 --> 00:35:16,840
You can connect external systems
to your local database, or you

631
00:35:16,840 --> 00:35:22,600
can do event processing to connect
multiple systems like your other

632
00:35:22,610 --> 00:35:26,960
databases or your email server,
payment system, all sorts of things.

633
00:35:27,190 --> 00:35:31,820
So we re- try to make sure your
Postgres is the glue for everything.

634
00:35:33,130 --> 00:35:33,300
Yeah

635
00:35:33,784 --> 00:35:36,994
Nik: And speaking of Postgres
maybe last question from me.

636
00:35:37,424 --> 00:35:41,191
So now Postgres 19 beta 2
just released,  Postgres 20

637
00:35:41,201 --> 00:35:42,511
development has started already.

638
00:35:42,641 --> 00:35:48,551
What what do you think should be changed
in Postgres or brought to Postgres to make

639
00:35:48,651 --> 00:35:51,131
the product you are building even better?

640
00:35:52,548 --> 00:35:55,718
Peter: I think we do have to go…
I would like to go back to notify.

641
00:35:55,738 --> 00:36:03,958
Removing that global lock is not easy,
and I'm not sure if it's doable with the

642
00:36:03,958 --> 00:36:09,808
way notify is currently implemented, but
that area of the code hasn't really been

643
00:36:09,818 --> 00:36:14,418
worked on much since Postgres 9 and,
maybe there's something that could be done

644
00:36:14,418 --> 00:36:18,108
there to remove the footguns from notify.

645
00:36:19,048 --> 00:36:23,415
And even if you don't remove them,
I think notify's really complicated

646
00:36:23,415 --> 00:36:25,945
performance characteristics should
probably be, should probably be actually

647
00:36:25,945 --> 00:36:29,022
documented instead of having to figure
this out like through by… I mean,

648
00:36:29,192 --> 00:36:31,472
we figured this out basically by,
by reading the Postgres source code.

649
00:36:31,982 --> 00:36:33,002
That, that's one thing.

650
00:36:33,692 --> 00:36:36,912
Qian: Yeah, I think another thing
is about, like, how to ho- better

651
00:36:36,922 --> 00:36:42,062
s- even better support for larger
larger blobs or larger columns.

652
00:36:42,850 --> 00:36:45,880
when you-- when people are using DBOS,
sometimes they want to checkpoint, say, a

653
00:36:45,880 --> 00:36:48,254
PDF or something  as part of the output.

654
00:36:48,714 --> 00:36:53,404
And still I know Postgres has some good
support for larger blobs right now.

655
00:36:53,404 --> 00:36:58,324
But I think most of the time, we still
recommend people to say "Upload your

656
00:36:58,784 --> 00:37:04,874
large documents to S3, then store a
pointer in the database." But that still

657
00:37:04,874 --> 00:37:09,734
leaves, a little bit gap in the durable
checkpointing, because there could be

658
00:37:09,734 --> 00:37:14,664
the case where you upload something
but you don't checkpoint or vice versa.

659
00:37:14,684 --> 00:37:19,254
It'll be great if we can really put
more, like, larger data into Postgres.

660
00:37:20,948 --> 00:37:24,318
Michael: There is a nice change,
I think, coming in 19 around that

661
00:37:24,478 --> 00:37:28,918
in that the, I think the default
TOAST compression is changing from

662
00:37:29,508 --> 00:37:34,198
like a, an older PGLZ, like a
Postgres specific compression

663
00:37:34,248 --> 00:37:36,438
algorithm to, I think it's LZ4.

664
00:37:37,050 --> 00:37:39,770
but yeah only minor on, on
the grand scheme of things.

665
00:37:40,148 --> 00:37:40,568
nice one

666
00:37:42,088 --> 00:37:42,108
Nik: Yeah.

667
00:37:42,388 --> 00:37:49,028
And speaking of notify, I think, yes,
global lock is still there, not removed.

668
00:37:49,028 --> 00:37:50,898
I remember some work
last year in PostgreSQL

669
00:37:51,826 --> 00:37:55,846
Peter: There was work in 19 on
improving the performance of

670
00:37:56,316 --> 00:38:01,476
broadcast, of the notify broadcast
when not all listeners are listening

671
00:38:01,506 --> 00:38:03,766
for all messages, which is great.

672
00:38:03,956 --> 00:38:05,076
That's a real performance improvement.

673
00:38:05,836 --> 00:38:08,516
The global lock is much harder to get rid
of, and I'm not sure if it's possible.

674
00:38:08,596 --> 00:38:09,306
But that is…

675
00:38:09,472 --> 00:38:11,302
Nik: work on removing that lock.

676
00:38:11,302 --> 00:38:12,592
I remember that, that work.

677
00:38:12,592 --> 00:38:17,502
I also remember I just founded my
email to document, as you said,

678
00:38:17,612 --> 00:38:22,052
the limitations from July 10, 2025.

679
00:38:22,072 --> 00:38:23,722
Nobody answered my email.

680
00:38:24,802 --> 00:38:26,072
So there is a patch proposed.

681
00:38:26,122 --> 00:38:27,212
Let's document it at least.

682
00:38:27,222 --> 00:38:27,522
Yeah,

683
00:38:27,634 --> 00:38:30,394
Peter: Yeah, the I, certainly 19 doesn't
include any work on the global lock.

684
00:38:30,414 --> 00:38:32,014
Maybe there's some in
development for 20, I'm not

685
00:38:32,084 --> 00:38:32,434
Nik: Yeah.

686
00:38:32,674 --> 00:38:33,064
Yeah.

687
00:38:33,124 --> 00:38:33,504
Yeah.

688
00:38:33,634 --> 00:38:34,414
It's worth checking.

689
00:38:34,744 --> 00:38:36,534
And that documentation should be changed.

690
00:38:36,534 --> 00:38:37,184
I agree with you.

691
00:38:37,724 --> 00:38:42,144
So maybe someone listening to us will
go and comment on that proposal, and

692
00:38:42,144 --> 00:38:43,764
we will get it documented at least.

693
00:38:44,454 --> 00:38:44,794
Great.

694
00:38:44,814 --> 00:38:45,714
Thank you so much.

695
00:38:45,764 --> 00:38:46,824
I enjoyed it a lot.

696
00:38:47,184 --> 00:38:50,234
I wish you all the best
with this wonderful product.

697
00:38:50,527 --> 00:38:54,147
I'm going to try it in a couple
of places, and I encourage

698
00:38:54,207 --> 00:38:55,577
our listeners to try it out.

699
00:38:55,807 --> 00:38:58,207
Especially it's open source, so
it's so easy to start, right?

700
00:39:00,653 --> 00:39:00,983
Thank you

701
00:39:01,851 --> 00:39:02,361
Michael: Nice one.

702
00:39:02,441 --> 00:39:03,471
Thanks very much, both of you

703
00:39:04,857 --> 00:39:05,617
Peter: Yeah, thanks a lot

704
00:39:06,163 --> 00:39:07,223
Nik: Yeah, have a great week.

705
00:39:08,179 --> 00:39:08,269
Qian: You

706
00:39:08,269 --> 00:39:08,539
too.

707
00:39:08,783 --> 00:39:08,913
Nik: Bye-bye