WEBVTT

00:00.000 --> 00:13.360
Alex, we'll now talk about getting to know, look at first pioneers, PouchDB and CouchDB.

00:13.360 --> 00:14.760
Let's welcome him.

00:14.760 --> 00:19.760
All right, hello.

00:19.760 --> 00:21.760
Hi, my name is Alex.

00:21.760 --> 00:26.040
I'm from a company called Neighborhoodie in Berlin, and I'm going to show you PouchDB

00:26.040 --> 00:28.040
and CouchDB.

00:28.960 --> 00:32.680
It's just going to dive straight in and then do something very ill-advised,

00:32.680 --> 00:36.560
namely demo the entire thing live, because I think to chose it very well.

00:36.560 --> 00:40.080
So CouchDB is a non-relational document-based database.

00:40.080 --> 00:43.520
It is extremely good at sinking data back and forth and doing all of the things that

00:43.520 --> 00:46.480
track as generally interested in.

00:46.480 --> 00:49.040
It's built in Erlang and it runs on the server.

00:49.040 --> 00:54.480
PouchDB with a P is the JavaScript implementation of kind of the same thing

00:54.480 --> 00:56.280
and it runs in the browser.

00:56.360 --> 01:05.280
And together, they talk with each other and they're really good at building

01:05.280 --> 01:10.280
sync engine based offline capable collaborative web apps with real-time

01:10.280 --> 01:12.280
ish capabilities.

01:12.280 --> 01:16.280
And yeah, because we think that the barrier to entry is very low and they're very

01:16.280 --> 01:20.280
easy to reason about, we're just going to do the whole thing live right now.

01:20.280 --> 01:23.280
Let's see if that works.

01:23.280 --> 01:29.280
We're going to go to the PouchDB website because on the PouchDB website PouchDB itself is hosted there.

01:29.280 --> 01:31.280
And so we're just going to do what I said.

01:31.280 --> 01:34.280
We're going to get ourselves a PouchDB instance.

01:34.280 --> 01:39.280
So doing things, this angle is very bad for me to see my cursor.

01:39.280 --> 01:42.280
But I think we can make it work.

01:42.280 --> 01:45.280
Okay, so I stupidly just scrolled past that.

01:45.280 --> 01:47.280
But we just got ourselves a PouchDB instance.

01:47.280 --> 01:51.280
It's basically a wrapper around indexDB.

01:51.280 --> 01:54.280
Usually what people like to do with databases is put data in them.

01:54.280 --> 01:59.280
So for this presentation, we're going to put variables into our database.

01:59.280 --> 02:02.280
Everything in Couch and Pouch is JSON.

02:02.280 --> 02:03.280
So keys and values.

02:03.280 --> 02:04.280
Some keys are special.

02:04.280 --> 02:05.280
They have an underscore.

02:05.280 --> 02:07.280
This is predictably the unique ID.

02:07.280 --> 02:09.280
We're only going to have one Badger.

02:09.280 --> 02:10.280
So that's fine.

02:10.280 --> 02:11.280
Put the Badger in.

02:11.280 --> 02:12.280
Sweet.

02:12.280 --> 02:13.280
Now the Badger.

02:13.280 --> 02:17.280
Usually you want to get stuff out again.

02:17.280 --> 02:20.280
There are many ways of getting stuff out of Couch and Pouch.

02:20.280 --> 02:26.280
We're going to do the simplest one and use the get method on our database.

02:26.280 --> 02:29.280
I promise this gets more complex and interesting in a minute.

02:29.280 --> 02:31.280
You just get it by its ID.

02:31.280 --> 02:33.280
Very straightforward.

02:33.280 --> 02:34.280
There's a Badger.

02:34.280 --> 02:35.280
Awesome.

02:35.280 --> 02:38.280
And we can see there are three keys that we just put in.

02:38.280 --> 02:41.280
Food type and ID, but there's a fourth key that we didn't put in.

02:41.280 --> 02:44.280
So this is something PouchDB did for us.

02:44.280 --> 02:46.280
This is a document revision.

02:47.280 --> 02:50.280
So now all you need to know is the document revision has two parts.

02:50.280 --> 02:54.280
In front of the dash is an incrementing integer that tells you the how many

02:54.280 --> 02:57.280
If version of the Badger this is.

02:57.280 --> 03:02.280
So this is the first thing after the dash is the content addressable hash of the document.

