perf(organizations): speed up filtered API lists
All checks were successful
CI/CD Pipeline / Quality Gate (push) Successful in 28s
CI/CD Pipeline / Build and Push Images (push) Successful in 10s
CI/CD Pipeline / Internal Notify (push) Successful in 0s
CI/CD Pipeline / Deploy Dev in Dokploy (push) Successful in 1s

This commit is contained in:
2026-05-14 17:08:03 +02:00
parent df89e498cc
commit 19a7d5a91c
10 changed files with 360 additions and 62 deletions

View File

@@ -12,6 +12,7 @@ from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from django.urls import reverse
from organizations.cache import invalidate_organization_api_cache
from organizations.filters import OrganizationFilter
from organizations.models import Organization, OrganizationDataSnapshot
from organizations.services import OrganizationDataSnapshotRefreshService
from rest_framework import status
@@ -195,6 +196,46 @@ class OrganizationsApiV2Test(APITestCase):
],
)
def test_list_default_uses_snapshot_summary_without_loading_full_data(self):
organization = Organization.objects.create(
name='ООО "Легкий снапшот"',
inn="7712345685",
kpp="771201008",
ogrn="1027700132203",
)
OrganizationDataSnapshot.objects.create(
organization=organization,
data={
"industrial": [{"id": index} for index in range(100)],
"fns_reports": [{"id": index} for index in range(50)],
},
registries=[],
)
with CaptureQueriesContext(connection) as captured:
response = self.client.get(
reverse("api_v2:organizations:organizations-list"),
{"inn": organization.inn, "has_registry": "false"},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
item = response.data["data"][0]
self.assertEqual(item["data"], {})
self.assertEqual(
item["data_sources"],
[
{"source": "fns_reports", "count": 50},
{"source": "industrial", "count": 100},
],
)
full_snapshot_data_queries = [
query["sql"]
for query in captured
if "ORGANIZATIONS_DATA_SNAPSHOT" in query["sql"].upper()
and '"data"' in query["sql"]
]
self.assertEqual(full_snapshot_data_queries, [])
def test_list_returns_snapshot_data_when_sources_are_requested(self):
organization = Organization.objects.create(
name='ООО "Явные данные"',
@@ -740,6 +781,18 @@ class OrganizationsApiV2Test(APITestCase):
self.assertEqual(no_registry.data["meta"]["pagination"]["total_count"], 1)
self.assertEqual(no_registry.data["data"][0]["uid"], str(without_registry.uid))
def test_has_registry_filter_uses_uncorrelated_identity_subqueries(self):
filterset = OrganizationFilter(
data={"has_registry": "true"},
queryset=Organization.objects.all(),
)
self.assertTrue(filterset.is_valid(), filterset.errors)
sql = str(filterset.qs.query).upper()
self.assertIn(" IN ", sql)
self.assertNotIn("EXISTS", sql)
def test_list_defaults_to_has_registry_true(self):
with_registry = Organization.objects.create(
name='ООО "Дефолтный реестр"',
@@ -852,6 +905,39 @@ class OrganizationsApiV2Test(APITestCase):
self.assertEqual(has_fns.data["meta"]["pagination"]["total_count"], 1)
self.assertEqual(has_fns.data["data"][0]["uid"], str(with_fns.uid))
def test_has_fns_reports_filter_does_not_preload_report_identities(self):
organization = Organization.objects.create(
name='ООО "Отчетность без preload"',
inn="7800000004",
kpp="780001004",
ogrn="1027700133004",
)
FinancialReport.objects.create(
external_id="fin-presence-no-preload",
ogrn=organization.ogrn,
file_name="fin_presence_no_preload.xlsx",
file_hash="d" * 64,
load_batch=1,
status=FinancialReport.Status.SUCCESS,
source=FinancialReport.SourceType.API,
)
with CaptureQueriesContext(connection) as captured:
response = self.client.get(
reverse("api_v2:organizations:organizations-list"),
{"has_fns_reports": "true", "has_registry": "false"},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["meta"]["pagination"]["total_count"], 1)
distinct_report_queries = [
query["sql"]
for query in captured
if "PARSERS_FINANCIAL_REPORT" in query["sql"].upper()
and "SELECT DISTINCT" in query["sql"].upper()
]
self.assertEqual(distinct_report_queries, [])
def test_limits_response_data_sources(self):
organization = Organization.objects.create(
name='ООО "Источник"',

View File

@@ -99,6 +99,10 @@ class OrganizationDataSnapshotRefreshServiceTest(TestCase):
snapshot.data["industrial"][0]["certificate_number"],
"SNAPSHOT-BATCH-CERT",
)
self.assertEqual(
snapshot.data_source_counts,
[{"source": "industrial", "count": 1}],
)
def test_refresh_for_fns_batch_matches_by_ogrn(self):
organization = Organization.objects.create(