Skip to content

Commit 8c113d9

Browse files
edsavagecursoragent
andcommitted
[ML] Add Gradle build cache for Java integration tests
Enable Gradle's local build cache for the ES integration test builds and persist it to GCS between CI runs. On cache-warm builds (same ES commit), compilation drops from ~15-20 min to ~1-2 min, saving significant CI wall-clock time. - Add gradle-build-cache-init.gradle init script - Pass --build-cache --init-script to both Gradle invocations - Download/upload cache tarball from GCS bucket - Extend post-checkout hook to inject GCS credentials for Java IT steps - Install gsutil on ES test agents when needed Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 540ae1c commit 8c113d9

File tree

4 files changed

+84
-6
lines changed

4 files changed

+84
-6
lines changed

.buildkite/hooks/post-checkout

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ if [[ "$BUILDKITE_PIPELINE_SLUG" == ml-cpp* ]]; then
3131
export BUILDKITE_API_READ_TOKEN=$(vault read -field=token secret/ci/elastic-ml-cpp/buildkite/api_read_token 2>/dev/null || echo "")
3232
fi
3333

34-
# sccache with GCS backend — inject credentials for all build_test steps.
35-
# The GCS service account key is stored in Vault. Build scripts detect
36-
# SCCACHE_GCS_BUCKET and configure sccache automatically.
37-
if [[ "$BUILDKITE_STEP_KEY" == build_test_* ]]; then
34+
# GCS service account — inject credentials for build and Java IT steps.
35+
# Build steps use it for sccache; Java IT steps use it for the Gradle
36+
# build cache. The key is stored in Vault.
37+
if [[ "$BUILDKITE_STEP_KEY" == build_test_* || "$BUILDKITE_STEP_KEY" == java_integration_tests_* ]]; then
3838
SCCACHE_GCS_KEY_JSON=$(vault read -field=key secret/ci/elastic-ml-cpp/sccache/gcs_service_account 2>/dev/null || echo "")
3939
if [ -n "$SCCACHE_GCS_KEY_JSON" ]; then
4040
export SCCACHE_GCS_BUCKET="elastic-ml-cpp-sccache"

.buildkite/scripts/steps/run_es_tests.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ export PR_AUTHOR=$(expr "$BUILDKITE_BRANCH" : '\(.*\):.*')
2424
export PR_SOURCE_BRANCH=$(expr "$BUILDKITE_BRANCH" : '.*:\(.*\)')
2525
export PR_TARGET_BRANCH=${BUILDKITE_PULL_REQUEST_BASE_BRANCH}
2626

