This extension wraps standard Maven surefire and failsafe plugins to support local and remote build caching.
The Apache Maven Build Cache Extension is an open-source
project adding support of artifact caching to maven, also allowing to skip goal executions via cache.
It can cover a wide range of typical scenarios, however, it's not a good choice for pipelines separating compile and
test phases. It does not properly handle test reports, does not support flexible test filtering for test distribution
(caching them separately depending on filtered test subset) for multi-job execution.
Also it does not cache so called CLI executions like mvn surefire:test, only lifecycle executions
like mvn clean verify, which is also not always convenient especially for large scale projects.
Add to the .mvn/extensions.xml of your project:
<extensions>
<extension>
<groupId>com.github.seregamorph</groupId>
<artifactId>surefire-cached-extension</artifactId>
<version>0.22</version>
</extension>
</extensions>This extension will print the cache statistics after the build.
It's possible to define surefire-cached.json file for the module. If the file is not found,
the extension will go thru the parent modules (till root) and try to find it there.
If no configuration is provided, the default configuration is used
{
"common": {
"inputIgnoredProperties": [
"java.version",
"os.arch",
"os.name",
"env.CI",
"env.GITHUB_BASE_REF",
"env.GITHUB_REF",
"env.GITHUB_RUN_ID",
"env.GITHUB_JOB",
"env.GITHUB_SHA",
"project.version"
],
"inputProperties": [
"java.specification.version"
],
"excludeModules": [],
"excludeClasspathResources": [
"META-INF/MANIFEST.MF",
"META-INF/maven/**/pom.properties",
"META-INF/maven/**/pom.xml"
]
},
"surefire": {
"artifacts": {
"surefire-reports": {
"includes": ["surefire-reports/TEST-*.xml"]
}
}
},
"failsafe": {
"artifacts": {
"failsafe-reports": {
"includes": [
"failsafe-reports/TEST-*.xml",
"failsafe-reports/failsafe-summary.xml"
]
}
}
}
}inputProperties are properties and environment variables (defined with env. prefix) that are taken into calculation
of cache entity key. inputIgnoredProperties are properties and environment variables that are included as
meta-information for the cache entity, but not taken into calculation of hash. It can be convenient to have all
this meta-information to find the origin of the cache entity (e.g. GitHub pipeline and commit hash that produced it).
It's possible to customize parameters overriding (no appending) default. E.g. specify modules which should be
excluded from hash calculation (like modules with git.properties containing build timestamp and commit hash). Or
custom artifacts to be cached (separate for surefire and failsafe):
{
"common": {
"//": "Exclude modules with timestamp or git.properties",
"excludeModules": ["com.acme:module-build-version-timestamp"],
"excludeClasspathResources": [
"META-INF/MANIFEST.MF",
"META-INF/maven/**/pom.properties",
"META-INF/maven/**/pom.xml",
"git.properties"
]
},
"surefire": {
"artifacts": {
"surefire-reports": {
"includes": [
"surefire-reports/TEST-*.xml",
"surefire-reports/testng-results.xml"
]
},
"jacoco": {
"includes": ["jacoco-surefire.exec"]
}
}
},
"failsafe": {
"artifacts": {
"failsafe-reports": {
"includes": [
"failsafe-reports/TEST-*.xml",
"failsafe-reports/failsafe-summary.xml",
"failsafe-reports/testng-results.xml"
]
},
"jacoco": {
"includes": ["jacoco-failsafe.exec"]
}
}
}
}It's possible to define global configuration options. These parameters can be specified in .mvn/maven.config as
default, or in the maven command line. Configuration parameters should be prefixed
with -D (e.g. -DcacheExpirationHours=4). All these parameters are optional and have default value.
Parameters:
| Parameter | Comment | Default value |
|---|---|---|
cacheExpirationHours |
Time interval in hours for how long the cache entity is valid. Now used only for s3 storage. | 6 |
Build your project with the extension, the caching will use default file storage $HOME/.m2/test-cache
mvn clean installOr compile separately and run unit tests
mvn clean install -DskipTests=true
mvn surefire:testOr via phase
mvn testThen run integration tests of your project
mvn clean install -DskipTests=true
mvn failsafe:integration-test -Dit.test=SampleITor via phase
mvn verifyRun server from this repo
./mvnw clean install
docker compose upBuild your project with the extension using the remote cache
mvn clean verify -DcacheStorageUrl=http://localhost:8080/cacheSee also cache monitoring chapter.
It's possible to access S3 directly from Maven build (no HTTP service required).
First you need to use this extension in .mvn/extensions.xml (instead of the regular surefire-cached-extension):
<extensions>
<extension>
<groupId>com.github.seregamorph</groupId>
<artifactId>surefire-cached-extension-s3</artifactId>
<version>0.22</version>
</extension>
</extensions>Declare cacheStorageUrl property as s3 URI to the bucket:
mvn clean verify -DcacheStorageUrl=s3://bucketnameOther parameters will be obtained from the environment variables (like AWS_REGION), system property
(like aws.region) or default local AWS profile.
Depending on the authorization provider these environment variables may be used
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
For the other options (including loading from ~/.aws/credentials) see
AWS documentation
The entities will be saved in S3 with cacheExpirationHours expiration timeout.
The extension generates text and json reports at the end of the build. Sample text report:
[INFO] Total test cached results (surefire-cached):
[INFO] SUCCESS (5 modules): 2m18s serial time
[INFO] FROM_CACHE (3 modules): 36s serial time saved
[INFO] Cache hit read operations: 9, time: 0.297s, size: 320.00 KB
[INFO] Cache miss read operations: 5, time: 0.022s
[INFO] Cache write operations: 20, time: 0.141s, size: 13.81 MB
Json report can be found in target/surefire-cached-report.json of the build root module. Sample:
{
"pluginResults" : {
"surefire-cached" : {
"SUCCESS" : {
"totalModules" : 5,
"totalTimeSec" : 138.363
},
"FROM_CACHE" : {
"totalModules" : 3,
"totalTimeSec" : 36.281
},
"SKIPPED_CACHE" : {
"totalModules" : 1,
"totalTimeSec" : 5.420
}
}
},
"cacheServiceMetrics" : {
"readHitOperations" : 9,
"readMissOperations" : 5,
"readHitBytes" : 327683,
"readHitMillis" : 297,
"readMissMillis" : 22,
"readFailures" : 0,
"readSkipped" : 0,
"writeOperations" : 20,
"writeBytes" : 14484173,
"writeMillis" : 141,
"writeFailures" : 0,
"writeSkipped" : 0
}
}The extension wraps and replaces default Mojo factory DefaultMavenPluginManager with own implementation CachedMavenPluginManager. All mojos are delegating to default behaviour except Surefire and Failsafe plugins. They are wrapped to caching logic, which calculates task inputs (classpath elements hash codes) and reuses existing cached test result when available.
If you run a cache server, it's possible to scrape Prometheus metrics via http://localhost:8080/actuator/prometheus
Use these queries to build cache dashboards (TODO provide JSON to import to Grafana):
Click to expand
Queries:
rate(sum(cache_saved_time_seconds_total{pluginName="surefire-cached"})[2m])rate(sum(cache_spent_time_seconds_total{pluginName="surefire-cached"})[2m])rate(sum(cache_overlap_time_seconds_total{pluginName="surefire-cached"})[2m])
Queries:
rate(sum(cache_saved_time_seconds_total{pluginName="failsafe-cached"})[2m])rate(sum(cache_spent_time_seconds_total{pluginName="failsafe-cached"})[2m])rate(sum(cache_overlap_time_seconds_total{pluginName="failsafe-cached"})[2m])
Queries:
- reads
rate(sum(get_cache_size_total)[2m])/1048576 - writes
rate(sum(put_cache_size_total)[2m])/1048576
The default Maven multi-module build does not do an efficient multi-core CPU utilization.
See turbo-builder for more details. This extension is
compatible with maven-surefire-cached extension.
By default Maven executes unit and integration tests (via surefire and failsafe plugins) in a single execution,
however at large scale it makes sense to split huge test suites to smaller pieces (test distribution).
See test-distribution for more details. This extension
is also compatible with maven-surefire-cached extension.