03:02.280 --> 03:06.280
Which makes it very easy to compare documents and revisions with each other very cheaply.

03:06.280 --> 03:09.280
We're going to see why that's cool later on.

03:09.280 --> 03:10.280
Yeah.

03:10.280 --> 03:14.280
So you kind of post add multiple animals.

03:14.280 --> 03:16.280
We're going to do that really quickly.

03:16.280 --> 03:18.280
So we have some more things to look at.

03:18.280 --> 03:21.280
So this is a bulk dox thing.

03:21.280 --> 03:24.280
You just put in an array of JSON.

03:24.280 --> 03:25.280
Okay.

03:25.280 --> 03:27.280
These are all.

03:27.280 --> 03:31.280
So every write to Couch and Pouch is the topic.

03:31.280 --> 03:34.280
And when you do multiple of them, they are still handled individually.

03:34.280 --> 03:37.280
So if one of them fails, the others will work, which is nice.

03:37.280 --> 03:41.280
And yeah, sometimes you want to know how many animals you have.

03:41.280 --> 03:48.280
So let's look at the easiest way to get what is my cursor and why.

03:48.280 --> 03:51.280
The easiest way to get data out of it.

03:51.280 --> 03:54.280
And that's with the all dox command.

03:54.280 --> 03:58.280
We pass in an option that says include the dox because we know how many there are.

03:58.280 --> 04:02.280
We know that there are small sometimes you would not want to do this.

04:02.280 --> 04:04.280
And then you get your animals.

04:04.280 --> 04:05.280
Cool.

04:05.280 --> 04:07.280
You can see they all have a rev.

04:07.280 --> 04:08.280
Brilliant.

04:09.280 --> 04:11.280
That covers reading and writing.

04:11.280 --> 04:16.280
So you can also power your user interface with PatchDB.

04:16.280 --> 04:20.280
So Patch and Pouch and Couch both have a thing called the changes feed,

04:20.280 --> 04:23.280
which is basically just an event a meter that emits an event

04:23.280 --> 04:25.280
whenever something changes.

04:25.280 --> 04:28.280
And you can see how you could easily attach that.

04:28.280 --> 04:30.280
If you have a reactive front and you are, for example,

04:30.280 --> 04:33.280
you attach the changes handler to the state management of that

04:33.280 --> 04:35.280
and then decide whether you want to update.

04:35.280 --> 04:42.280
The view, we're just going to add one of those really quickly.

04:42.280 --> 04:46.280
And this is also just a method on our database instance called changes.

04:46.280 --> 04:49.280
We wanted to start now.

04:49.280 --> 04:54.280
We say live is true, which means it will stay active until we turn it off again.

04:54.280 --> 04:58.280
And again, we would like the entire document body inside the change.

04:58.280 --> 05:00.280
Just going to start that.

05:00.280 --> 05:04.280
And to actually trigger this, we now have to do something with the database.

05:05.280 --> 05:07.280
So we haven't done updates yet.

05:07.280 --> 05:10.280
So let's do those next.

05:10.280 --> 05:13.280
You might assume updates work a bit like this.

05:13.280 --> 05:18.280
Oh, I forgot to copy the thing on second.

05:18.280 --> 05:21.280
Work a bit like this where you just do a put again.

05:21.280 --> 05:25.280
You reuse an idea that you know exists and you put in some new keys and values.

05:25.280 --> 05:28.280
And you would be wrong because that doesn't work.

05:28.280 --> 05:29.280
Why does it not work?

05:29.280 --> 05:33.280
So you get a big error message, which is interesting for two reasons.

05:34.280 --> 05:38.280
First of all, there's an HTTP status code in there.

05:38.280 --> 05:43.280
409 conflict, which is curious because we didn't make any network requests.

05:43.280 --> 05:45.280
So why is that there?

05:45.280 --> 05:49.280
CouchDB, like the big one, is an HTTP server.

05:49.280 --> 05:52.280
Every piece of data, every action you can take on the data,

05:52.280 --> 05:55.280
every function of the database has an HTTP endpoint.

05:55.280 --> 06:01.280
And since patchDB is an implementation of couch, it only makes sense that it behaves consistently.

06:01.280 --> 06:05.280
In addition, patch can also be used as a client library for couchDB.

06:05.280 --> 06:09.280
So it just makes sense to do it this way.

06:09.280 --> 06:13.280
The more interesting questions, why is there a conflict?

06:13.280 --> 06:17.280
So couchDB has a concept of history with its document revisions.

06:17.280 --> 06:21.280
So when you make an update, you not only have to specify which ID you want to update,

06:21.280 --> 06:24.280
you also have to specify which state of that document you want to update.

06:24.280 --> 06:28.280
And that's basically what underpins the very awesome

06:28.280 --> 06:31.280
syncing capability of couch and patch.

06:31.280 --> 06:39.280
So if you recall, we already had our badge on.

06:39.280 --> 06:42.280
And our badge already had an idea.

06:42.280 --> 06:49.280
So the easiest way to do this is to just reuse the badge on.

06:49.280 --> 06:55.280
We're going to destructure the poor guy and just override the one key that we want to change.

06:56.280 --> 06:58.280
And that works.

06:58.280 --> 07:01.280
And you can see, first of all, the changes feed fired.

07:01.280 --> 07:08.280
So we could now, if we had a UI, we could now take this change, see if we need to do anything with it.

07:08.280 --> 07:10.280
And update the state.

07:10.280 --> 07:14.280
And you can also see that the document now is at revision two.

07:14.280 --> 07:18.280
And the hash is also changed because the document is changed.

07:18.280 --> 07:20.280
All right.

07:20.280 --> 07:22.280
Now let's sync some data.

07:22.280 --> 07:27.280
We're going to connect the patch DB, which runs in the browser to a remote couch DB,

07:27.280 --> 07:30.280
which is running on this laptop, but you know what I mean.

07:30.280 --> 07:32.280
It could be anywhere.

07:32.280 --> 07:38.280
So couch and patch support true multi-primary replication, meaning you can connect several databases,

07:38.280 --> 07:42.280
and several, I mean, many more than two via replication,

07:42.280 --> 07:44.280
and they'll eventually be consistent with each other.

07:44.280 --> 07:48.280
You can also do partial replication, filter replication as you like.

07:48.280 --> 07:55.280
And the big selling point here is, of course, that these networks can be partitioned.

07:55.280 --> 08:00.280
So the database is still fully functional, and they will sort themselves out once they reconnect.

08:00.280 --> 08:03.280
So let's start a replication.

08:03.280 --> 08:06.280
Real quick.

08:06.280 --> 08:12.280
This replication goes from the local database to a remote one, and back again, it is live.

08:12.280 --> 08:17.280
So again, it stays open until we close it, and it will retry if it goes offline,

08:17.280 --> 08:21.280
which is kind of the secret superpower here.

08:21.280 --> 08:23.280
All right.

08:23.280 --> 08:26.280
First thing that's going to happen is a pile of errors are going to happen.

08:26.280 --> 08:27.280
Why is this?

08:27.280 --> 08:30.280
Because again, couch DB is an HTTP server.

08:30.280 --> 08:35.280
If patch wants to know something about resource that isn't there, it will get a full or full.

08:35.280 --> 08:41.280
That's totally normal, which is why patch DB repeatedly tells us that this is totally normal.

08:42.280 --> 08:44.280
I'm going to hide those.

08:44.280 --> 08:48.280
And now we can also see that the replication has done something.

08:48.280 --> 08:50.280
It's going to push, what does it push?

08:50.280 --> 08:55.280
It is pushed out for documents into the database.

08:55.280 --> 09:00.280
So let's look at those.

09:00.280 --> 09:02.280
This is photon.

09:02.280 --> 09:06.280
This is the admin panel that couch just hosts for you.

09:06.280 --> 09:08.280
And we're already in the correct database.

09:08.280 --> 09:10.280
And if I refresh, we can see.

09:10.280 --> 09:12.280
Let me hit that bigger.

09:12.280 --> 09:13.280
We have a document.

09:13.280 --> 09:15.280
The badger is that revision two.

09:15.280 --> 09:17.280
And we're going to do a change.

09:17.280 --> 09:20.280
So we're going to make the ferret a bit nicer.

09:20.280 --> 09:21.280
I have it.

09:21.280 --> 09:22.280
It can be instead of blood.

09:22.280 --> 09:23.280
I don't know what ferret see.

09:23.280 --> 09:24.280
I'm sorry.

09:24.280 --> 09:30.280
And if you go back to here, you can see that there was a pull.

09:30.280 --> 09:32.280
That's our ferret being pulled.

