Skip to content

[9.4] [ResponseOps] Skip unchanged alerts-as-data index templates on install (#280154) - #291097

Merged
ersin-erdal merged 1 commit into
elastic:9.4from
ersin-erdal:backport/9.4/pr-280154
Sep 15, 2026
Merged

ersin-erdal merged 1 commit into
elastic:9.4from
ersin-erdal:backport/9.4/pr-280154

Conversation

@ersin-erdal

Copy link
Copy Markdown
Contributor

Backport

This will backport the following commits from main to 9.4:

Questions ?

Please refer to the Backport tool documentation

elastic#280154)

## Summary

Extends the skip-unchanged (`_meta` content-hash) installation work from
elastic#278126 to **index templates**, completing that behavior across the
resource types the alerts service installs per context.

elastic#278126 is merged. Previously `createOrUpdateIndexTemplate` always PUT
the template on every install. Now it stamps a content hash into the
template's `_meta`, GET-checks the currently-installed template, and
**skips the write when the hash matches**.

## Why

This is the remaining slice of Phase 2 of elastic#246016. On its own, elastic#278126
makes a repeated context install a no-op for the ILM policy and
component templates, but the **index template still re-PUT** every time.

Elasticsearch already recognises an identical PUT and leaves the cluster
state alone, so these writes were not publishing new cluster states —
but the body still crosses the wire from every node, and the master
still queues and runs a cluster-state update task that parses the
template before concluding nothing changed. That matters most in
combination with the per-node install lock (elastic#280126): when one node
installs and the others follow, a follower's install should be cheap
GETs, not redundant cluster-state writes. With this change, a follower's
repeated install is a no-op across component *and* index templates.

## What changed

- `create_or_update_index_template.ts`:
- a single `getExistingIndexTemplate` GET now returns both the existing
`total_fields.limit` (for the Phase-1 preservation) and the existing
`_meta.content_hash`.
- the content hash is computed **after** the Phase-1 limit preservation,
so the stamped hash matches the body actually installed.
- skip the `putIndexTemplate` (and the preceding `simulateTemplate`)
when the installed hash matches.

### Fail-safe

Skipping happens only on a positive hash match. A missing stamp
(templates installed before this change), a 404, or any GET failure —
permissions, an exhausted retry — leaves the installed content unknown
and falls through to the normal PUT, so the check can never block an
install that would otherwise have succeeded. The worst case is today's
behavior: a redundant write.

On that fall-through path we also cannot preserve a higher existing
`total_fields.limit` (same as a 404). The skip still keys off the stamp,
not the live body: a hand-edited template with an intact `content_hash`
stays un-repaired, where every install used to rewrite it.

### The `total_fields.limit` is tracked apart from the hash

The limit is deliberately left out of the content hash. Kibana raises it
outside this path while a mapping install crawls the limit up
(`updateIndexTemplateFieldsLimit`), and operators raise it by hand
through DevTools or the fields-limit API — both leaving `_meta` intact.
Hashing it would make every such raise read as a changed template and
rewrite byte-identical content on the next startup, which is exactly the
write this PR exists to remove.

It is compared as a number instead, and skipping requires *both* an
unchanged hash and an installed limit that already satisfies the
configured one, so a genuinely raised configured limit still installs
and a template missing `ignore_dynamic_beyond_limit` still gets
repaired:

| change | before | now |
| --- | --- | --- |
| limit raised out of band (crawl / DevTools) | redundant install |
skipped |
| configured limit raised | install | install |
| configured limit lowered | install (preserving the higher limit) |
skipped |
| anything else in the template changed | install | install |

### Logging

The install is logged at `info` with the reason it went ahead (`not
installed`, `installed template carries no content hash`, `content
changed (<old> -> <new>)`, or an unsatisfied limit). Installs are the
rare event after this change, so a resurgence of template writes is
visible without enabling debug logging. The skip stays at `debug`, since
at `info` it would add a line per template per (context x space) on
every startup.

## Scope / not included

- **Concrete write index / mapping** hash-skip and the
**monotonic-version rule** are intentionally *not* here — they're
separate, more involved changes (the mapping path interacts with the
auto-increase retry, and version-monotonicity needs semver comparison).
This PR is the index-template slice.
- No change to *when* install runs or to auto-increase behavior.

## Testing

- `node scripts/jest
x-pack/platform/plugins/shared/alerting/server/alerts_service` — 333
pass, incl. index-template tests (stamped PUT / skip-on-match /
PUT-on-stale-hash / PUT-when-unreadable) and updated integration
assertions.
- `node scripts/jest .../rule_registry/server/rule_data_plugin_service`
— pass.
- `node scripts/jest_integration --config
x-pack/platform/plugins/shared/alerting/jest.integration.config.js
x-pack/platform/plugins/shared/alerting/server/integration_tests/skip_unchanged_index_templates.test.ts`
— 5 pass against a real Elasticsearch: the stamp/skip round trip, the
re-PUT on change, the unstamped upgrade path, and both limit directions
(the out-of-band raise driven through the real
`updateIndexTemplateFieldsLimit`).
- scoped `type_check` and `eslint` clean.

Contributes to elastic#246016

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 49e091e)
@ersin-erdal ersin-erdal added the backport This PR is a backport of another PR label Sep 15, 2026
@ersin-erdal
ersin-erdal enabled auto-merge (squash) September 15, 2026 11:44
@kibanamachine
kibanamachine requested review from pmuellr and removed request for kibanamachine September 15, 2026 11:45
@kibanamachine

Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

✅ unchanged

@ersin-erdal
ersin-erdal merged commit 3db1ddd into elastic:9.4 Sep 15, 2026
60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport This PR is a backport of another PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants