Strip undeclared keys from application_settings.zoekt_settings

🤖 AI-authored change.

What does this MR do and why?

An undeclared key in the zoekt_settings jsonb column on application_settings makes every PUT /api/v4/application/settings call fail — including payloads with nothing to do with search, such as {"gravatar_enabled": true} — because the column's JSON schema declares additionalProperties: false. The only remedy available today is a manual database edit.

This adds a post-deployment migration that removes keys the schema does not declare, and keeps additionalProperties: false so an undeclared key is still rejected on write.

References

  • Closes #621732, split from #607625 (Problem 3)
  • Schema ee/app/validators/json_schemas/application_setting_zoekt_settings.json; the validator and the jsonb_accessor :zoekt_settings block in ee/app/models/ee/application_setting.rb
  • Precedent: db/migrate/20251211100451_remove_zoekt_minimum_projects_for_traversal_id_search_from_application_settings.rb

Query plans — Database Lab gitlab-production-main; the link carries the plan and the query as shipped, including the jsonb_typeof guard:

application_settings is a single-row table, so the statement is one sequential scan of one page plus a jsonb_each per row. It updates 0 rows because GitLab.com's own row is healthy — 13 keys present, all declared — which is the expected no-op.

How to set up and validate locally

  1. Put an undeclared key into the column:
    ApplicationSetting.current.update_column(:zoekt_settings,
      ApplicationSetting.current.zoekt_settings.merge('zoekt_orphaned_key' => 2))
  2. PUT /api/v4/application/settings with {"gravatar_enabled": true} → 400, {"message":{"zoekt_settings":["must be a valid json schema"]}}
  3. bin/rails db:migrate
  4. The same PUT → 200
Design choice and key provenance

Why a broken row is skipped. jsonb_each raises on a jsonb that is not an object, and the column carries no jsonb_typeof check constraint, so an array or scalar value would abort the whole migration and the upgrade with it. The WHERE therefore skips such a row: it is broken beyond what a key strip can repair, and rewriting it to {} would destroy data.

Why the schema stays strict. Loosening additionalProperties to true would make the same settings save succeed, but it would also start silently accepting misspelled and stale keys into a column nothing reads — trading a loud failure for a quiet one. Cleaning up the data preserves the write-time rejection that makes a typo visible.

Where the orphaned key came from. zoekt_max_restarts_15m is a real setting: added in 35641669 (2026-06-10, %19.1), while the reporter's instance ran 9208f123 (2026-06-02) — which is why their git log -S search found nothing. That instance is a downgrade artifact. The migration does not depend on it: it removes whatever the schema does not declare, whatever put it there.

Test evidence. Two mutations of the shipped code, each measured rather than inferred. Reverting #up to a no-op turns three of the five examples red (5 examples, 3 failures); removing the jsonb_typeof guard turns one red (5, 1), and it is the non-object example the guard exists for. Restored, all five pass. The fourth example is the regression test for this bug — it asserts that saving gravatar_enabled is refused while the undeclared key is present and accepted once the migration has run. Pipeline 2865212892 ran all five examples green in three job classes at the current head. Full output: snippet 6058298.

What I did not verify

  • No #down. The removed keys are undeclared by definition, so nothing reads them and their values cannot be reconstructed; rollback is a documented no-op rather than an omission.
  • The query plan is a no-op on GitLab.com's row, because that row is healthy. The non-empty case was measured only locally — no production row carrying an undeclared key exists to measure.
  • Not exercised against a live self-managed instance; the reproduction steps above are the path a reviewer can run.

🤖 Automated change — mention me to get a response. Feedback welcome. Ask to speak to John directly anytime.

Edited by John Mason

Merge request reports

Loading
Loading