09:32.280 --> 09:36.280
And the changes feed is also fired with our updated ferret.

09:36.280 --> 09:39.280
And this is nice because we've just specified the changes feed in our browser.

09:39.280 --> 09:42.280
But that change came from somewhere completely different.

09:42.280 --> 09:43.280
And we don't care.

09:43.280 --> 09:45.280
We have one changes feed handler.

09:45.280 --> 09:48.280
It could be our own change on this client could be somewhere else.

09:48.280 --> 09:51.280
All the same thing.

09:51.280 --> 09:52.280
Cool.

09:52.280 --> 09:55.280
Now let's stress this a bit and make a thousand animals.

09:55.280 --> 09:59.280
And we're going to put them directly into the couch.

09:59.280 --> 10:02.280
And with this, I just want to demo that.

10:03.280 --> 10:05.280
Pouchy B can also be a client for a note.

10:05.280 --> 10:09.280
Couchy B.

10:09.280 --> 10:12.280
Oops.

10:12.280 --> 10:17.280
Oh, I just noticed that.

10:17.280 --> 10:20.280
One second, I missed a place to file.

10:20.280 --> 10:24.280
That was dumb.

10:24.280 --> 10:25.280
Oh, no.

10:25.280 --> 10:27.280
I can't find my thousand animals anymore.

10:27.280 --> 10:31.280
That was very unfortunate.

10:31.280 --> 10:36.280
There we go.

10:36.280 --> 10:39.280
There are a thousand animals.

10:39.280 --> 10:46.280
Please don't pay huge arrays of Jason at home.

10:46.280 --> 10:48.280
Point.

10:48.280 --> 10:51.280
And the changes feed fires a bunch of times.

10:51.280 --> 10:57.280
And if we go here and refresh, we can see everything's full of bears.

10:57.280 --> 11:02.280
And that would usually be a problem, but in this case it's what we want.

11:02.280 --> 11:07.280
If we go back here and we check with all docs again how many animals we have,

11:07.280 --> 11:12.280
we will see that they have already been replicated back down into the Pouchy B.

11:12.280 --> 11:14.280
And we have a thousand for animals.

11:14.280 --> 11:16.280
So that's very cool.

11:16.280 --> 11:18.280
Okay.

11:18.280 --> 11:20.280
I have to rush a bit.

11:20.280 --> 11:24.280
Let's go offline and do the really exciting stuff.

11:24.280 --> 11:26.280
Where's my offline button?

11:27.280 --> 11:30.280
We're taking this browser offline.

11:30.280 --> 11:33.280
And we're just going to do some things.

11:33.280 --> 11:36.280
If we weren't offline, for example, we're going to add a new animal.

11:36.280 --> 11:39.280
Because a thousand for animals is nowhere near enough.

11:39.280 --> 11:41.280
We're going to add an auto.

11:41.280 --> 11:43.280
That just works really in.

11:43.280 --> 11:47.280
Let's delete the pangolin on account of it not being furry enough.

11:47.280 --> 11:52.280
So, deletes in cartoon culture.

11:52.280 --> 11:53.280
Interesting.

11:53.280 --> 11:56.280
It's something in a distributed database.

11:56.280 --> 11:59.280
Because once the other databases reconnect,

11:59.280 --> 12:02.280
they will just replicate that thing back into existence.

12:02.280 --> 12:05.280
So you have to keep track of the deletion and the way we do that,

12:05.280 --> 12:08.280
we just market as deleted with a special key.

12:08.280 --> 12:10.280
Deleted true.

12:10.280 --> 12:13.280
And now the pangolin is gone.

12:13.280 --> 12:17.280
And yeah, the changes feed also explicitly tells us that it's a deletion,

12:17.280 --> 12:20.280
which is also quite useful.

12:20.280 --> 12:23.280
But all this really means is that we can really easily build apps

12:23.280 --> 12:26.280
that seamlessly work online or offline without the end user.

12:26.280 --> 12:30.280
Or even us as developers having to care particularly much about the connectivity status.

12:30.280 --> 12:32.280
As long as our code does not talk to the network,

12:32.280 --> 12:36.280
the replication protocol would just do the thing in the background.

12:36.280 --> 12:37.280
Right.

12:37.280 --> 12:38.280
Now, conflicts.

12:38.280 --> 12:39.280
Let's close conflicts.

