Plain Old Ruby Object (PORO)

Petra "Yeah I'll Commit That Later"

Petra "Yeah I'll Commit That Later"

by Stefan Exner

In the presentation titled "Yeah I'll Commit That Later" by Stefan Exner at Ruby Unconf 2018, the speaker explores the concept of transactions within the Ruby ecosystem, particularly focusing on the Petra framework designed to easily manage user-specific changes and data persistence. The discussion outlines how transactions allow users to make changes that are invisible to others until they are committed.

Key Points:

  • Introduction to Transactions:

    • Transactions allow a sequence of operations to complete successfully or rollback entirely, ensuring data consistency.
    • The analogy to Las Vegas is used: what happens inside the transaction remains private until committed.
  • Problem Context:

    • Many applications require user-specific changes, such as shopping carts or editing posts where only the current user should see their modifications.
    • Traditional implementations involve duplicating data structures to manage visibility, which can be complex.
  • Active Record and Transactions:

    • Active Record in Rails already employs transactions, but developers might not fully grasp their functionality and implications, such as changes being invisible until the 'after_commit' callback.
    • A common use case involves simultaneous user interactions, like withdrawing from a bank account, where proper transaction handling is crucial to prevent conflicts and negative balances.
  • Petra Framework Introduction:

    • Petra aims to simplify transaction management for Ruby developers by abstracting the complexity and focusing on ordinary Ruby objects.
    • Example application showcases a user management system where changes made by one user are not visible to others until committed, mimicking a typical CRUD application.
  • Conflict Resolution:

    • The potential issue of two users editing the same object in different transactions is addressed, likening it to Git conflict resolution.
    • Petra can automatically handle these conflicts, prompting users when changes outside their transaction conflict with their modifications.

Conclusion:

The presentation concludes with an encouragement for developers to explore the Petra project on GitHub, emphasizing the collaborative nature of its development. The importance of managing user-specific changes in applications efficiently and the potential challenges of transaction conflicts are highlighted as ongoing areas for development.

This talk illustrates how understanding and implementing transaction management can significantly enhance the functionality and reliability of Ruby applications, making it a worthwhile consideration for developers working within the Ruby ecosystem.

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.