Ecto 2.0.0-rc is out!

We are really glad to announce the release candidate for Ecto 2.0 has been released. The upcoming Ecto version packs many goodies:

  • We have migrated to DBConnection, which considerably improves the performance of how Ecto manages connections to the database as a whole. Improved pool management, faster encoding/decoding, support for prepared queries and more;
  • The effective deprecation of Ecto.Model in favor of a more data-oriented approach. With Ecto 2.0, we want developers to focus on using Ecto as a tool instead of being responsible for your domain design;

  • Support for many_to_many associations and a more flexible approach to associations in general, including parallel preloading of associations;

  • Introduction of a new SQL sandbox that allows developers to run tests that rely on the database concurrently;

  • Support for subqueries, composite primary keys, insert_all (alongside update_all and delete_all) and more.

There are many more features, bug fixes, and improvements. There are also backwards incompatible changes (hence the 2.0 version). We recommend you to check the CHANGELOG for more information.

We recommend developers to give the release candidate a try. There are only a couple of improvements left before the final release. In case you encounter an unexpected behaviour, please open up an issue report.

This release supports both MySQL (mariaex) and PostgreSQL (postgrex) adapters.

Using Ecto 2.0.0-rc with Phoenix

This release candidate introduced some backwards incompatible changes for those using Ecto in their Phoenix projects. We summarize those changes below:

  1. The first step is to update the Phoenix.Ecto dependency to 3.0.0-rc (which depends on ecto 2.0.0-rc) in your mix.exs:
    {:phoenix_ecto, "~> 3.0.0-rc"}
    
  2. Ecto 2.0 now requires an explicit :ecto_repos configuration for running tasks like ecto.migrate and others. Open up your config/config.exs and add:
    config :my_app, ecto_repos: [MyApp.Repo]
    
  3. We also recommend developers to move the creation and migration of their test databases to aliases. First, remove the following lines from your test/test_helper.exs:
    Mix.Task.run "ecto.create", ~w(-r <%= application_module %>.Repo --quiet)
    Mix.Task.run "ecto.migrate", ~w(-r <%= application_module %>.Repo --quiet)
    

    And now add the following entry to defp aliases in your mix.exs file:

    "test": ["ecto.create --quiet", "ecto.migrate", "test"]
    
  4. Ecto also changed how it stores changeset in errors. If you are using gettext, you will need to update your errors.po files as well as your error_helper.ex view
  5. Phoenix projects by default use transactions in the test environment. Ecto 2.0 replaces the test transactions API by the new SQL sandbox. In your test/test_helper.exs, replace the following:

    Ecto.Adapters.SQL.begin_test_transaction(MyApp.Repo)
    

    by

    Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)
    

    Then, in each test/support/*_case.ex file, replace:

    unless tags[:async] do
      Ecto.Adapters.SQL.restart_test_transaction(MyApp.Repo, [])
    end
    

    by

    :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
    
    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
    end
    

That’s it! You can check the pull request that migrates Phoenix generators to Ecto 2.0-rc for a detailed list of changes.

Before we move on, let’s explore exactly what the sandbox changes mean in the next section.

Concurrent transactional tests

In previous versions, Ecto would keep a single connection to the database during tests and wrap the connection inside a transaction for every test. Ecto 2.0 introduces a sandbox mechanism where every test gets its own connection to the database, all of them wrapped in a transaction. Such allows tests to run concurrently by passing async: true to use MyApp.ConnCase (and similar) even if they rely on the database.

Let’s further explore the changes we did above. In your test helper, you should now see:

Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)

This command sets the sandbox to manual mode, meaning any Elixir process must explicitly check a database connection out before performing any database operation. If you open up test/support/conn_case.ex, you should see:

setup tags do
  :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)

  unless tags[:async] do
    Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
  end

  :ok
end

The second line in the snippet above does exactly that: it checks a connection out and wraps it inside a transaction. After the connection is checked out, it belongs exclusively to the current test process.

If you enabled async: true in your test case, its tests will run concurrently with tests from other test cases, each of them using its own and isolated database connection.

If the test is not asynchronous (:async was not set to true), we will execute the unless block in setup and share the connection we just checked out with all other processes, effectively falling back to the mechanism we had in Ecto 1.0.

The SQL Sandbox provides other options for managing your concurrent tests and you can learn more about them in the Ecto documentation.

We recommend developers to enable their concurrent tests and enjoy even faster test suites. When doing so, have the following in mind:

  • MySQL does not support concurrent tests. Unfortunately, MySQL will run into deadlocks when different connections try to write to the same table inside transactions and those transactions are rolled back;
  • When testing your models, controllers, and views, most of the tests should be able to run concurrently as is, except if they are interacting with other Elixir processes in your application (see example in the next bullet);

  • On the other hand, your channel tests require the collaboration between two processes: the test process and the channel process itself. If the channel process is accessing the database, you will need to allow explicitly it to use a database connection. See the Ecto.Adapters.SQL.Sandbox.allow/2 function;

  • Finally, tools for writing acceptance tests like Hound and Wallaby also support concurrent tests. You can find more information about doing so in the Phoenix.Ecto README.

Summing up

We are really excited about Ecto 2.0. By focusing on Ecto as a tool instead of an abstraction layer, we expect developers to have a simpler and a more light-weight mechanism for interacting with databases.

In the upcoming weeks, we will publish a series of blog posts that focuses on the new features and perspectives brought by Ecto 2.0. Meanwhile, don’t forget to read the CHANGELOG for the full list of changes.


Subscribe to Elixir Radar

3 responses to “Ecto 2.0.0-rc is out!”

  1. Congratulations, and many thanks to you and the rest of the Ecto team and contributors!

  2. Great news, this is getting better and better!

  3. ewh says:

    Congrats on the release. We’ve been using Ecto 2.0 beta for a few weeks now and the new changes have been very welcome. Keep up the great work!