Skip to content

Recreate an empty DataStore table when a load fails - #29

Merged
jguo144 merged 2 commits into
masterfrom
jguo144/recreate-empty-datastore-table-on-load-failure
Jul 10, 2026
Merged

Recreate an empty DataStore table when a load fails#29
jguo144 merged 2 commits into
masterfrom
jguo144/recreate-empty-datastore-table-on-load-failure

Conversation

@jguo144

@jguo144 jguo144 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Problem

When a user sets a Data Dictionary type override that the data can't satisfy (e.g. a Vendor # column of integers set to timestamp), the next xloader upload fails and leaves the resource in a dead end:

  • xloader tries a direct COPY first; on a bad value it fails, then falls back to tabulator.
  • Both paths delete the existing table when field types change. The tabulator path then runs create_table + insert in a single transaction; the insert fails → the whole transaction rolls back → no table exists.
  • Type overrides are stored only in the DataStore table's Postgres column comments, not on the resource. So once the table is dropped, the table, its data dictionary, and datastore_active are all gone — the Data Dictionary tab disappears and the user has no in-UI way to fix the type. Only a full re-upload recovers it.

Fix

Capture the existing field definitions (types + data dictionary, including type overrides) before the load. If all load attempts fail and the table was dropped, recreate an empty table with those fields so the Data Dictionary stays visible and editable. The user then corrects the column type and re-uploads.

Key properties:

  • Best-effort — the recovery has its own try/except and never masks the real load error, which is still re-raised (job status stays error, log shows the true cause).
  • No-op when the table still exists — e.g. a partial/truncated load, matching "if any data loaded, don't recreate."
  • No-op for brand-new resources — nothing to preserve.
  • Does not persist the file hash or mark the table as containing all records, so re-uploading the corrected file still triggers a load (rather than being skipped by the hash gate).
  • Config-gated — new ckanext.xloader.recreate_empty_table_on_error option (default True) lets operators disable it.

Outcome

A failed upload leaves the resource recoverable — empty table, datastore_active True, Data Dictionary tab visible, original overrides intact — instead of a dead end.

Tests

Added TestRecreateEmptyTableOnError in test_jobs.py — unit tests that drive xloader_data_into_datastore_ with collaborators mocked (no DB/RQ worker), covering: recreate-on-drop (fields/overrides preserved, hash not persisted), no-op when table survives, no-op for new resources, config-disabled, and recovery-never-masks-original-error.

🤖 Generated with Claude Code

When a Data Dictionary type override cannot be satisfied by the data
(e.g. an integer column set to timestamp), the next xloader upload fails.
Both the direct COPY and tabulator paths delete the existing table when
field types change, and the tabulator reload runs create_table + insert
in a single transaction; the failing insert rolls the whole thing back,
leaving no table at all. Since type overrides live only in the DataStore
table's column comments, the table, its data dictionary, and
datastore_active are all gone -- the Data Dictionary tab disappears and
the only recovery is a full re-upload.

This captures the existing field definitions (types + data dictionary,
including type overrides) before the load, and if all load attempts fail
and the table was dropped, recreates an empty table with those fields so
the Data Dictionary stays visible and editable. The user can then fix the
column type and re-upload.

Recovery is best-effort (its own errors are logged, never masking the
real load error), skipped when the table still exists (a partial/
truncated load), skipped for brand-new resources with nothing to
preserve, and gated behind the new
ckanext.xloader.recreate_empty_table_on_error config option (default
True). It deliberately does not persist the file hash or mark the table
as containing all records, so re-uploading the corrected file still
triggers a load.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@orca-security-us orca-security-us Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Infrastructure as Code high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Vulnerabilities high 0   medium 0   low 0   info 0 View in Orca

Capture the set_resource_metadata mock inside the patch context instead
of reading jobs.set_resource_metadata after the context exits (which
returned the real, restored function).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jguo144
jguo144 merged commit 74a8ef5 into master Jul 10, 2026
16 checks passed
@jguo144
jguo144 deleted the jguo144/recreate-empty-datastore-table-on-load-failure branch July 10, 2026 14:37
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.

2 participants