12:39.280 --> 12:42.280
We're going to do some conflicting offline rights.

12:42.280 --> 12:45.280
Let's see what happens there.

12:45.280 --> 12:47.280
We're going to get a badger.

12:47.280 --> 12:50.280
And we're going to let the badger in the browser

12:50.280 --> 12:53.280
be a ramen noodle efficient order.

12:53.280 --> 12:56.280
And we're going to let the badger in the server.

12:56.280 --> 12:58.280
Remember, these are no longer connected.

13:04.280 --> 13:05.280
Yeah, I'm hurrying.

13:08.280 --> 13:10.280
Yeah, but I'm pretty much on track.

13:10.280 --> 13:11.280
It's all good.

13:11.280 --> 13:14.280
Talking with safe successfully.

13:14.280 --> 13:15.280
Cool.

13:15.280 --> 13:17.280
We have two parallel badgers.

13:17.280 --> 13:19.280
There are now two versions of the same badger,

13:19.280 --> 13:20.280
which is the real one.

13:20.280 --> 13:21.280
Only time will tell.

13:21.280 --> 13:23.280
For now, this is shooting as badger.

13:23.280 --> 13:25.280
They both exist.

13:25.280 --> 13:28.280
So let's reconnect and see what happens.

13:31.280 --> 13:34.280
So the first thing that's going to happen is probably, hello.

13:38.280 --> 13:39.280
Yeah.

13:39.280 --> 13:42.280
So one part of the replication has happened.

13:42.280 --> 13:44.280
So it's pulled to the change from the server.

13:44.280 --> 13:46.280
And in a second, it's also going to do a push.

13:46.280 --> 13:48.280
There's an incremental back off thing happening here,

13:48.280 --> 13:50.280
so it doesn't club up the connection.

13:52.280 --> 13:54.280
Take a few seconds when you leave there.

14:00.280 --> 14:01.280
Come on.

14:03.280 --> 14:04.280
There we go.

14:04.280 --> 14:06.280
Okay, now let's see which badger is real badger.

14:06.280 --> 14:08.280
And then we are near the end of the tour.

14:10.280 --> 14:12.280
So what happened?

14:13.280 --> 14:15.280
The badger has won.

14:15.280 --> 14:16.280
Cool.

14:16.280 --> 14:18.280
Does that mean the ramen badger is now a conscient history?

14:18.280 --> 14:19.280
No.

14:19.280 --> 14:21.280
The ramen badger lives on.

14:21.280 --> 14:23.280
He lives behind the conflict button.

14:23.280 --> 14:27.280
So it can actually be in part should be do not do last right wins.

14:27.280 --> 14:29.280
Because in some use cases,

14:29.280 --> 14:31.280
that is equivalent to just randomly deleting user data,

14:31.280 --> 14:33.280
which sometimes you cannot do.

14:33.280 --> 14:40.280
So we give you as a developer the option of choosing what to do with your conflicts.

14:40.280 --> 14:43.280
You can ignore them, which is effectively last right or wins,

14:43.280 --> 14:46.280
or just random right or wins, because we have no blocks.

14:46.280 --> 14:49.280
Or you can do proper conflict resolution,

14:49.280 --> 14:53.280
which may even mean asking the users what they originally meant.

14:54.280 --> 14:56.280
Okay.

14:56.280 --> 14:57.280
That.

14:57.280 --> 14:58.280
Yeah.

14:58.280 --> 15:05.280
So with many other offline first solutions,

15:05.280 --> 15:09.280
quite a lot of your work will be just getting to this point, right?

15:09.280 --> 15:13.280
You have to add a lot of abstractions between your sync layer and your actual data.

15:13.280 --> 15:17.280
And because a lot of other systems can't handle arbitrary data,

15:17.280 --> 15:20.280
you'll see we don't have a schema, we didn't define anything, right?

15:20.280 --> 15:24.280
With cash and pouch, the conflict handling does not care what your data looks like.

15:24.280 --> 15:27.280
And it's always just human readable Jason.

15:27.280 --> 15:31.280
So it's offline capable, real timey, it sinks, it's conflict aware,

15:31.280 --> 15:34.280
and we did all of that without writing a single line of code.

15:34.280 --> 15:37.280
We only invoked some library methods.

15:38.280 --> 15:41.280
All right. So just do a small recap about cash and pouch.

15:41.280 --> 15:45.280
So the open source Apache Foundation projects with a long history,

