Inventory App Part 1. Software Transactional Memory for Scala. Comparing Activate with Slick

Inventory App Part 1. Software Transactional Memory for Scala. Comparing Activate with Slick

Inventory App Part 1. Software Transactional Memory for Scala. Comparing Activate with Slick

What is STM?

Wikipedia defines it as follows: software transactional memory (STM) is a concurrency control mechanism analogous to database transactions for controlling access to shared memory in concurrent computing. It is an alternative to lock-based synchronization. Hmmm… but what does it mean in practice?

From a programmers perspective STM is a way of handling concurrent reads and writes to a shared memory. This technique was inspired by relational databases. As we all know most of these dbs have ACID guarantees, which means that their operations are

  • Atomic – transactions are done or aborted as a whole, they can’t be half-way done
  • Consistent – after each transaction the data is left in a consistent state, it doesn’t matter whether the transaction ended with a success or was aborted
  • Isolated – two different operations won’t interfere with each other
  • Durable – changes done by successful transactions are visible after commit

As you can see the key thing here is transaction. A transaction bundles operations together and is applied in an orderly fashion to the rows in the db. STM brings those concepts to the code. No more threads racing to access shared memory. With STM all modifications have to be done via transactions so the ACID properties hold for our concurrent code.

You still might think “why should I care? I don’t have any shared mutable state in my Scala programs”. In some sense most of us have a mutable state … we call it a database.

So where does Activate fit here? Activate is build on top of RadonSTM (which brings STM to Scala) and aims to simplify the persistence layer by applying the STM approach to data handling. It might seem really confusing at the beginning, but trust me you will quickly get it after looking on some code samples.

Activate components explained

Now I will guide you through the process of creating a simple repository class using Activate. In case you get lost take a look at the source code here.

Connecting to db

First of all you need to add the needed jars to your project. In sbt you can do this as follows:

https://gist.github.com/pjazdzewski1990/29531a9388e4c146e74e

[InventoryBuild.scala]

Having that in mind let’s define the connection. If you expected a long and complex connection procedure I have to disappoint you. Most of the config is done via plain classes like this

https://gist.github.com/pjazdzewski1990/bebe3fcb730e7773e4c4

[ActivityContext.scala]

InventoryContext defines an in-memory db ideal for our needs.

Schema

In Activate most things are done by code. It’s not different with the schema. Here’s the classes we will use:

https://gist.github.com/pjazdzewski1990/d9a8e9e68d28bf011f88

[Domain.scala]

Item holds all the data. ItemEntity is our gateway between application and database. Why bother to create two classes instead of one? We don’t want to leak persistence information outside of the persistence layer. HTTP API or other parts of the app don’t have to know we use Activate, that’s why when passing data outside of the persistence layer we will convert them to case classes first.

Notice how we defined fields here. Some fields are vals, but one is a var. The idea here is to demonstrate a feature that Activate supports – read-only columns. Since name and code are immutable, we can’t modify them in code. Since we don’t modify them in code Activate won’t update the underlying columns.

As a small bonus codeMustBeUnique – uniqueness constraint on one of the fields.

And this the contract for our repository

https://gist.github.com/pjazdzewski1990/98583ff7f43f8bada5f1

[Repositories.scala]

You might have noticed two things here. Extensive usage of OfficeId and Try classes. The reason on why I did that is the same … type of safety. OfficeIdis simple wrapper around an UUID String. So we can’t use our API methods with any Strings, but only those that really represent an entity. It doesn’t look like much of an improvement here, but in larger projects, it’s very, very helpful. I also used Try many times in my code.

The reason behind that is I would like to signal errors in a simple and readable manner without a lot of boilerplates. We don’t have to worry much about error handling here. The API layer will translate Failures into 404 and 500 responses.

Migration

Now let’s prepare the db to use it with the app. In order to use that we will need to run migration to setup all the tables and fields. This part is also very straightforward

https://gist.github.com/pjazdzewski1990/9fac7c1ac344b2fd266b

[SchemaMigration.scala]

As you can see nothing out of the ordinary here. We define a “setup” function and add a timestamp defining execution order for this migration.

Uff that was a bit of code.

Working with Activate

Having all the code in place we can start doing some real work. Let’s define ActivateRepository. The code goes roughly as follows:

https://gist.github.com/pjazdzewski1990/9d0373424c0cd30f94f1

[ActivateRepository.scala]

Let’s dig a bit in the code. The simplest to explain is probably createItem. It leverages the power of Activate. In fact if you look closely it simply creates a new ItemEntity object. The magic happens behind the scenes. Activate propagates the data we put into the instance to the db. We work with plain objects, Activate does all the heavy lifting (as long as we remember about the transactional block).

Simple as that. This nice model frees you of thinking about the backing db details and helps you to concentrate on code.

Having that in mind understanding the rest is easy. Although updateItemlooks intimidating it uses the same trick, but this time we need to load the original record, load the new data and update the item with it. You might wonder about mapcanonical and flatten, but they are just noise to keep the types in check.

