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.
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 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 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
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
Try classes. The reason on why I did that is the same … type 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 boilerplate. We don’t have to worry much about error handling here. The API layer will translate
Failures into 404 and 500 responses.
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
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
flatten, but they are just noise to keep the types in check.
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.
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.
In Slick when modelling 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
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 an meta information to the table, you can add it as a function in the class. Neat.
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 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 on this extension. In fact you don’t have to choose Activate or Slick, you can use them in combination.
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.