samphilipd

samphilipd

How to cache erlang builds on CI?

Hey everyone!

I’m working on optimising our build pipeline. I have a flow that looks like:

– compile
– dialyzer, test etc

These are executed in separate containers. The compiled files are moved from the first container to the second by saving the _build/dev directory to a workspace, which is attached to the subsequent containers.

This works well in that application files are never recompiled, however every time it always recompiles all the Erlang modules. What have I missed in order to cache these.

Thanks!
Sam

Most Liked

idi527

idi527

:wave:

Just ran into a somewhat similar issue (erlang deps didn’t seem to be cached) and in my case the problem was in not caching deps folder after running mix compile. This is important since rebar3 actually stores ebins in deps, and in _build mix only creates symlinks.

So, my approach before (which had the same problem as in OP):

# deps stage
- cache pull deps
- mix deps.get
- cache push deps

# compile stage
- cache pull deps, _build
- mix compile
- cache push _build

and my approach now:

# deps stage
- cache pull deps
- mix deps.get
- cache push deps

# compile stage
- cache pull deps, _build
- mix compile
- cache push deps, _build # <-- !!!
idi527

idi527

Once for each stage, yes. Otherwise mix complains about missing dependencies.

tristan

tristan

Rebar3 Core Team

We were made aware of this recently and got it fixed. Didn’t realize all compilation wasn’t done to deps by mix :slight_smile:

The rebar3 version installed by mix should now be 3.13.1 which supports an additional argument of where to send output.

I’m not sure what version of mix uses the new output option… Will go look in a bit.

tristan

tristan

Rebar3 Core Team

Verified this is not yet supported in mix and likely won’t get attention until the end of May.

So if someone has time and wants to patch mix before then it’d be a great help. The change is to add -o <outputdir> where outputdir is the path to the dep under _build/ to the rebar3 bare compile command mix runs, if rebar3 version is >= 3.13.1 (or error if it fails with “invalid option” and tell the user to upgrade rebar3 I guess).

axelson

axelson

Scenic Core Team

Are you caching both the deps and build folders? On circle ci we’re caching them both separately

      # restore deps cache
      - restore_cache:
          keys:
            # CI_CACHE_VERSION is used to manually bust the cache
            - messages-deps-v9-{{ .Environment.CI_CACHE_VERSION }}-{{ .Branch }}-{{ checksum "mix.lock" }}
            - messages-deps-v9-{{ .Environment.CI_CACHE_VERSION }}-{{ .Branch }}
            - messages-deps-v9-{{ .Environment.CI_CACHE_VERSION }}-

      # restore build cache
      - restore_cache:
          keys:
            - messages-build-v9-{{ .Environment.CI_CACHE_VERSION }}-{{ .Branch }}
            - messages-build-v9-{{ .Environment.CI_CACHE_VERSION }}-

Then steps related to compiling and running migrations. After that we can save the cache:

      # save deps cache
      - save_cache:
          key: my-app-deps-v9-{{ .Environment.CI_CACHE_VERSION }}-{{ .Branch }}-{{ checksum "mix.lock" }}
          paths: "deps"
      - save_cache:
          key: my-app-deps-v9-{{ .Environment.CI_CACHE_VERSION }}-{{ .Branch }}
          paths: "deps"
      - save_cache:
          key: my-app-deps-v9-{{ .Environment.CI_CACHE_VERSION }}-
          paths: "deps"

      # save build cache
      - save_cache:
          key: my-app-build-v9-{{ .Environment.CI_CACHE_VERSION }}-{{ .Branch }}
          paths: "_build"
      - save_cache:
          key: my-app-build-v9-{{ .Environment.CI_CACHE_VERSION }}-
          paths: "_build"

There’s multiple caches so that if for example the mix.lock changes then we can fallback to the next most recently used cache. Also the v9 is there so that when we make large changes we can cache bust everything and we can do something similar by changing the CI_CACHE_VERSION env variable (but we can change this without pushing a code update). A reason you’d need to bust the cache is when a library reads from application config during compilation (a great example is the Elixir mime type plug: https://hexdocs.pm/mime/MIME.html).

Here’s the official CircleCi elixir guide (although I don’t find it super helpful): https://circleci.com/docs/2.0/language-elixir/

I hope that helps!

Where Next?

Popular in Questions Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New

Other popular topics Top

gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New

We're in Beta

About us Mission Statement