Skip to content

Support TTL indexes in the GORM-Mongo mapping DSL and reconcile index option changes#15747

Open
codeconsole wants to merge 2 commits into
apache:8.0.xfrom
codeconsole:mongo-ttl-index-support
Open

Support TTL indexes in the GORM-Mongo mapping DSL and reconcile index option changes#15747
codeconsole wants to merge 2 commits into
apache:8.0.xfrom
codeconsole:mongo-ttl-index-support

Conversation

@codeconsole

Copy link
Copy Markdown
Contributor

What

Two related improvements to MongoDB index initialisation in MongoDatastore.initializeIndices.

1. TTL indexes can be declared in the mapping DSL

A single-field index declared with expireAfterSeconds now materialises a MongoDB TTL index:

static mapping = {
    created index: true, indexAttributes: [expireAfterSeconds: 7776000]   // 90 days
}

Previously this was impossible. MongoConstants.mapToObject builds IndexOptions by reflectively invoking same-named single-argument setters, but the driver's only TTL setter is the two-argument IndexOptions.expireAfter(Long, TimeUnit), which the mapper can't reach. So expireAfterSeconds is now pulled out of the index attributes and applied explicitly.

TTL is single-field only — MongoDB rejects expireAfterSeconds on a compound index with CannotCreateIndex; that error surfaces unchanged.

2. Index-option conflicts are reconciled, not just logged

All three index paths (declared getIndices(), getCompoundIndices(), per-property) now route through one helper. On IndexOptionsConflict (server error 85 — an index already exists on these keys with different options) it reconciles instead of only logging the stack trace:

  • TTL value change (e.g. a configurable retention changed between restarts) is applied in place via collMod — no drop, no rebuild, no window without an index.
  • Any other conflicting change is dropped and recreated only when indexAttributes: [recreateOnConflict: true] is declared; otherwise it logs a clear, actionable message. A unique index is never silently dropped.

Why

Without DSL support, a TTL index has to be created imperatively (raw-driver createIndex in a bootstrap hook), and changing its retention needs hand-rolled drop-and-recreate logic. That is easy to get wrong: a stale plain index on the same key blocks the TTL create, and the TTL then silently never applies. Declaring it in the mapping, with in-place reconciliation handled by the framework, removes both the boilerplate and that footgun.

Tests

TtlIndexSpec (runs against the Mongo testcontainer) covers:

  • expireAfterSeconds in the mapping produces a TTL index with the declared value.
  • Mutating the live TTL and re-initialising restores the declared value on the same index (one index, no conflict) — exercising the collMod reconcile path.

Existing IndexAttributesAndCompoundKeySpec (unique / compound) still passes.

… option changes

indexAttributes: [expireAfterSeconds: N] now materialises a MongoDB TTL index.
The driver only exposes the two-argument IndexOptions.expireAfter, which
MongoConstants.mapToObject cannot reach (it only invokes single-argument
setters), so expireAfterSeconds is pulled out of the attributes and applied
explicitly.

All three index paths in MongoDatastore.initializeIndices (declared, compound,
and per-property) now route through a single helper. On IndexOptionsConflict
(error 85) it reconciles instead of only logging: a TTL change is applied in
place with collMod (no drop, no rebuild, no gap); any other conflicting change
is dropped and recreated only when indexAttributes: [recreateOnConflict: true]
is declared, otherwise logged with guidance.

Adds TtlIndexSpec covering TTL creation from the mapping declaration and
in-place TTL reconciliation on re-initialisation.
…hetic _fts key

A declared text index has key {field: 'text'}, but MongoDB reports an existing one with a
synthetic {_fts: 'text', _ftsx: 1} key, so the IndexOptionsConflict reconcile never matched it
and recreateOnConflict couldn't absorb a differently-named text index. Since MongoDB allows at
most one text index per collection, an existing text index is unambiguously the conflicting one
— findIndexByKeyPattern now matches any existing text index when the declared index is itself a
text index, regardless of key shape or name.

Adds TextIndexViaAttributesSpec covering conventional text-index declaration via
indexAttributes: [type: 'text'] and absorption of a legacy differently-named text index.
@testlens-app

testlens-app Bot commented Jun 19, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: b939e01
▶️ Tests: 40374 executed
⚪️ Checks: 44/44 completed


Learn more about TestLens at testlens.app.

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.0000%. Comparing base (c64ab81) to head (b939e01).
⚠️ Report is 7 commits behind head on 8.0.x.

Additional details and impacted files

Impacted file tree graph

@@      Coverage Diff       @@
##   8.0.x   #15747   +/-   ##
==============================
==============================
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant