diff --git a/backend/alembic/versions/c7d8e9f0a1b2_add_run_source_hash_composite_index.py b/backend/alembic/versions/c7d8e9f0a1b2_add_run_source_hash_composite_index.py new file mode 100644 index 00000000..42155453 --- /dev/null +++ b/backend/alembic/versions/c7d8e9f0a1b2_add_run_source_hash_composite_index.py @@ -0,0 +1,38 @@ +"""Add composite index (run_id, source_hash) for NOT EXISTS dedup + +Adds ix_translation_records_run_source_hash on translation_records(run_id, source_hash) +to accelerate the correlated NOT EXISTS subquery in orchestrator_query.get_run_records +when deduplicate=true. + +Revision ID: c7d8e9f0a1b2 +Revises: a1b2c3d4e5f6 +Create Date: 2026-06-04 13:30:00.000000 + +""" +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "c7d8e9f0a1b2" +down_revision: str | Sequence[str] | None = "a1b2c3d4e5f6" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + """Create composite index.""" + op.create_index( + "ix_translation_records_run_source_hash", + "translation_records", + ["run_id", "source_hash"], + postgresql_using="btree", + sqlite=True, + ) + + +def downgrade() -> None: + """Drop composite index.""" + op.drop_index("ix_translation_records_run_source_hash", + table_name="translation_records") diff --git a/backend/src/models/translate.py b/backend/src/models/translate.py index b524cf01..0e616a6b 100644 --- a/backend/src/models/translate.py +++ b/backend/src/models/translate.py @@ -142,6 +142,8 @@ class TranslationRecord(Base): __table_args__ = ( Index("ix_translation_records_run_status", "run_id", "status"), Index("ix_translation_records_source_hash_status", "source_hash", "status"), + Index("ix_translation_records_run_source_hash", "run_id", "source_hash", + comment="Composite index for NOT EXISTS dedup subquery"), ) # #endregion TranslationRecord diff --git a/backend/src/plugins/translate/orchestrator.py b/backend/src/plugins/translate/orchestrator.py index 01044573..22b17c0a 100644 --- a/backend/src/plugins/translate/orchestrator.py +++ b/backend/src/plugins/translate/orchestrator.py @@ -173,4 +173,3 @@ class TranslationOrchestrator: # endregion get_run_history # #endregion TranslationOrchestrator -# #endregion TranslationOrchestrator diff --git a/frontend/src/lib/components/translate/TranslationRunResult.svelte b/frontend/src/lib/components/translate/TranslationRunResult.svelte index 4a3121b9..eadbe9db 100644 --- a/frontend/src/lib/components/translate/TranslationRunResult.svelte +++ b/frontend/src/lib/components/translate/TranslationRunResult.svelte @@ -168,6 +168,7 @@ import { SvelteSet } from "svelte/reactivity"; /** Load next page of unique rows and append to records list. */ async function loadMoreRecords() { + if (recordsLoadingMore) return; recordsLoadingMore = true; const nextPage = recordsPage + 1; try { @@ -319,7 +320,7 @@ import { SvelteSet } from "svelte/reactivity";
- {_t.translate?.run?.status_label || 'Статус'} + {_t.translate?.run?.status_label || 'Status'} {getRunStatusLabel(status.status)}
@@ -586,7 +587,7 @@ import { SvelteSet } from "svelte/reactivity";
- {#if !allRecordsLoaded && recordsTotal > records.length} + {#if recordsTotal > 0}

{(_t.translate?.run?.showing_records || 'Showing {count} of {total} unique rows.') .replace('{count}', records.length)