readItem and deleteItem are a bit different. To make use of them we will use Activate’s type safe query mechanism. Here’s an example:

https://gist.github.com/pjazdzewski1990/9c3c35ee036eed5b9956

As you can see the return and the query itself are typed. We make usage out of it when comparing (Activate defines operators starting with :, like :== for comparison). Framework defines a lot more features than that. You can explore them all in the docs.

Comparing Activate with Slick

We can’t simply compare Activate and Slick. Although they both “live” in the persistence layer, they don’t aim to do the same thing. In my opinion Slick tries to be only a functional-relational mapper from relational databases to code, where Activate is a fully featured framework for persistence. Despite those differences I would like to share with you some thoughts that came to my mind when implementing the same repository with Slick.

Migrations

Slick doesn’t help us much in this case. Although we have access to TableQuery.ddl we need to come up with a scheme for using this on our own. Activate on other hand provides us with a standard way of doing schema versioning using Migration base class. It’s all done in code. It reminds me of evolutions in Play framework.

Table definitions

In Slick when modeling a table you have to define a Table[T] class. It has to contain definitions for all the columns and metadata. This Table class is the backbone of type-safe query model. Although handy, it might be a bit confusing at the beginning. You have to remember to fill the Tuple with all the column types, define the * projection, and add metadata (like O.AutoInc).

A lot for a beginner … but after you get a grip on Slick it gets natural. For Activate Entityis the table. Every single class extending Entity corresponds to a db table and every instance of that class to a row. No special syntax is required. In case you need to add meta information to the table, you can add it as a function in the class. Neat.

Querying

When it comes to querying, Slick gives us the TableQuery class. The part of Slick queries I really like is the fact, that they are backed by objects, so you can pass them around and compose them easily. Activate on the other hand relies on functions. You pass a function to query and the database gets queried. I feel like in this case functions provide us with less flexibility than objects.

Although I haven’t used them in my examples, you can also use forcomprehensions to query the DB. Slick uses them extensively. In Activate you can use them too. Just take a look at this extension. In fact, you don’t have to choose Activate or Slick, you can use them in combination.

Session handling

Both Activate and Slick provide us with means for managed and automatic session handling. Ad hoc sessions are handled by withDynSession in Slick and in Activate the default transactional block creates one when needed. In both libraries sessions are objects, so it can be created and passed around when a managed solution is needed.

Speed (of writing)

Slick comes with a price that you have to pay at the beginning of the development when defining your base classes. Activate skips this part almost completely, hence it feels faster to write.

Speed (of execution)

At this point, I can’t tell you much about it. Can’t wait to do some benchmarks … When the results are available I will make sure to put them on our blog.

The full Slick example is available on Github.

Lessons learned and opinions

To sum up. To me Activate looks really promising and I can’t wait to test it in a bigger problem. I really liked the simplicity of Activate and how it removed the boilerplate from my code.

Notable mentions – interesting things that didn’t make it to this post but Activate offers:

  • Async Queries, Non-blocking support
  • Constraints and Invariants
  • Lifecycle Callbacks
  • Polyglot Persistence

What is your take on STM and Activate? Did you work with it? Do you have it running in production? Let me know in the comments.

Links

Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.

See also

Download e-book:

Scalac Case Study Book

Download now

Authors

Patryk Jażdżewski

I'm a software consultant always looking for a problem to solve. Although I focus on Scala and related technologies at the moment, during the last few years I also got my hands dirty working on Android and JavaScript apps. My goal is to solve a problem and learn something from it. While working with teams I follow "The Boy Scout" Rule - "Always check a module in a cleaner state than when you checked it out". I think this rule is so good, that I extend it also to other aspects of software development - I try to improve communication patterns, processes and practices ... and all the things that might seem non-technical but are vital to success.

Latest Blogposts

17.04.2024 / By  Michał Szajkowski

Mocking Libraries can be your doom

Test Automations

Test automation is great. Nowadays, it’s become a crucial part of basically any software development process. And at the unit test level it is often a necessity to mimic a foreign service or other dependencies you want to isolate from. So in such a case, using a mock library should be an obvious choice that […]

04.04.2024 / By  Aleksander Rainko

Scala 3 Data Transformation Library: ducktape 0.2.0.

Scala 3 Data Transformation Library: Ducktape 2.0

Introduction: Is ducktape still all duct tape under the hood? Or, why are macros so cool that I’m basically rewriting it for the third time? Before I go off talking about the insides of the library, let’s first touch base on what ducktape actually is, its Github page describes it as this: Automatic and customizable […]

28.03.2024 / By  Matylda Kamińska

Scalendar April 2024

scala conferences april 2024

Event-driven Newsletter Another month full of packed events, not only around Scala conferences in April 2024 but also Frontend Development, and Software Architecture—all set to give you a treasure trove of learning and networking opportunities. There’re online and real-world events that you can join in order to meet colleagues and experts from all over the […]

software product development

Need a successful project?

Estimate project