Skip to content

fix: stop forcing is_write_index=false on aliases at index creation - #42

Closed
marevol wants to merge 3 commits into
mainfrom
fix/alias-write-index
Closed

marevol wants to merge 3 commits into
mainfrom
fix/alias-write-index

Conversation

@marevol

@marevol marevol commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

HttpCreateIndexAction set is_write_index: false on every alias whose writeIndex() the caller had left unset, so an index created with prepareCreate(name).addAlias(new Alias(a)) came up with a read-only alias. Indexing through it then failed with

no write index is defined for alias [a]

even though the alias pointed at a single index, which both the transport client and the REST API treat as writable.

Where it bites

Fess reaches this on a fresh install. SuggestHelper calls Suggester.createIndexIfNothing(), which creates the suggest index with exactly that call shape and adds both a search and an update alias; Suggester then hands the update alias to SuggestIndexer as the index name, so every suggest write goes through an alias this action had marked read-only. There is no suggest index among Fess's seeded fess_indices, so that is the only path.

Why no test caught it

fess-suggest's tests drive a real embedded node, so they never go through HttpCreateIndexAction. Moving them onto this client surfaced it immediately: 74 of 689 failed, all on the write path.

The fix

Leave the flag alone unless the caller set it. Note that Alias.toXContent writes the field unconditionally with no null check, so an unset alias now goes out as "is_write_index": null, which the create-index API treats as unset — the tests state that rather than asserting the field is absent.

Two regression tests, verified red-green: restoring the forcing makes the first one fail.

Verification

mvn clean package — 372 tests, 0 failures.

HttpCreateIndexAction set is_write_index=false on every alias whose
writeIndex() was unset, so an index created with
prepareCreate(name).addAlias(new Alias(a)) came up with a read-only alias.
Indexing through that alias then failed with

    no write index is defined for alias [a]

even though the alias pointed at a single index, which both the transport
client and the REST API treat as writable. Leave the flag unset unless the
caller set it.

Found while moving fess-suggest onto this client: Suggester.createIndexIfNothing()
creates its search and update aliases exactly this way, and every write through
them failed.
Two cases: an alias the caller left alone must not go out as
is_write_index=false, and an alias the caller did mark must keep the value it
was given. Alias.toXContent writes the field unconditionally, so an unset alias
is sent as null, which the create-index API treats as unset - the test states
that rather than asserting the field is absent.

Verified red-green: with the forcing restored, the first test fails.
Leaving Alias.writeIndex() unset was not enough. Alias.toXContent writes the
field unconditionally with no null check, so an unset alias went out as
is_write_index: null, and Elasticsearch 8 rejects that outright:

    Unknown token [VALUE_NULL] in alias [...]

which broke Elasticsearch8ClientTest.test_create_index and test_resolve_index.

Render the alias here instead of delegating, omitting every field the caller
did not set. That is the only rendering both engines read as "the caller did
not decide": false makes the alias read-only, null is a parse error on ES8.

The field names are spelled out because Alias's own ParseField constants are
private.

373 tests pass.
@marevol

marevol commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #43 — the conflict is the signal that this landed already.

The fork took the same fix, in a better place. This branch worked around
Alias.toXContent writing is_write_index unconditionally by re-rendering the
alias by hand in HttpCreateIndexAction, spelling out the field names because
Alias's own ParseField constants are private. Once Alias became part of this
repository that workaround was unnecessary: #43 put the null check in
Alias.toXContent itself and left HttpCreateIndexAction delegating to it.

On main today:

  • Alias.toXContent writes is_write_index only when writeIndex != null,
    where upstream writes it unconditionally.
  • HttpCreateIndexAction no longer sets alias.writeIndex(false) before rendering.
  • HttpCreateIndexActionTest carries the three regression tests from this branch —
    byte-identical to the version here apart from the package rename.

So both engines get the same wire format this branch produced: the field is omitted
when the caller did not decide, rather than sent as false (which makes the alias
read-only) or as null (which Elasticsearch 8 rejects with
Unknown token [VALUE_NULL] in alias).

Closing rather than rebasing; there is nothing left to merge.

@marevol marevol closed this Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant