Ruby Unconf 2018

Petra "Yeah I'll Commit That Later"

Slides:

https://gitpitch.com/stex/petra-rails_demo/master

The mentioned examples and demo application can be found at

https://github.com/stex/petra-rails_demo

Ruby Unconf 2018

00:00:16.770 Okay, I try to reach you with other speakers first, and you can okay in the roof. Perfect! So yeah, I will be talking about Petra today. As you know, in the Ruby ecosystem, you have to find nice, gentle names that are also meaningful. I thought about the concept of transactions and how they are somehow persisted, involving the process of production and so on.
00:00:26.290 I also have a logo design, but unfortunately, I can't draw very well. However, I hope you like it anyway. Now, a bit about my motivation and my background. I have worked with ticket booking engines and publishing platforms in the past and noticed that the same problem comes up quite often: you have to persist some data for the user, but they should only see their own changes.
00:00:45.370 For example, if someone puts an item in a shopping cart, only that user can see the shopping cart; no one else can. Similarly, in WordPress, if I make changes to a post, I can preview it, but no one else can see it yet. So usually, you're building a structure that is essentially a copy of what you already have. You have to make a copy of your post that is only shown to the user, alongside the original post with changes.
00:01:25.390 I eventually thought, isn't there a way to abstract this, so that only I can see the changes I made, but no one else can? That sounds a lot like transactions. Can you raise your hand if you have heard about or used transactions in the past? Okay, quite a few of you are programmers.
00:01:47.219 For those who haven't encountered them, a transaction is essentially a sequence of instructions that can either be completely confirmed and processed or not at all. This ensures that you don't have a half-finished state in your application; everything is either done or not done at all. Inside a transaction, what you do is like Las Vegas: it stays inside the transaction. Until you decide to share the results with the world, no one can see it.
00:02:24.440 This is a classic example taught in computer science. Consider a withdrawal method where you specify a bank account and an amount to withdraw. The program retrieves the account balance, saves it in a variable, and compares this to the amount the user wants to withdraw.
00:03:07.630 You can only withdraw as much money as you have in your account. However, in a Rails application, multiple requests might happen simultaneously, which could mean that while we are checking the balance, someone else withdraws funds from that account. This can lead us to believe we have more money than we do, resulting in a negative balance.
00:04:05.150 This is where atomicity comes into play; we want that entire method to be processed as one unit. You might not even realize it, but if you are using Active Record, everything you do when you save, update, or create records is effectively done within transactions.
00:04:36.230 If you mistakenly assume that your changes are persisted, they actually are not visible to others until you use 'after_commit.' Also, if an exception is raised during a transaction, all your changes are reversed, which can be surprising for developers. So why does Active Record already implement transactions? The answer lies in the fact that it uses the underlying database system, such as MySQL or Postgres. However, Active Record transactions can span multiple requests.
00:05:52.790 So let's take a look at a simple application I created. It's a basic user management system that functions similarly to what you would encounter in a tutorial. Say I want to edit a user’s name and make it a more appropriate name. After making the change, I can reload the page and see it reflected there.
00:06:47.840 But if I open a new tab, that change shouldn't be visible since I am working with transactions that only I can see. The user I added will be visible in this tab to me, but not to a new private window. When I create a new user in one tab, that action is contained in a transaction which is not visible to anyone else until I commit it.
00:08:05.300 After committing the transaction, the changes are made public. So, it’s not particularly magical; you can add all changes to a session and then apply them. What I wanted to avoid was writing this same code every time it’s needed. When I show you the UsersController code, it looks just like the typical CRUD actions: create, read, update, and destroy.
00:09:19.000 The only unique aspect you might notice is the 'petra' method call. So how does this work? The simple answer is that Ruby programmers know a bit about magic, and understanding what happens here is a bit difficult without delving into the Rails framework.
00:10:20.060 When I started building Petra, I wanted it to work on basic Ruby objects, making it easier to understand. I hoped to keep it straightforward, focusing on plain old Ruby objects. We created a simple user class with two attributes and a name function to access the first and last name.
00:11:54.350 When we want to use this user object inside a transaction, we explicitly create a new user named FooBar and place them inside a transaction. By the time we exit the transaction scope, the original name remains until we decide to change it.
00:12:55.660 Now, if I decide to commit this transaction, the changes become visible outside. For example, we could rename the user inside the transaction but still see the original name outside the block until we commit. At that point, the changes are made publicly visible.
00:15:19.800 However, what happens if two users in different transactions are renaming the same object at the same time? It's similar to conflict resolution in Git. If both users make conflicting changes, the application needs a way to determine which change to commit.
00:16:55.210 This is where Petra comes in. It can automatically handle these kinds of conflicts and notify you when another user has made changes while you were working on an object. This way, you can decide how to handle the changes.
00:18:14.000 The system can be set up to handle scenarios where an object changes outside your transaction, ensuring that proper action is taken when conflicts arise. Overall, there's quite a bit of potential for expanding on this idea, especially in real-world applications.
00:19:02.300 I encourage anyone interested in contributing to this fascinating area to check out the project on GitHub and feel free to submit pull requests or issues.
00:19:40.060 Thank you for your time, and if you have any questions, please let me know. Otherwise, I hope you enjoyed it.
00:21:12.700 The question was regarding the scenario where an outside transaction deletes a record that we changed inside our transaction. If the original object doesn't exist anymore, it would indeed pose a conflict. The developer must decide how to handle this situation.