elastic/elasticsearch
Development workflow
The inner loop most contributors use day to day.
Branch and rebase
git checkout main
git pull --rebase upstream main
git checkout -b my-feature-branchAlways rebase on top of main rather than merging it into your branch. The maintainers will squash on merge.
Build incrementally
Most edits do not require a full build. Use targeted Gradle tasks:
# Compile a single subproject
./gradlew :server:compileJava
# Run only the tests for that subproject
./gradlew :server:test
# Run the precommit gates for one subproject
./gradlew :server:precommitThe precommit task is the meta-task wired in build-tools-internal/src/main/groovy/elasticsearch.precommit.gradle. It runs Spotless, forbidden-apis, third-party audit, license-header check, JarHell, and a handful of project-specific checks.
Reproduce a failing test from CI
Buildkite's failure logs include a REPRODUCE WITH: line. Copy and run it verbatim — it captures the full Gradle command, including -Dtests.seed, JVM args, and the --tests selector. Example:
./gradlew :server:test \
--tests "org.elasticsearch.cluster.coordination.CoordinatorTests.testElectionSchedulerStaysCalmIfDoesNotKnowAboutMasterCandidates" \
-Dtests.seed=DEADBEEF -Dtests.locale=en-US -Dtests.timezone=UTC \
-Dtests.iters=20Run a multi-node cluster from source
./gradlew :run -PnumNodes=3Or, for a more realistic setup with x-pack security enabled and pre-configured users:
./gradlew :x-pack:plugin:runEach node logs to build/run/<node-name>/logs/. Stop with Ctrl+C.
Debugging
Attach a debugger by adding --debug-jvm to any test or run task. The JVM listens on port 5005:
./gradlew :server:test --tests <ClassName> --debug-jvmFor a running cluster:
./gradlew :run --debug-jvmFormat and lint before pushing
./gradlew spotlessApply
./gradlew :server:precommitspotlessApply reorders imports and prunes unused imports; running it once before every push prevents review churn.
Adding a new transport action
The mechanical recipe (see Action layer for the longer version):
- Define a new
ActionType<Response>in a*Actionconstants class. - Implement
Transport*Action extends HandledTransportAction<Request, Response>(or one of the master / shard / nodes-fanout variants). - Implement a
Rest*Action extends BaseRestHandlerand register HTTP routes. - Register the action in
ActionModule(or in a plugin'sgetActions()for plugin-contributed actions). - Add a YAML REST test under the relevant
rest-api-spec/test/or per-modulesrc/yamlRestTest/. - Add unit tests for the request/response serialization (
Writeableround-trips) and the handler logic.
Adding a new setting
Setting<T> instances live next to the service that uses them. Register cluster/index settings via the plugin's getSettings() and IndexScopedSettings machinery.
public static final Setting<TimeValue> MY_TIMEOUT_SETTING = Setting.timeSetting(
"indices.my_feature.timeout", TimeValue.timeValueSeconds(30), Property.NodeScope, Property.Dynamic);If the setting is dynamic, register a listener with ClusterSettings or IndexSettings so changes are picked up without a restart.
Common dev pitfalls
- Wildcard imports: never. Spotless will fix them, but avoid them in the diff.
- Editing generated files: ANTLR grammars, transport version CSVs, and a few REST spec files are regenerated. Edit the source instead.
FilevsPath: prefer NIOPath.forbidden-apisblocks most uses ofjava.io.Fileoutside of legacy code paths.- Direct
Threadcreation: useEsExecutorsand the named thread pools. - Global state: the codebase avoids singletons; always wire dependencies through the constructor.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.