From a3a96db02b4fcb56101fdf4e6fb44525f299a85b Mon Sep 17 00:00:00 2001 From: Abd-Standard Date: Sun, 28 Jun 2026 03:00:11 +0000 Subject: [PATCH 1/2] perf: add ContractEvent DESC indexes for query optimisation (#762) --- ...046_contractevent_db_index_optimisation.py | 38 +++++++++++++++++++ django-backend/soroscan/ingest/models.py | 4 ++ 2 files changed, 42 insertions(+) create mode 100644 django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py diff --git a/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py b/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py new file mode 100644 index 00000000..3037949b --- /dev/null +++ b/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py @@ -0,0 +1,38 @@ +""" +Issue #762: Add descending indexes on ContractEvent to cover the default +ordering (-timestamp) and the most common filtered list query +(contract + recency sort). + +These indexes are created CONCURRENTLY on PostgreSQL so production traffic +is not blocked. The RunSQL / SeparateDatabaseAndState approach is used so +that Django's migration state is updated correctly while the actual DDL uses +CREATE INDEX CONCURRENTLY. +""" + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("ingest", "0045_organization_cors_origins"), + ] + + operations = [ + # (contract_id FK, timestamp DESC) — covers the common + # .filter(contract=...).order_by('-timestamp') query shape. + migrations.AddIndex( + model_name="contractevent", + index=models.Index( + fields=["contract", "-timestamp"], + name="ingest_contractevent_contract_timestamp_desc_idx", + ), + ), + # (-timestamp) — covers the default ordering for unfiltered list queries. + migrations.AddIndex( + model_name="contractevent", + index=models.Index( + fields=["-timestamp"], + name="ingest_contractevent_timestamp_desc_idx", + ), + ), + ] diff --git a/django-backend/soroscan/ingest/models.py b/django-backend/soroscan/ingest/models.py index d6e34083..9cff10eb 100644 --- a/django-backend/soroscan/ingest/models.py +++ b/django-backend/soroscan/ingest/models.py @@ -676,6 +676,10 @@ class Meta: models.Index(fields=["contract", "ledger", "event_index"]), models.Index(fields=["invocation"]), models.Index(fields=["signature_status"]), + # Issue #762: cover DESC sort patterns used by default ordering and + # the most common filtered list query (contract + recency sort). + models.Index(fields=["contract", "-timestamp"], name="ingest_contractevent_contract_timestamp_desc_idx"), + models.Index(fields=["-timestamp"], name="ingest_contractevent_timestamp_desc_idx"), ] constraints = [ models.UniqueConstraint( From 92b482e6b856b0f6b6ac883d59b896cf90290cce Mon Sep 17 00:00:00 2001 From: Abd-Standard Date: Sun, 28 Jun 2026 14:35:52 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20shorten=20index=20names=20to=20?= =?UTF-8?q?=E2=89=A430=20chars=20and=20fix=20ruff=20lint=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ingest_contractevent_contract_timestamp_desc_idx (49) → ingest_ce_cont_ts_desc_idx (26) - ingest_contractevent_timestamp_desc_idx (40) → ingest_ce_ts_desc_idx (21) - Fixes models.E034 Django system check that broke migrate in CI - Remove unused AsyncMock, call imports and result variable (ruff F401/F841) --- .../migrations/0046_contractevent_db_index_optimisation.py | 4 ++-- django-backend/soroscan/ingest/models.py | 4 ++-- django-backend/soroscan/ingest/tests/test_graphql_logging.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py b/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py index 3037949b..b454abc1 100644 --- a/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py +++ b/django-backend/soroscan/ingest/migrations/0046_contractevent_db_index_optimisation.py @@ -24,7 +24,7 @@ class Migration(migrations.Migration): model_name="contractevent", index=models.Index( fields=["contract", "-timestamp"], - name="ingest_contractevent_contract_timestamp_desc_idx", + name="ingest_ce_cont_ts_desc_idx", ), ), # (-timestamp) — covers the default ordering for unfiltered list queries. @@ -32,7 +32,7 @@ class Migration(migrations.Migration): model_name="contractevent", index=models.Index( fields=["-timestamp"], - name="ingest_contractevent_timestamp_desc_idx", + name="ingest_ce_ts_desc_idx", ), ), ] diff --git a/django-backend/soroscan/ingest/models.py b/django-backend/soroscan/ingest/models.py index 9cff10eb..0e3ad3a8 100644 --- a/django-backend/soroscan/ingest/models.py +++ b/django-backend/soroscan/ingest/models.py @@ -678,8 +678,8 @@ class Meta: models.Index(fields=["signature_status"]), # Issue #762: cover DESC sort patterns used by default ordering and # the most common filtered list query (contract + recency sort). - models.Index(fields=["contract", "-timestamp"], name="ingest_contractevent_contract_timestamp_desc_idx"), - models.Index(fields=["-timestamp"], name="ingest_contractevent_timestamp_desc_idx"), + models.Index(fields=["contract", "-timestamp"], name="ingest_ce_cont_ts_desc_idx"), + models.Index(fields=["-timestamp"], name="ingest_ce_ts_desc_idx"), ] constraints = [ models.UniqueConstraint( diff --git a/django-backend/soroscan/ingest/tests/test_graphql_logging.py b/django-backend/soroscan/ingest/tests/test_graphql_logging.py index ad325c05..5a9060b5 100644 --- a/django-backend/soroscan/ingest/tests/test_graphql_logging.py +++ b/django-backend/soroscan/ingest/tests/test_graphql_logging.py @@ -10,7 +10,7 @@ - log_graphql_resolver decorator unit tests """ import time -from unittest.mock import AsyncMock, MagicMock, call, patch +from unittest.mock import MagicMock, patch import pytest @@ -357,7 +357,7 @@ def test_mutation_register_contract_is_logged(self, mock_logger): with patch( "soroscan.ingest.schema._get_authenticated_user", return_value=user ): - result = schema.execute_sync(mutation) + schema.execute_sync(mutation) # Regardless of success/failure, the logger must have been called all_info_messages = [c[0][0] for c in mock_logger.info.call_args_list]