15:45.280 --> 15:47.280
nobody's going to sell you anything.

15:47.280 --> 15:50.280
Built on open standards, no lock-in, HTTP and Jason,

15:50.280 --> 15:53.280
if your stack speaks that you're dumb, basically.

15:53.280 --> 15:57.280
True multi-primary replication, human readable state always,

15:57.280 --> 16:02.280
in flight and transport, not operations or transactions being stored,

16:02.280 --> 16:06.280
conflict aware, through and through completely indifferent to your data structure.

16:07.280 --> 16:10.280
No auto resolution magic, you get to decide what to do,

16:10.280 --> 16:12.280
plus loads of other awesome features.

16:12.280 --> 16:14.280
Yeah, we love cash to be in pouch.

16:14.280 --> 16:18.280
We've done a lot of interesting work in the past 10 years all over the world.

16:18.280 --> 16:24.280
Please come talk to me and Jan, the man over there about this.

16:24.280 --> 16:28.280
We enjoy this topic a lot as you may be able to tell.

16:28.280 --> 16:32.280
We're quite friendly and we also have stickers, and that's it.

16:32.280 --> 16:34.280
Those are the URLs. Thank you very much.

16:34.280 --> 16:39.280
Thank you.

16:39.280 --> 16:41.280
Thank you.

16:41.280 --> 16:42.280
Thank you.

16:42.280 --> 16:43.280
Thank you.

16:43.280 --> 16:45.280
You have the tools to remain as far as possible.

16:45.280 --> 16:47.280
I can't see anything, but we have some.

16:47.280 --> 16:48.280
Yes.

16:48.280 --> 16:52.280
There was a second where you had edited in the admin dashboard,

16:52.280 --> 16:56.280
and you went back to the application, or just the console,

16:56.280 --> 16:58.280
and you saw that it was changed.

16:58.280 --> 16:59.280
Yeah.

16:59.280 --> 17:03.280
Why was that was it pulling for this like the changes feed?

17:03.280 --> 17:05.280
It's actually two separate things.

17:05.280 --> 17:08.280
So the replicator we had set up a bi-directional replication.

17:08.280 --> 17:11.280
So any change on the couch to be what automatically

17:11.280 --> 17:13.280
you get pulled replicated into the pouch.

17:13.280 --> 17:17.280
And then the changes feed fired because something changed in the local database.

17:17.280 --> 17:18.280
I see.

17:18.280 --> 17:20.280
So it's like being changed from the same place, basically.

17:20.280 --> 17:22.280
Yeah, in a way. Yeah.

17:22.280 --> 17:23.280
Yeah.

17:23.280 --> 17:26.280
Doesn't really care where the change came from in the head.

17:26.280 --> 17:27.280
Nice.

17:27.280 --> 17:29.280
Some other question.

17:33.280 --> 17:37.280
Is it mainly a key value store, or is there any like transaction

17:37.280 --> 17:39.280
multi-document functionality?

17:39.280 --> 17:41.280
You can build one on top if you like.

17:41.280 --> 17:42.280
It just stores JSON.

17:42.280 --> 17:45.280
I mean, it also stores binary data if you wanted to.

17:45.280 --> 17:47.280
But in principle, it's just JSON.

17:47.280 --> 17:50.280
But we've built a transactional store on top of that as well.

17:50.280 --> 17:52.280
It's entirely possible.

17:52.280 --> 17:53.280
Like a ledger system.

17:53.280 --> 17:54.280
Yeah.

17:54.280 --> 17:55.280
Thank you.

17:55.280 --> 18:03.280
I didn't catch this.

18:03.280 --> 18:07.280
If you don't as the developer resolve the conflicts,

18:07.280 --> 18:12.280
or the multiple clients guarantee to converge on a consistent state.

18:12.280 --> 18:13.280
Yes.

18:13.280 --> 18:16.280
It's always a deterministic even if the separator from each other.

18:16.280 --> 18:17.280
Okay.

18:17.280 --> 18:20.280
Good question.

18:20.280 --> 18:21.280
Good question.

18:21.280 --> 18:23.280
Yes. Absolutely. Good question.

18:24.280 --> 18:26.280
More or good.

18:26.280 --> 18:27.280
Okay.

18:27.280 --> 18:29.280
Alex, thank you very much.

18:29.280 --> 18:30.280
Thank you.

