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:
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
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:
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 val
s, 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
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. OfficeId
is 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 Failure
s 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
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:
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 updateItem
looks 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 map
, canonical
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:
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 Entity
is 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 for
comprehensions 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