Build cache is an extension targeted to simplify and make more efficient work with large repositories in Maven. That is achieved by a combination of features:
Large projects usually pose scalability challenges and work with such projects require build tool which scales. Cache extension addresses that with incremental build execution and ability to efficiently work on sub-parts of a larger project without building and installing dependencies from the larger project. Though, features implemented in maven should give noticeable benefits in medium and small sized projects as well.
The idea of the build cache is to calculate key from module inputs, store outputs in cache and restore them later transparently to the standard Maven core. In order to calculate the key cache engine analyzes source code, build flow, plugins and their parameters. This allows to deterministically associate each project state with unique key and restore up-to-date (not changed) projects from cache and rebuild out-of-date(changed) ones. Restoring artifacts associated with a particular project state improves build times by avoiding re-building unnecessary modules. Cache does not make any interventions to actual build execution process and fully delegates build work to Maven core. This ensures that artifacts produced in presence of cache are equivalent to result produced by a standard Maven build.
To achieve accurate key calculation incremental Maven combines automatic introspection of project object model and allows fine-grained tuning by means of configuration file and xml attributes. Source code content fingerprinting is digests based which is more reliable over widely used file timestamps in tools like Make or Apache Ant. Deterministic build state allows reliably cache outputs even of the build in progress and share them between teams using remote cache. Deterministic inputs calculation allows distributed and parallel builds running in heterogeneous environments (like cloud of build agents) could efficiently reuse cached build artifacts. Therefore, incremental Maven is particularly well-suited for large Maven projects that have significant number of small modules. Remote cache in conjunction with relocatable inputs identification effectively enables “change once - build once” approach across all environments.
The challenge of implementing build cache in Maven is that domain model is overly generic and doesn't have dedicated api for build inputs. Because of that, even 2 identically looking builds from the same source code could normally produce 2 different results. The question here is tolerance level - can you accept particular discrepancies or not. For most of teams artifacts produced in the same build environment from the same source code will be considered equivalent and technical differences between them (like different timestamps in jar manifests) could be ignored. Now consider scenario when artifact is first produced with compiler X and cached. Later, without touching source code, compiler changes to Y and build yields significantly different outcomes of compilation. Should the produced artifacts be considered as equivalent? Both Yes and No answers are possible and could be even desirable in different scenarios. When productivity and performance are the primary concerns it could be desirable to tolerate insignificant discrepancies and maximise reuse of cached builds. As long as correctness is in focus there could be demand to comply with the exact release process. In the same way as with classic Maven, correctness is ensured by proper build configuration and controllable build environments. In the same way as with classic Maven the previous build is just an approximation of today build with some tolerance (implementation, configuration and environment driven).
At very simple form, the build cache Maven is essentially a hash function which takes Maven project and produces cache key for a project. Then the key is used to store and restore build results. Because of different factors there could be collisions and instabilities in the produced key. Collision could happen if the same key produced from the semantically different build states and will result in unintended reuse. Instability means that same input yields different key in different runs resulting in cache misses. The ultimate target is to find tradeoff between correctness and performance by means of configuration. In current implementation this is achieved by configuring cache processing rules in xml file.
In order to achieve better correctness need to:
In order to achieve better reuse need to:
Essentially cache setup is a process of inspecting build, taking these decision and reflect them in the cache configuration.
Please notice though idea of perfectly matching builds might be tempting, but it is not practical with regard to caching. Perfect correctness could lead to prevailing hit misses and render caching useless when applied to real projects. In practice, configuring sufficient(good enough) correctness might yield the best outcomes. Incremental Maven provides flexible and transparent control over caching policy and allows achieving desired outcomes - maximize usability or maximize equivalence(correctness) between pre-cached candidates and requested builds.
Cache extension is an opt-in feature. It is delivered as is and though the tool went through careful verification it‘s still build owner’s responsibility to verify build outcomes.
Given all the information above, the Incremental Maven is recommended to use in scenarios when productivity and performance are in priority. Typical cases are:
-DskipTests
and similar.For cases there correctness must be ensured (eg prod builds), it is recommended to disable cache and do clean builds. Such scheme allows to validate cache correctness by reconciling outcomes of cached builds against the reference builds.
Getting started - getting starting with cache and usage manual
Usage - shared cache setup procedure
Remote cache setup - shared cache setup procedure
How-To - cookbook for typical scenarios
Performance - performance tuning
Cache Parameters - description of supported parameters
Maximilian Novikov - Project lead. Idea, design, coordination and verification.
Alexander Ashitkin - Co-design and implementation of the majority of functionality
Alexander Novoselov - Hashing module implementation