Skip to content

feat: add a Data Consistency admin page (#1622) - #2086

Merged
netomi merged 2 commits into
mainfrom
admin-data-consistency
Aug 21, 2026
Merged

feat: add a Data Consistency admin page (#1622)#2086
netomi merged 2 commits into
mainfrom
admin-data-consistency

Conversation

@netomi

@netomi netomi commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Closes #1622 and #1729 and #1733 .

What

  • Extension.active lost-update fix: @DynamicUpdate on Extension so
    Hibernate only writes changed columns, closing a race where a full-entity
    UPDATE from one thread could resurrect a stale active flag after another
    thread had legitimately flipped it. Also adds
    findExtensionsWithInconsistentActiveFlag() to detect extensions already
    left in that state.
  • Data Consistency admin page, built on top of that fix as a general,
    pluggable framework (ConsistencyCheck) for this and future checks:
    • ConsistencyCheckService lists checks with a live finding count and can
      fix one finding or all findings for a check. Findings are always computed
      live against the current DB state - nothing is persisted between runs.
    • A daily JobRunr job runs every check and auto-fixes the ones that don't
      need human judgment (autoFixOnSchedule(), opt-out per check).
    • Every fix - scheduled or manual - is logged via LogService under a
      system user (ConsistencyCheckUser), the same convention used by
      ExtensionControlService, so it shows up on the existing Admin Logs page.
    • First check: ExtensionActiveFlagCheck.
    • New endpoints under /admin/consistency; a new "Data Consistency" page
      in the admin dashboard with one card per check, a "Fix all" action
      directly on the card, per-finding "Fix" once expanded, and a manual
      "Refresh".

Testing

  • ./gradlew test: 987 tests passing.
  • yarn test (webui): 74 tests passing.
  • yarn lint (webui): clean.

🤖 Generated with Claude Code

netomi added 2 commits August 21, 2026 23:57
Extension.setActive() was routinely called as part of a full-entity
merge/update. Hibernate generates a SQL UPDATE covering every mapped
column, so a version-publish thread that read the entity before
another thread flipped active could overwrite that flag back to its
stale value once it saved - a classic lost update, not guarded by any
transaction isolation level Postgres offers by default.

Add @DynamicUpdate so Hibernate only includes changed columns in the
UPDATE, closing the window for this specific race without introducing
optimistic-locking retries/contention on a hot entity.

Also add a native query to find extensions whose active flag disagrees
with whether any of their versions is active, as a way to detect and
repair rows that were already corrupted before this fix - used by the
data-consistency check landing in the next commit.
Introduce a small, pluggable framework for detecting and repairing
known data inconsistencies, plus an admin UI for it:

- ConsistencyCheck: the extension point a check implements (id, name,
  description, check(), fix(entityId)); autoFixOnSchedule() defaults
  to true but lets a check opt out when a fix needs human judgment.
- ConsistencyCheckService: lists checks with their live finding count,
  fetches findings for one check on demand, and fixes one or all
  findings for a check. No persistence of past runs - findings are
  always computed live against the current database state.
- ConsistencyCheckJobRequestHandler + ScheduleConsistencyCheckJobs: a
  daily JobRunr job that runs every check and auto-fixes the ones that
  allow it, so routine drift self-heals without an admin having to
  notice and click a button.
- Every fix, whether run interactively by an admin or by the scheduled
  job, is recorded via LogService under a system user
  ("ConsistencyCheckUser"), the same convention already used for
  ExtensionControlService actions, so it shows up on the existing
  Admin Logs page instead of a bespoke history view.
- ExtensionActiveFlagCheck: the first check, built on the
  findExtensionsWithInconsistentActiveFlag() query added in the
  previous commit, for extensions whose active flag disagrees with
  whether any of their versions is active.
- ConsistencyAPI: GET /admin/consistency (summaries), GET
  /admin/consistency/{checkId}/findings, POST
  /admin/consistency/{checkId}/fix and .../fix/{entityId}.
- webui: a "Data Consistency" admin dashboard page showing one card per
  check with its live finding count; expanding a card lazily loads its
  findings with a "Fix" action per finding, and a "Fix all" action is
  available directly on the card without expanding it. A "Refresh"
  button re-fetches the check list on demand.
@netomi
netomi merged commit a77ab0c into main Aug 21, 2026
5 checks passed
@netomi
netomi deleted the admin-data-consistency branch August 21, 2026 22:12
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.

Need a way to check the database for consistency

1 participant