Wondered why transactions can carry with them deadlocks and race conditions? Then this video maybe is for you. We are not going to discuss exactly how deadlocks and race conditions occur. Instead we are talking about a way to avoid them completely using Project Arrow's STM. Make sure to follow this video until the end to learn interesting aspects of it and how transactions work in this way. I hope you enjoy the video and I hope you find it useful for your every day transactional endeavors. As usual, until the next video, be sure to stay tech, keep programming, be kind and have a good one everyone!
PS: Given the intense scheduling of the channel and the amount of work, the Wednesdays sessions, under the pilot project, have moved to Thursdays 8PM CET. The reason for this is to see if that scheduling reaches more people given generic availability to watch premieres a on this day.
---
Chapters:
00:00:00 Start
00:00:36 Intro
00:00:39 Software Transactional Memory (STM)
00:02:04 Explaining Software Transactional Memory in Project Arrow
00:02:24 Talking about "Architectural Support for lock-free data-structures"
00:02:58 Back to STM
00:04:23 Another run through "Architectural Support for lock-free data-structures"
00:04:42 Explaining Transaction cases
00:07:55 Optimistic Concurrency Control
00:08:31 Introduction to the example
00:08:44 Talking about the "8 reasons why you should... or should not choose Ktor from a Spring Comparison Perspective"
- https://www.youtube.com/watch?v=S3k6C1XaYr8
00:13:14 Talking about the "8 reasons why you should... or should not choose Ktor from a Spring Comparison Perspective"
- https://www.youtube.com/watch?v=S3k6C1XaYr8
00:14:47 Explaining the requestDocking method - Simple Common Transaction
00:17:32 Talking about past 5 coroutine videos:
- https://youtu.be/LyAOF37cJ-c
- https://youtu.be/DYTbVcGZbH8
- https://youtu.be/lir1-2dpAzA
- https://youtu.be/SstFGOcu3ls
- https://youtu.be/0MJartdpoT4
00:20:24 Explaining the refuel method - Simple Common Transaction Highlighting Conflict
00:23:37 Talking about the Dispatcher.IO quiz video: "Unleashing Kotlin's Potential: Dispatching 100 Coroutines Made Easy"
- https://www.youtube.com/watch?v=7UB7zE3E-f0
00:24:13 Explaining the refuelWithRollback method - Simple Common Transaction with rollback on error
00:32:22 End Notes
00:34:51 See you in the next video!
00:35:39 End Credits
00:36:20 Disclaimer
---
Source code:
- https://github.com/jesperancinha/space-ship-adventures
---
Soundtrack:
- https://soundcloud.com/joaoesperancinha/slow-guitar-13-jesprotech
---
References:
- https://ktor.io/docs/welcome.html
- https://arrow-kt.io/learn/coroutines/stm/
- https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/2005-ppopp-composable.pdf
- https://dl.acm.org/doi/pdf/10.1145/2976002.2976020
- https://cs.brown.edu/~mph/HerlihyM93/herlihy93transactional.pdf
PS: Given the intense scheduling of the channel and the amount of work, the Wednesdays sessions, under the pilot project, have moved to Thursdays 8PM CET. The reason for this is to see if that scheduling reaches more people given generic availability to watch premieres a on this day.
---
Chapters:
00:00:00 Start
00:00:36 Intro
00:00:39 Software Transactional Memory (STM)
00:02:04 Explaining Software Transactional Memory in Project Arrow
00:02:24 Talking about "Architectural Support for lock-free data-structures"
00:02:58 Back to STM
00:04:23 Another run through "Architectural Support for lock-free data-structures"
00:04:42 Explaining Transaction cases
00:07:55 Optimistic Concurrency Control
00:08:31 Introduction to the example
00:08:44 Talking about the "8 reasons why you should... or should not choose Ktor from a Spring Comparison Perspective"
- https://www.youtube.com/watch?v=S3k6C1XaYr8
00:13:14 Talking about the "8 reasons why you should... or should not choose Ktor from a Spring Comparison Perspective"
- https://www.youtube.com/watch?v=S3k6C1XaYr8
00:14:47 Explaining the requestDocking method - Simple Common Transaction
00:17:32 Talking about past 5 coroutine videos:
- https://youtu.be/LyAOF37cJ-c
- https://youtu.be/DYTbVcGZbH8
- https://youtu.be/lir1-2dpAzA
- https://youtu.be/SstFGOcu3ls
- https://youtu.be/0MJartdpoT4
00:20:24 Explaining the refuel method - Simple Common Transaction Highlighting Conflict
00:23:37 Talking about the Dispatcher.IO quiz video: "Unleashing Kotlin's Potential: Dispatching 100 Coroutines Made Easy"
- https://www.youtube.com/watch?v=7UB7zE3E-f0
00:24:13 Explaining the refuelWithRollback method - Simple Common Transaction with rollback on error
00:32:22 End Notes
00:34:51 See you in the next video!
00:35:39 End Credits
00:36:20 Disclaimer
---
Source code:
- https://github.com/jesperancinha/space-ship-adventures
---
Soundtrack:
- https://soundcloud.com/joaoesperancinha/slow-guitar-13-jesprotech
---
References:
- https://ktor.io/docs/welcome.html
- https://arrow-kt.io/learn/coroutines/stm/
- https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/2005-ppopp-composable.pdf
- https://dl.acm.org/doi/pdf/10.1145/2976002.2976020
- https://cs.brown.edu/~mph/HerlihyM93/herlihy93transactional.pdf
Category
🤖
TechTranscript
00:00Thank you for listening.
00:30STM. Software Transactional Memory. What is it? What can we do with it? And how can we use it in our applications?
00:44If we go to Project Error's webpage in arrow-kt.io, we will find six different options that we can choose from.
00:53One of them is Quick Start, then we've got Typed Errors, then Concurrency and Resources, Resilience, Immutable Data with Objects, and Collections and Functions.
01:02In Quick Start, we can have a look at how Project Error works, and how can we quickly start with it and learn how it does what it needs to do.
01:11Then we can choose Typed Errors, and this is a small introduction about the different patterns that we can use in Project Error, and it starts out with very simple patterns.
01:22Then we move on to Concurrency and Resources. These are more specific to Kotlin coroutines, and how can we better manage our Kotlin coroutines in our code.
01:32Resilience is also an interesting one, because it also uses Kotlin coroutines.
01:36And then we've got Immutable Data with Objects. Arrow Optics allows us to work and process data that is deeply nested in different data structures.
01:46And then finally, we've got Collections and Functions that provides extra functionality when we are working, for example, with collections that require different functions for them.
01:56But we are talking about Software Transactional Memory, and that is located in Concurrency and Resources, and then under Transactional Memory, of course.
02:05Now, Transactional Memory, or Software Transactional Memory, is actually a generic concept that has been developed from a long time ago, actually since the 80s and 90s.
02:15And in the beginning of the 90s, there was a document that has been written, or a draft, or a paper that was written by Maurice Helihy and J. Elliot Moss,
02:25called Transactional Memory Architectural Support for Lock-Free Data Structures.
02:31Think about the title, Lock-Free Data Structures.
02:34This is the main point of using Software Transactional Memory, because the idea is that we can avoid the phenomenons that can occur with using locked systems.
02:46Now, if you have been working a long time with transactional systems, especially using the database, you are used to this concept of locks.
02:54And locks always lead us to one specific problem that is called a deadlock, or a race condition, which is a problem that I think many developers, maybe not all of us, maybe a small percentage of us, maybe just some people,
03:10we kind of developed this idea that this is something that we need to deal with when we are working with databases.
03:17And it always comes up when we are doing that.
03:21Well, in this case, transactional memory wants to make sure that we don't have to worry about that.
03:26But there's a difference.
03:28When we are talking about databases, we are intrinsically dependent on transactions that occur in a database.
03:36And therefore, when we couple our, for example, our JPA repository, our CRUD repository, or our Coroutine CRUD repository,
03:43we are dealing with queries that are using the database.
03:46And some of these queries may use a transactional system.
03:49And if they are using a transactional system, they are using the isolation levels of a database.
03:55But transactional memory in this case means that we can deal and work with data and process it in memory,
04:01and only at the end commit that data and see what happens.
04:05Maybe we can persist that data in memory, or maybe not.
04:08But that is what it intends to solve.
04:10And because it's in memory, it leads to the creation of a system that is deadlock-free and race conditions-free.
04:17Now, how does this work, and how is this possible?
04:21Well, the theory is enormous, and if we look at the original document that is available in Brown University,
04:27we can download the whole document and read it, we may understand how it works.
04:31But I want to sum it up to very small lines of text.
04:37Simply what this does is, when we create a transactional system, something like this will happen.
04:43This is what we normally see when we are performing transactions, for example, in a database.
04:49So we've got transaction A and transaction B.
04:51Both of them are performing operations in the database, especially updates or edits or inserts.
04:57Whatever the case, when we are dealing with this, we are potentially also dealing with conflicts.
05:03And dealing with conflicts is something that, if you are used to dealing with databases,
05:06especially if you are working with the Spring Framework, for example, with the JPA, with the Java Persistence API,
05:13then you probably know that you need to deal with things like database isolation.
05:19You need to deal with the levels of the transaction that you want to deal with.
05:22And you also need to deal with mechanisms of pessimistic and optimistic locking.
05:26And the whole reason and the goal is always to avoid deadlocks and to avoid race conditions,
05:34but also to avoid data inconsistencies.
05:38We want to make sure that our data is consistent.
05:41But that is considering a database.
05:44STM solves a different problem.
05:46Potentially it can also solve database problems, but it solves memory issues.
05:50And similarly to how databases work, it also tries to avoid deadlocks and race conditions,
05:58except that in this case it avoids them completely because the mechanism is different.
06:03And how it works is kind of like this one over here.
06:06In this case, we are seeing two different transactions.
06:10One transaction makes a change to the data,
06:14and the second transaction will find that change.
06:18The first transaction will commit, and the second transaction will commit as well,
06:22but it will try to commit to a change that it didn't expect.
06:25And in that case, what will happen is that it will automatically restart the whole transaction
06:30and perform that change for us.
06:33If it doesn't succeed on the second time,
06:35and it does not have to do with changes in the data,
06:38then of course the transaction will also stop,
06:40but in that case there will be no more retries.
06:43You see how it works?
06:44There is no chance of deadlock,
06:46because one of them will have to succeed committing
06:49before the other one can trigger the restart mechanism.
06:54So, and in other cases, we can have multiple retries like this
06:59until we get a result,
07:02until the two transactions can commit together.
07:05And in that case, we've got commits from a transaction A
07:08and commits from a transaction B.
07:10But STM is very interesting in the perspective of code.
07:15How do we implement this, and how do we use it?
07:18And how do we make sure that we don't use this in a wrong way?
07:23And how does it actually work?
07:25Now, these are all interesting questions
07:27that we will try to answer in this video
07:29if we start looking at the example that I created for us.
07:32But before we make a deep dive in our example,
07:35I just want to mention one important thing.
07:38Everything that we've discussed here,
07:40the way that the transactions work,
07:42and how we make sure that they avoid deadlocks
07:44and they avoid race conditions,
07:46that actually has a name.
07:48And the name is called optimistic concurrency control.
07:51And this is a strategy to deal with different transactions
07:54that exist since the 1970s,
07:57since 1979 to be more precise.
08:01This strategy can be used in these kinds of paradigms,
08:05these kinds of patterns,
08:06to make sure that we perform our transactions
08:09in this kind of way,
08:11where we avoid all of these problems.
08:14Now, this works in memory,
08:15can potentially work in databases.
08:18And that's all I wanted to say
08:20before we continue to our example.
08:21So now let's go to our example,
08:23and let's go and make a quick dive
08:25in what I have designed for you
08:27so that we can understand more about this.
08:29So if we go to our project,
08:31which is called Spaceship Parent,
08:33this is available on GitHub
08:35under my handler, J. Ashpen and Singer.
08:38And here we can find our example again in app.
08:42If you saw this video over here,
08:44you know where to find it.
08:45And if not, let me just tell you where it is.
08:48Basically, you just go here to main,
08:49to Kotlin,
08:50and here I've already made some changes to my service.
08:53And I have separated the DAOs from the service.
08:57And now we have in service,
08:58a new service called Docking Service.
09:00And Docking Service is the example
09:02that we are going to have a look at today.
09:04So what I've got in the Docking Service
09:07is something that simulates refueling of spaceships
09:10or docking of spaceships.
09:12We start out with a simple example,
09:14which is the Docking Bay example.
09:16And then we are going to go to the fuel station
09:18to try and fuel our spaceships.
09:22We are going to tank in these stations
09:24for our spaceships.
09:27So the first example is the Docking Service.
09:30Let's first have a look at what it has here.
09:33So Docking Service starts
09:34with two different initializations.
09:36So these two initializations
09:38are creating a Docking Bay and a Fuel Station.
09:41But if you look at this closely,
09:42it's not creating these instances in a normal way.
09:46What it's doing is creating this via Tvar and new.
09:51Tvar means transactional variable.
09:54And that means that we are creating
09:56two different transactional variables
09:58that are the representation of our Docking Bay
10:00and our Fuel Station.
10:02The Fuel Station starts with 100 as fuel,
10:05whatever unit you want to call it.
10:08For the example, it doesn't really matter.
10:09And then the Docking Bay will have place
10:12for one single spaceship.
10:15Now, why are we doing late init var
10:17and not doing a simple val
10:20or simply injecting
10:22or simply creating in a different,
10:24more simpler way that Kotlin provides?
10:27Well, late init var has very specific uses
10:29and this is one of them.
10:31The reason for this is that
10:32if you notice the way initialize is being created,
10:36initialize is being created
10:38using a suspend function.
10:40We are using initialize with a suspend function.
10:43Now you can say,
10:44hey, don't use a suspend function.
10:45It will be much easier.
10:47The problem with that is that
10:48new in this case is a suspend function.
10:52STM in Project Arrow relies heavily on coroutines.
10:57And that is not a surprise.
10:58That is exactly how it works behind it.
11:01Now we are creating transactions in memory.
11:03We are creating memory-based transactions
11:06and these transactions
11:07are highly dependent on coroutines.
11:09This is important to understand
11:11and keep in mind
11:12as we are going through the code.
11:15Now, okay,
11:18but how can this actually fix
11:20the problem of initialization?
11:22For this example,
11:24I am creating a service
11:25that will later be used on a controller
11:27and we are trying to manage state.
11:31That's also another thing that STM provides
11:33is a way to manage state
11:35in a transactional way
11:37in our application.
11:39I mean, there are many different uses for STM.
11:42And so if we need a state
11:44and to keep that state,
11:45think about it.
11:46How would we initialize a global variable
11:48in another way
11:49by using this new?
11:51I mean, it's a suspend function, right?
11:53If I would use here val,
11:54this wouldn't work
11:56because by using val here,
12:01I could potentially use this like this.
12:07But val, in this case,
12:10would accept incoming variables
12:13of type tvar docking bay.
12:16So this would compile.
12:18But the problem is that
12:19if I would then initialize these variables
12:21from here,
12:24and I would do this,
12:25this wouldn't work
12:26because new, of course,
12:27is a suspend function.
12:28And that is not the way
12:29we initialize variables
12:30in our instances.
12:32And so I had to formulate a way
12:35to be able to use global variables
12:38that would keep state in a service.
12:40And this is the way
12:41I decided to do that
12:43with a late init var.
12:44This is relevant to our example
12:47because it has to do with
12:48how would we create a state
12:51in a service for us.
12:53And this is one way to do that.
12:55Now, important is that
12:56this docking service
12:57will be used in controllers
12:58in the future.
12:59It will be used
13:00in Ktor language
13:02in routing.
13:03But it is important for us
13:05also to understand
13:06how the services are initialized
13:08in Ktor using coin.
13:11Now, I've explained this
13:13in that video before,
13:14but important here
13:15is that we go to administration
13:17in this point
13:19or frameworks in this case.
13:22And in frameworks,
13:23we can find
13:23all the different initializations
13:25of the different services
13:26that I have created
13:26for this whole example
13:28of spaceship parent.
13:29But the important one for us
13:31is the docking service.
13:33I want a docking service to work,
13:35but I also want the service
13:37to initialize my variables.
13:39So I implicitly call
13:40that initialize method
13:42over here
13:42by using a coroutine scope
13:44with dispatchers.io
13:46as contacts.
13:47This is the dispatchers
13:48that I decided to use here.
13:50We can choose
13:51any other dispatcher.
13:53It doesn't really matter
13:53at this point
13:54because this is all about
13:55the initialization
13:56of this service.
13:57And then we launch it
13:59and then we simply
14:00just initialize
14:02a docking service
14:03over here.
14:05We initialize
14:06our variables
14:07and then in the end
14:08we create
14:08our single instance,
14:11our singleton effectively,
14:13that we'll be able then
14:14to inject in our application
14:16by giving it the type
14:18and putting here
14:19the actual service
14:19we want to make available
14:21in the whole of our application
14:22using coin.
14:24But we are not going
14:26to inject anything here.
14:27We are simply just going
14:28to perform unit tests.
14:29But before we get
14:31to the unit tests,
14:33now we need to further
14:34continue with the example
14:35now that we understand
14:37why we did this
14:38and what this actually is.
14:40So we've got
14:41transactional variables here.
14:43And transactional variables
14:44can only be handled
14:45under an atomically scope.
14:48Atomically in this case
14:49means exactly
14:50the transaction.
14:52We are creating
14:53a transaction
14:54with atomically
14:55and this will simply
14:57run this example here
14:59and it will make questions.
15:01So for example,
15:02when we read
15:03that the bay
15:04is not occupied,
15:06we can then
15:07put our spaceship.
15:08But this will happen
15:09at the same time
15:09for transaction 1 and 2.
15:11If we launch
15:11two different transactions
15:12calling this method,
15:15we'll make sure
15:17that one of them
15:18will get the bay
15:19not occupied
15:20but the second one
15:21will probably get it
15:23that it's occupied
15:23because look at
15:24what it does here.
15:25It will write
15:26that the docking bay
15:27is occupied.
15:29But here's the thing.
15:30They will run
15:31all the way
15:32up to here.
15:34What will happen
15:35and we will not see
15:35this in the code
15:36very clearly
15:37is that
15:38it will try to dock once
15:40and it will work
15:43the first transaction
15:44and the second transaction
15:45will try to dock once
15:46it will fail
15:47and on the second time
15:48it runs
15:48it will say
15:50spaceship must wait
15:51docking bay
15:52is occupied.
15:53So let's look at
15:54the unit test
15:55that calls this method
15:57and test it
15:58if it works.
16:00So if we go here
16:00to test
16:01and go to Kotlin
16:02and go to our test
16:04for the docking service
16:05this one over here
16:06at the top
16:07we will find
16:08should succeed
16:10request docking.
16:11This is a test
16:11that will simply create
16:12a test application
16:13for us.
16:14It will load
16:14the whole module
16:15of the application
16:16it will make
16:16everything available
16:17for us
16:17and it will allow
16:19us to use the service
16:20that will be
16:20injected here
16:21yes by inject
16:23but not via coin
16:24this inject
16:25is via coin test
16:27so it's a subset
16:29of coin
16:30that allows us
16:31to make unit tests
16:32using the injection
16:33framework.
16:35So in this example
16:37we are launching
16:38two different services
16:39I'm calling
16:40two different spaceships
16:41here
16:42with two different names
16:43of bridges
16:43around Lisbon
16:44there is the 25th of April
16:46bridge
16:47and there's the
16:47Vasco da Gama bridge
16:48that we used
16:50to cross the
16:50Tagus river
16:51or in portuguese
16:52Teju.
16:53That's just a curiosity
16:54of the video
16:55but anyways
16:56we are launching
16:56two different spaceships
16:57that have different names
16:58that's the point
16:59we launched them
17:00and we are trying
17:01to dock
17:01one of these spaceships
17:03to the port.
17:04Now I'm not doing
17:04any assertions
17:05in this test
17:06what I do want
17:07to see is the log
17:08so let's now
17:09just run this
17:09and see what happens.
17:11Before we do that
17:13important
17:13is that we realize
17:14that we are launching
17:15all of this in parallel
17:16with the dispatcher's
17:17IO context
17:18in a coroutine scope.
17:20So this is important
17:21we are launching
17:22in parallel
17:23if you are interested
17:24in learning
17:25about coroutines
17:26and you want
17:27to understand
17:27this a bit more
17:28I sincerely advise
17:30you to have a look
17:31at my five different
17:33videos about coroutines
17:34which start from here
17:35all the way
17:36up to here.
17:37This is a whole course
17:38that I'm doing
17:39about coroutines
17:40it keeps evolving
17:41and it has more
17:42and more episodes
17:43make sure to watch
17:43the playlist
17:44I will put the link
17:45in the description
17:45make sure to read that.
17:46So
17:47now we just want
17:50to run our test
17:51and see what happens
17:51when two spaceships
17:52try to dock
17:53in the same port.
17:55Let's see what
17:56happens
17:57we will run this
17:58the test
18:00should run successfully
18:01because I'm not
18:01doing any assertion
18:02and I also don't expect
18:04any error to occur
18:05but what it is important
18:06is that we read the logs
18:07and understand
18:08a very basic principle
18:09around here.
18:11We are saying that
18:12Vasco da Gama is docking
18:1425th of April is docking
18:16now the two spaceships
18:19are trying to dock
18:20at the same time
18:20it says
18:21the two of them
18:22docked successfully
18:23but the 25th of April
18:25must wait
18:26because the docking
18:27may is occupied
18:28what happened here
18:30is that both of them
18:31tried to dock
18:31let's put this in parallel
18:34so that we can
18:35understand this more clearly
18:37if I go now
18:38to the docking service
18:39and look at the example
18:40we can see that
18:42all right
18:43so in this case
18:45Vasco da Gama
18:46docked successfully
18:47this one
18:48is the first one
18:49that was able to dock
18:50to write
18:51that our docking bay
18:52is occupied
18:53now
18:54because it didn't
18:56commit it yet
18:5625th of April
18:58also says
18:58hey
18:59it's successfully docked
19:01but the problem
19:02is that the commit
19:03occurs right here
19:04at the end
19:05here
19:06when we leave
19:07the scope
19:07of
19:08the atomically
19:09when we leave
19:11that scope
19:12both of them
19:12will try to commit
19:13and what happens
19:14is that
19:14the first successful
19:15one will be the winner
19:16and the other one
19:17will lose
19:18and when it loses
19:19it will try again
19:20and when it tries again
19:21it will see that
19:23hey
19:23it is occupied
19:25so
19:26that means
19:27that
19:28it will print
19:29spaceship
19:30must wait
19:31docking bay
19:31is occupied
19:32and that's what
19:32we see here
19:3325th of April
19:34must wait
19:35docking bay
19:36is occupied
19:36so
19:38this is a basic
19:39example
19:39that tell us
19:40what is going on
19:42in this case
19:43when we are
19:44trying to run
19:45two different
19:46transactions
19:46on the same target
19:47when we are
19:49reading
19:49from
19:50an object
19:51here
19:52we are just using
19:53a simple instance
19:54of an object
19:54that is running
19:55in our application
19:56and we are
19:57with
19:59the t var
20:00measuring
20:01changes
20:02that are occurring
20:03to it
20:03essentially
20:04in this case
20:05we are just
20:05essentially
20:06seeing if it has
20:07been replaced
20:08or not
20:08so the docking bay
20:09has it been replaced
20:10has it not been replaced
20:12and if it has
20:13then the other
20:15transaction will know
20:15and fail
20:16if a change has occurred
20:18and he tries to commit
20:19to the same target
20:20so this is
20:22the first example
20:23the second example
20:24is refuel
20:25now refuel
20:26let me just put it
20:27in the whole screen
20:28so that we can have a look
20:29at what it is
20:31more exactly
20:32we still have
20:33the atomically here
20:34and we are
20:35here running
20:36one transaction
20:37so every time
20:38we call this method
20:39we are calling
20:39a transaction
20:40that will operate
20:41on the fuel station
20:42object
20:42this one over here
20:43that we saw
20:44at the top
20:44and it's now
20:45a t var
20:46fuel station
20:47so this generic type
20:50is now a t var
20:51fuel station
20:51now
20:52the first thing
20:53it does
20:53it reads
20:54the fuel station
20:54and then
20:55if there is
20:57enough fuel
20:58then it will
21:00refuel the ship
21:02and it will
21:04try to write
21:05and update
21:06the fuel station
21:07and then
21:08it will say
21:09space ship
21:10has been
21:10successfully
21:11refueled
21:12if not
21:13then the space ship
21:14will fail
21:15then it will not
21:16be able to refuel
21:17so this is
21:19a simple example
21:20which is essentially
21:21is some repetition
21:22of what we saw
21:23before
21:23let's have a look
21:24at what happens
21:25when we run
21:26the test
21:26so if we run
21:28this test
21:28which is this one
21:29over here
21:30just very quickly
21:32we are refueling
21:33in the same way
21:34as we did before
21:34but we are refueling
21:36with the first
21:37space ship
21:3725th of April
21:38with 50
21:40and then we are
21:41refueling
21:41Vasco da Gama
21:42with 100
21:43now both of these
21:45cases can be successful
21:46because we are
21:47launching them
21:47in parallel
21:48and that means
21:48that when I'm
21:49making this assertion
21:50over here
21:50to find out
21:51how much fuel
21:52is left
21:53on the space ship
21:54I want to know
21:55if it's 0
21:56or 50
21:57anything other
21:58will be a failure
21:59because if
22:00Vasco da Gama
22:01is the first one
22:02then that means
22:03that all the fuel
22:04will be taken out
22:05of the fuel station
22:06and if not
22:07then only 50
22:08will be taken out
22:09and we will get 50
22:10in the end
22:10so let's see
22:11what happens
22:12in our logs
22:13because this will run
22:14also successfully
22:15but we will be able
22:16to see the behavior
22:16in our logs
22:19so when we
22:20have a look at here
22:21further down the line
22:23we see that
22:24the 25th of April
22:25spaceship
22:27or bridge
22:28is refueling
22:29with 50 units
22:30and Vasco da Gama
22:31is refueling
22:32with 100 units
22:33they all try
22:33at the same time
22:34now
22:3525th of April
22:37successfully refueled
22:39remaining fuel 50
22:40Vasco da Gama
22:41successfully refueled
22:42remaining fuel 0
22:44of course
22:44this is before the commit
22:45so we get
22:46all the different logs
22:47as if the two
22:48spaceships were successful
22:49except that
22:50when the commit occurs
22:52the first one to commit
22:54in this case
22:54was the 25th of April
22:56and that means
22:57that Vasco da Gama
22:58will have detected
22:59this error
23:00and we will try
23:01to run the transaction
23:02and upon the first retry
23:04it will find out
23:05that there is no longer
23:07fuel available
23:08and it will fail
23:09now these logs
23:10are here only for tests
23:11this is not something
23:13that you should do
23:14in production
23:14this is just simply
23:15a way
23:16that we have to visualize
23:17what is happening
23:18in the background
23:19and remember
23:19that these two
23:21different calls
23:21to our method
23:23in our service
23:23the refuel
23:25will have been made
23:27in parallel
23:28in this way
23:28with a coroutine scope
23:30using context
23:31dispatchers.io
23:33and if you want to learn
23:34more about dispatchers.io
23:36there is this old video
23:38that I did a while ago
23:39that explains really well
23:40what dispatchers.io are
23:42and how they can be used
23:44this is just a response
23:46to a quiz
23:46I sincerely advise you
23:48to watch the quiz
23:49and watch the answer
23:51to the quiz
23:51that I give in this video
23:52because it is something
23:54that if you are not
23:55very used to
23:56using coroutine scopes
23:58and coroutine contacts
23:59then perhaps the video
24:00will help you better
24:01understand how they work
24:03but in any case
24:04let's have a look
24:05at the last example
24:06of this video
24:06and the last example
24:07of this video
24:08is called
24:08refuel with rollback
24:09refuel with rollback
24:11is an interesting one
24:12because if we go here
24:13inside of it
24:14we will get
24:15the same method
24:18as before
24:18the refuel
24:19except that right now
24:20I am simulating a failure
24:22and this is what
24:23I've mentioned before
24:24if there is no conflict
24:27and there is still an error
24:28then that means
24:29that the commit
24:30does not occur
24:31and that seems logic
24:32but that's something
24:33that we want to see
24:34if it actually works
24:35that way
24:36and so
24:37what we are going to do
24:38is
24:38if we are requesting
24:41fuel of 20
24:42then it will fail
24:44now this is just
24:45an implicit way
24:47of creating an error
24:48we're just simulating
24:49an error
24:50it doesn't mean
24:50that has to be 20
24:51it's just a way
24:52of throwing an illegal
24:52state exception
24:53and then just saying
24:55spaceship encountered
24:56a system failure
24:57all right
24:58so
24:59let's see
25:00what this does
25:01it's then
25:03continues to say
25:05spaceship successfully
25:06refueled
25:07remaining fuel
25:08something something
25:09which is the fuel
25:10of the spaceship
25:11and the same thing
25:12if it fails
25:13then it cannot refuel
25:14now
25:15if there is a failure
25:16here
25:16and our test
25:18looks like this
25:19and we've got
25:23over here
25:24start of atomic
25:25transaction
25:25now here's an exercise
25:27for you
25:28how many times
25:29do you think
25:30we will see that log line
25:31start of atomic
25:33transaction
25:33in our logs
25:36what is the likely
25:38number of times
25:40that we are going
25:41to see
25:41that start
25:43of atomic transaction
25:45in our logs
25:46so let's see
25:47we are launching
25:48one coroutine
25:50that will call
25:52refuel with rollback
25:53from spaceship
25:5425th of april
25:55at this point
25:56you already
25:57notice that
25:59the name
25:59doesn't really matter
26:00for these algorithms
26:01but what does matter
26:03is the requested fuel
26:04with 50
26:05so it will take 50
26:06out of the remaining fuel
26:08there's another one
26:09which will take 100
26:11and there's another one
26:13that will take 50
26:14in this case
26:15no error is going
26:16to be thrown
26:17there will be no failure
26:19and this will be successful
26:22and I'm saying here
26:23that the fuel
26:24should be 0
26:25the reason for this
26:26is that
26:27whatever combination
26:28we decide to do here
26:29the result of the fuel
26:30will always be 0
26:31because if this one
26:32is successful
26:33this one will fail
26:34and this one will be successful
26:35which means that
26:3650 and 50 is 100
26:37will be 0
26:38if this one is successful
26:40it will be immediately 0
26:41and these two will fail
26:42and if this one is successful
26:44then we could have
26:46this one as a success
26:47or this one as a success
26:48but this one will definitely fail
26:51and this one will be successful
26:52making a collection of 50 plus 50
26:55and there will be 100 as well
26:56which will take
26:57all the fuel
26:59out of the fuel station
27:01but
27:02important
27:03how many times do you think
27:04that log is going to appear
27:06so
27:07in all of these cases
27:09we found that
27:10at least one fails
27:12and there could be two
27:16that fail
27:17if this one is the first one
27:19to go in
27:20and if
27:24two of them
27:25can fail
27:26then that means that
27:28both of them
27:30will try
27:31to perform again
27:33the transaction
27:34so that's now
27:35one two three
27:37plus two
27:37that's five
27:38if
27:41this one is successful
27:43this one is successful
27:44and this one fails
27:45then we will have
27:47two successes
27:49one that restarts
27:51so that means that we've got
27:53three already
27:54but one that restarts
27:55so that means a total of four
27:56let's see if we get
27:58five or four
27:59or something else
28:02if we check our logs
28:04we will see
28:06one two three
28:09four
28:15five
28:16six
28:17so how do we get
28:20six
28:21let's have a look
28:21at the logs
28:22so this one
28:25was successfully refueled
28:27this one successfully refueled
28:28and this one successfully refueled
28:29so that means
28:30all three of them
28:31did the operation correctly
28:36in the transaction
28:36and then what's going to happen
28:38is that
28:3825th of April
28:40attempting to refuel
28:41this one has been successfully
28:45so that means
28:46there has already been
28:47a retry here
28:48and then the other
28:50one failed
28:53Vasco da Gama failed
28:54Vasco da Gama failed
28:56the reason for this
28:58is that
28:59what we missed
29:01is that
29:03when we are making
29:05this test
29:06over here
29:07this one was successful
29:12this one tried to run
29:14it failed
29:15it tries again
29:16this one
29:17was successful
29:19this one tried to run
29:20and fails
29:21again
29:22and then
29:24it will retry
29:25and only then
29:26it will definitely fail
29:27because then there are no changes
29:29anymore
29:29to our memory
29:31so that means
29:33that we've got here
29:34for this one specifically
29:35two extra ones
29:36then we have three
29:37just the normal of running
29:39and then we also have
29:40this one
29:41which of course
29:42when trying to change
29:43the state
29:44of our fuel station
29:45it found that there has been
29:46changes occurred
29:47in the database
29:48and so it ran again
29:50so that means
29:52that you get
29:52six different logs
29:55of start
29:56of atomic transaction
29:57for this example
29:59let me just
30:00fix one thing here
30:02so that we can read
30:02this better
30:03okay
30:05so then we can see
30:07this in different lines
30:08for the last example
30:10now the last example
30:11the last of the last
30:12I know I said
30:13that that one
30:14was the last example
30:14but that was not
30:16the last example
30:17the last example
30:18is this one over here
30:19where we are going
30:20to test the exception
30:21that we want to throw
30:22when everything fails
30:24now here is an important thing
30:26when we run this
30:27we are also running
30:28in parallel
30:29there will be now
30:30a failure
30:31and now I want to ask you
30:33how many times
30:34do you think
30:34we are going to see
30:35that log
30:36that says
30:37start of atomic transaction
30:41let's run this test
30:44and see what happens
30:45if we go further down
30:56in the logs
30:57we will see
30:58that
30:59only two of them
31:02have occurred
31:03but it's still two
31:05so what happened
31:07with these two
31:08different logs
31:09it means that
31:10this refuel with rollback
31:14has been called
31:15two times
31:16the transaction
31:18inside of it
31:18has been called
31:19two times
31:20so this only appeared
31:23two times
31:23how can that be
31:25it's very simple
31:27the first one
31:29was successful
31:31so we took 30
31:32out of the total fuel
31:33and the second one
31:35requested 20
31:37but we know
31:38that in our
31:39simulation
31:3920
31:40means an exception
31:42it means
31:43an illegal state exception
31:44spaceship
31:45encountered
31:46a system failure
31:47and so
31:50in that case
31:51when we
31:54finally
31:55get to the end
31:56of this runtime
31:57we will get
31:5870
31:59and we will only get
32:00two logs
32:01because an exception
32:02means that
32:02the transaction
32:03gets immediately
32:04terminated
32:05and so
32:10this
32:10is only a small bit
32:12on how can we use
32:14STM
32:15in our code
32:16with Project Arrow
32:17and in Kotlin
32:19but there are a lot more
32:21things that we can do
32:22with Project Arrow
32:22if we go here to the
32:24web page of
32:24transactional memory
32:25in STM
32:27then we can find
32:28a lot of different text
32:30explaining even an example
32:31with
32:32in this case
32:33bank accounts
32:33and it will tell us
32:35how to use this
32:36in different ways
32:36now
32:37to be able to have
32:38used this in Ktor
32:39as we have seen
32:40just now
32:41I had to explore a lot
32:43and see how this works
32:44now there are
32:45other examples
32:46that I really want to do
32:47in a next video
32:49and these are
32:49other
32:50types
32:51that we can use
32:52that are
32:53transactionally safe
32:54and that we can use
32:56anatomically
32:56and we will do that
32:58in a coming video
32:58but it's important
33:00that we understand
33:00that the other types
33:02that we have
33:02that are available
33:03for us
33:04are this one
33:04TQ
33:05TMVAR
33:06TSET
33:07TMAP
33:08TARRAY
33:08and TSEMOPHORE
33:10how are they useful
33:12for us
33:12we will see later
33:13it's not important
33:14for now
33:15what's important
33:15for now
33:16is that we understand
33:17that STM
33:18is a way
33:19that we have
33:20at our disposal
33:21that works
33:22in a different way
33:23that transactions
33:24work in spring
33:25even if we don't
33:27use them
33:27with databases
33:28which
33:29surprise
33:29if you didn't know
33:30now you know
33:31that we can use
33:32those transactions
33:34also for in-memory
33:36entities
33:37but here
33:39we use this
33:40in a completely
33:41independent way
33:42and in the form
33:42of a library
33:43not a whole framework
33:44but a library
33:45which can be very handy
33:47if we are using
33:48alternative things
33:49to spring
33:50like for example
33:51KTOR
33:51or other kinds
33:52of frameworks
33:53it is completely
33:54independent
33:55and you can use it
33:55as well
33:56in a simple
33:57Java application
33:58you don't need
33:59to inject it
33:59you can just simply
34:01do something with it
34:02you can do it in Kotlin
34:03and then you can compile it
34:04to run
34:05as a Java application
34:06don't confuse
34:08when I say
34:08Java application
34:09with Java code
34:11remember that
34:13everything that we compile
34:14in Kotlin
34:14is still Java
34:16we still run a jar
34:17and we still
34:18run it in the JVM
34:19the Java virtual machine
34:21alright
34:22so this was the video
34:23about software
34:24transactional memory
34:25I thought this was important
34:27that we have a discussion
34:29about this
34:29these are things
34:30that have been developed
34:31through the years
34:32they start out
34:33in 1979
34:34with the optimistic
34:37concurrency control
34:39and then at the end
34:40of the 1980s
34:41begin 1990s
34:42we've got different scholars
34:44that then start
34:45implementing
34:45the first strategies
34:46and philosophies
34:47around software
34:48transactional memory
34:49if you enjoyed this video
34:52make sure to give us a like
34:53make sure to subscribe
34:54to this channel
34:55because I'm posting
34:55a lot of new videos
34:56in the coming period
34:57and make sure
34:59that you read
35:00the description
35:00so that you get
35:01well informed
35:02about the topics
35:02that we discussed
35:03here in this video
35:04and perhaps even read
35:05the different documents
35:07that I will post
35:07the links there also
35:08for you to read
35:09if you are interested
35:10and if you have
35:11the time for it
35:12and the patience
35:13for it of course
35:13because those are
35:14big essays
35:15and big abstracts
35:16and make sure
35:17to leave a comment
35:18I really appreciate
35:18the comments
35:19to my videos
35:20I always try to
35:21answer back
35:22and reply
35:22and react to them
35:23and make sure
35:24that we establish
35:25a community
35:26a just protect community
35:27and until the next video
35:29be sure to stay tech
35:32keep programming
35:33be kind
35:34and have a good one
35:36bye
35:53as a short disclaimer
36:22I'd like to mention
36:23that I'm not associated
36:24or affiliated
36:24with any of the brands
36:25eventually shown
36:26displayed
36:26or mentioned
36:27in this video