My local development feedback loop between code change and runnable container was annoyingly long on a Maven-based project I was recently working on. I wanted to speed things up.
The scenario was something like this:
- touch/change some source code
docker build
- maven downloads the world
- maven compiles my project
docker run
- touch/change some source code
docker build
- maven downloads the world
- maven compiles my project
docker run
- touch/change some source code
docker build
- maven downloads the world
- maven compiles my project
docker run
- …
I didn’t really enjoy the “maven downloads the world” steps, and wanted to minimize the number of times it needed to run.
Let’s follow along as I make my situation a little better. For illustration, we’ll start off with this generic archetype-created skeleton project:
<style>.gist table { margin-bottom: 0; }</style>
<script src="https://gist.github.com/f901ceaf75b3458c1cd0.js"></script>
Things aren’t that bad when I am building back-to-back, e.g.
$ docker build .
...
$ docker build .
...
Notice that the second build is fast as everything is cached up.
But what about when we do something like this:
$ docker build .
...
$ touch src/main/java/com/keyholesoftware/blog/App.java
...
$ docker build .
...
Notice that the second build is unnecessarily slowed down by the redownload portion.
I sat around and despaired for a while until I remembered the tricks I’ve seen with selective caching:
<style>.gist table { margin-bottom: 0; }</style>
<script src="https://gist.github.com/cd7eac7ee43d09ccfe89.js"></script>
Let’s try that sequence again.
$ docker build .
...
$ touch src/main/java/com/keyholesoftware/blog/App.java
...
$ docker build .
...
Getting better, but there were still a few downloads going on during the second build. They are related to the surefire test/plugin. Actually this process will help us iron out downloads which are chosen dynamically, and lock those down. In this case, we lock down our surefire provider.
<style>.gist table { margin-bottom: 0; }</style>
<script src="https://gist.github.com/85d3c479d7b5d6868ed3.js"></script>
Let’s try that sequence again.
$ docker build .
...
$ touch src/main/java/com/keyholesoftware/blog/App.java
...
$ docker build .
...
So now, unless we change the POM, we don’t have to redownload anything. Nice.
Now the scenario is something like this:
- touch/change some source code
docker build
- maven downloads the world
- maven compiles my project
docker run
- touch/change some source code
docker build
- maven compiles my project
docker run
- touch/change some source code
docker build
- maven compiles my project
docker run
- …
Notice the “maven downloads the world” step only happens once (unless I actually change the POM, of course).
Final Thoughts
There might be better ways to handle some of this (e.g. dependency:resolve/resolve-plugin but that doesn’t seem to work as thoroughly, and probably something with fig), but I mainly wanted to highlight a possible use of the selective adding/caching.
Other Notes:
Thanks for reading!
— Luke Patterson, asktheteam@keyholesoftware.com