27+
# Set up GCS credentials for Gradle build cache persistence (if available).
28+
# The post-checkout hook writes the GCS service account key for sccache;
29+
# reuse the same credentials for the Gradle cache bucket.
30+
if [ -n "${SCCACHE_GCS_BUCKET:-}" ] && [ -n "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]; then
31+
export GRADLE_BUILD_CACHE_GCS_BUCKET="${SCCACHE_GCS_BUCKET}"
32+
# Install gsutil if not already present
33+
if ! command -v gsutil &>/dev/null; then
34+
echo "--- Installing gsutil"
35+
pip3 install --quiet gsutil 2>/dev/null || pip install --quiet gsutil 2>/dev/null || echo "Warning: failed to install gsutil"
36+
fi
37+
fi
38+
2739
mkdir -p "${IVY_REPO}/maven/org/elasticsearch/ml/ml-cpp/$VERSION"
2840
cp "build/distributions/ml-cpp-$VERSION-linux-$HARDWARE_ARCH.zip" "${IVY_REPO}/maven/org/elasticsearch/ml/ml-cpp/$VERSION/ml-cpp-$VERSION.zip"
2941
# Since this is all local, for simplicity, cheat with the dependencies/no-dependencies split
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Gradle init script to enable the local build cache for ES integration test
3+
* builds. Injected via --init-script so that we don't need to modify the
4+
* cloned Elasticsearch repository.
5+
*
6+
* The local build cache stores task outputs keyed on their inputs. When the
7+
* cache directory is persisted between CI runs (e.g. via GCS), subsequent
8+
* builds with the same ES commit get near-instant compilation.
9+
*/
10+
11+
settingsEvaluated { settings ->
12+
settings.buildCache {
13+
local {
14+
enabled = true
15+
}
16+
}
17+
}

dev-tools/run_es_tests.sh

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
set -e
2626

27+
# Resolve the ml-cpp repo root before we cd away.
28+
ML_CPP_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
29+
2730
function isCloneTargetValid {
2831
FORK_TO_CHECK="$1"
2932
BRANCH_TO_CHECK="$2"
@@ -113,6 +116,52 @@ export GIT_COMMIT="$(git rev-parse HEAD)"
113116
export GIT_PREVIOUS_COMMIT="$GIT_COMMIT"
114117

115118
IVY_REPO_URL="file://$2"
116-
./gradlew $GRADLE_JVM_OPTS -Dbuild.ml_cpp.repo="$IVY_REPO_URL" :x-pack:plugin:ml:qa:native-multi-node-tests:javaRestTest $EXTRA_TEST_OPTS
117-
./gradlew $GRADLE_JVM_OPTS -Dbuild.ml_cpp.repo="$IVY_REPO_URL" :x-pack:plugin:yamlRestTest --tests "org.elasticsearch.xpack.test.rest.XPackRestIT.test {p0=ml/*}" $EXTRA_TEST_OPTS
119+
120+
INIT_SCRIPT="$ML_CPP_ROOT/dev-tools/gradle-build-cache-init.gradle"
121+
GRADLE_CACHE_DIR="$HOME/.gradle/caches/build-cache-1"
122+
CACHE_ARGS=""
123+
if [ -f "$INIT_SCRIPT" ]; then
124+
CACHE_ARGS="--build-cache --init-script $INIT_SCRIPT"
125+
fi
126+
127+
# Restore Gradle build cache from GCS if credentials are available.
128+
# This lets ephemeral CI agents reuse compilation outputs from prior builds.
129+
CACHE_KEY="gradle-build-cache-$(uname -m)"
130+
GCS_CACHE_PATH=""
131+
if [ -n "${GRADLE_BUILD_CACHE_GCS_BUCKET:-}" ] && [ -n "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]; then
132+
GCS_CACHE_PATH="gs://${GRADLE_BUILD_CACHE_GCS_BUCKET}/${CACHE_KEY}.tar.gz"
133+
if command -v gsutil &>/dev/null; then
134+
echo "--- Restoring Gradle build cache from $GCS_CACHE_PATH"
135+
mkdir -p "$GRADLE_CACHE_DIR"
136+
if gsutil -q stat "$GCS_CACHE_PATH" 2>/dev/null; then
137+
gsutil cp "$GCS_CACHE_PATH" /tmp/gradle-cache.tar.gz \
138+
&& tar xzf /tmp/gradle-cache.tar.gz -C "$HOME/.gradle/caches/" \
139+
&& rm -f /tmp/gradle-cache.tar.gz \
140+
&& echo "Gradle build cache restored ($(du -sh "$GRADLE_CACHE_DIR" 2>/dev/null | cut -f1))" \
141+
|| echo "Warning: failed to restore Gradle build cache, continuing without it"
142+
else
143+
echo "No cached Gradle build cache found, will build from scratch"
144+
fi
145+
else
146+
echo "gsutil not found, skipping Gradle build cache restore"
147+
fi
148+
fi
149+
150+
./gradlew $GRADLE_JVM_OPTS $CACHE_ARGS -Dbuild.ml_cpp.repo="$IVY_REPO_URL" :x-pack:plugin:ml:qa:native-multi-node-tests:javaRestTest $EXTRA_TEST_OPTS
151+
./gradlew $GRADLE_JVM_OPTS $CACHE_ARGS -Dbuild.ml_cpp.repo="$IVY_REPO_URL" :x-pack:plugin:yamlRestTest --tests "org.elasticsearch.xpack.test.rest.XPackRestIT.test {p0=ml/*}" $EXTRA_TEST_OPTS
152+
153+
# Upload Gradle build cache to GCS for future builds.
154+
if [ -n "$GCS_CACHE_PATH" ] && [ -d "$GRADLE_CACHE_DIR" ] && command -v gsutil &>/dev/null; then
155+
echo "--- Uploading Gradle build cache to $GCS_CACHE_PATH"
156+
CACHE_SIZE=$(du -sm "$GRADLE_CACHE_DIR" 2>/dev/null | cut -f1)
157+
if [ "${CACHE_SIZE:-0}" -gt 0 ] && [ "${CACHE_SIZE:-0}" -lt 4096 ]; then
158+
tar czf /tmp/gradle-cache.tar.gz -C "$HOME/.gradle/caches/" build-cache-1 \
159+
&& gsutil -o "GSUtil:parallel_composite_upload_threshold=50M" cp /tmp/gradle-cache.tar.gz "$GCS_CACHE_PATH" \
160+
&& rm -f /tmp/gradle-cache.tar.gz \
161+
&& echo "Gradle build cache uploaded (${CACHE_SIZE}M)" \
162+
|| echo "Warning: failed to upload Gradle build cache"
163+
else
164+
echo "Skipping cache upload (size=${CACHE_SIZE:-0}M, expected 1-4095M)"
165+
fi
166+
fi
118167

0 commit comments

Comments
 (0)