• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kobotoolbox / kpi / 30450583610
82%
master: 76%

Build:
Build:
LAST BUILD BRANCH: dev-2566-reduce-refresh-frequency-and-harden-reliability-of-the-billingusage
DEFAULT BRANCH: master
Ran 29 Jul 2026 12:16PM UTC
Jobs 10
Files 893
Run time 2min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

29 Jul 2026 12:11PM UTC coverage: 81.931% (-0.006%) from 81.937%
30450583610

push

github

web-flow
perf(longRunningMigrations): avoid full index scan in LRM 0027 xform paging DEV-2427 (#7333)

### 📣 Summary

Speeds up an internal data migration that backfills missing submission
`root_uuid`s, so it no longer times out on projects with a very large
number of submissions.

### 👷 Description for instance maintainers

Long-running migration 0027 (`root_uuid` backfill) pages through XForms
by repeatedly finding the next batch of distinct `xform_id`s that still
have `Instance` rows with a null `root_uuid`. That pagination query used
a plain `SELECT DISTINCT xform_id ... ORDER BY xform_id LIMIT`, relying
on PostgreSQL to deduplicate as it scans.

PostgreSQL has no native loose ("skip") index scan: it has to walk every
matching index entry in order, including duplicates, before it can move
on to the next distinct value. When a single XForm has millions of
`Instance` rows with a null `root_uuid`, the scan has to read all of
them just to advance past that one XForm, even though the query only
asks for 100 distinct ids.

In production this was confirmed with `EXPLAIN`, the planner estimated
million of rows to walk to produce the next 100 distinct ids. In
practice, the query got killed by PostgreSQL's `statement_timeout`
(4200s in Celery workers) with `canceling statement due to statement
timeout`, stalling the migration on that page of XForms.

Rewrote the pagination query as a recursive CTE that seeks directly to
the next distinct `xform_id` at each step (`xform_id > current ORDER BY
xform_id LIMIT 1`), instead of scanning through every duplicate row.
Verified against production data: the same lookup that used to time out
at 4200s now returns in under 1s.

### 💭 Notes

- No behavior change from the migration's point of view: same XForms are
found, same order, same batch size (`CHUNK_SIZE`). Only the SQL used to
find them changed.
- Timeout is barely impossible to reproduce locally without a huge
amount of submissions.

9488 of 12791 branches covered (74.18%)

30721 of 37496 relevant lines covered (81.93%)

5.51 hits per line

Coverage Regressions

Lines Coverage ∆ File
667
5.22
-74.11% kpi/utils/query_parser/canopy_autogenerated_parser/__init__.py
319
26.46
-47.68% kpi/models/import_export_task.py
192
34.68
-29.86% kpi/serializers/v2/asset.py
188
59.1
-32.58% kpi/models/asset.py
123
28.65
-33.24% kpi/views/v2/asset.py
117
25.28
-65.73% kpi/utils/query_parser/query_parser.py
97
29.35
-48.26% kobo/apps/project_ownership/models/transfer.py
80
71.12
-21.8% kpi/mixins/object_permission.py
70
23.4
-74.47% kpi/utils/xlsform_preprocessors/kobomatrix_handler.py
67
25.35
-31.46% kpi/filters.py
64
12.36
-71.91% kobo/apps/reports/report_data.py
63
32.52
-51.22% kobo/apps/accounts/forms.py
57
32.58
-64.04% kpi/deployment_backends/mock_backend.py
54
35.34
-40.6% kpi/paginators.py
53
38.71
-8.73% kpi/deployment_backends/openrosa_backend.py
50
54.57
-12.35% kpi/deployment_backends/base_backend.py
49
28.28
-49.49% kpi/password_validation.py
48
59.0
-24.0% kpi/utils/xml.py
44
37.07
-37.93% kpi/models/asset_snapshot.py
42
0.0
0.0% kobo/apps/long_running_migrations/jobs/0027_backfill_remaining_root_uuid.py
41
45.06
-9.43% kobo/apps/audit_log/models.py
39
35.2
-31.2% kpi/models/paired_data.py
38
34.52
-45.24% kobo/apps/audit_log/base_views.py
37
38.83
-35.92% kpi/utils/usage_calculator.py
35
52.28
-17.77% kpi/mixins/formpack_xlsform_utils.py
35
18.75
-72.92% kpi/utils/xlsform_preprocessors/koborank_handler.py
34
41.74
-29.57% kpi/utils/autoname.py
34
37.14
-48.57% kpi/utils/cache.py
32
53.92
-31.37% kpi/models/asset_file.py
31
62.63
-31.31% kpi/utils/django_orm_helper.py
29
21.95
-70.73% kpi/utils/xlsform_preprocessors/koboscore_handler.py
27
0.0
-72.97% kobo/apps/accounts/adapter.py
27
16.67
-18.0% kobo/apps/project_ownership/utils.py
24
40.0
-53.33% kpi/fields/jsonschema_form_field.py
24
54.84
-38.71% kpi/utils/kobo_to_xlsform.py
23
51.06
-48.94% kpi/views/v2/authorized_application_user.py
22
31.58
-38.6% kpi/utils/mailer.py
21
65.45
-9.55% kpi/permissions.py
21
42.79
-9.77% kpi/renderers.py
21
23.08
-53.85% kpi/utils/asset_translation_utils.py
21
40.58
-30.43% kpi/utils/data_exports.py
21
26.47
-61.76% kpi/utils/export_cleanup.py
21
66.83
-10.24% kpi/utils/mongo_helper.py
20
52.38
-31.75% hub/models/extra_user_detail.py
20
0.0
-68.97% kobo/apps/audit_log/tasks.py
19
58.18
-34.55% kpi/signals.py
18
35.56
-40.0% kpi/fields/file.py
18
37.84
-48.65% kpi/serializers/v2/create_user.py
18
60.56
-25.35% kpi/utils/sluggify.py
18
37.93
-62.07% kpi/views/token.py
17
46.99
-10.24% kobo/apps/organizations/views.py
17
17.86
-60.71% kpi/utils/submission.py
16
60.0
-11.43% kobo/apps/audit_log/signals.py
13
37.14
-18.57% hub/utils/i18n.py
13
26.09
-56.52% kpi/fields/relative_prefix_hyperlinked_related.py
13
47.13
-14.94% kpi/tasks.py
12
43.06
-16.67% kobo/apps/project_ownership/serializers/invite.py
12
82.76
-13.79% kpi/utils/asset_content_analyzer.py
12
22.73
-54.55% kpi/utils/bugfix.py
11
69.44
-30.56% kpi/models/asset_user_partial_permission.py
11
23.31
-3.09% kpi/serializers/v2/asset_permission_assignment.py
11
55.56
-40.74% kpi/utils/xlsform_preprocessors/base_handlers.py
10
60.71
-35.71% kobo/apps/audit_log/utils.py
10
66.2
-14.08% kobo/apps/project_ownership/models/invite.py
10
14.8
-5.1% kobo/apps/subsequences/utils/versioning.py
10
72.92
-20.83% kpi/models/asset_version.py
10
35.29
-58.82% kpi/utils/strings.py
10
38.1
-23.81% kpi/utils/urls.py
9
35.29
-52.94% kpi/serializers/v1/authorized_application_user.py
7
61.76
-20.59% kobo/apps/storage_backends/base.py
7
21.6
-5.6% kobo/apps/stripe/utils/subscription_limits.py
7
32.14
-25.0% kobo/apps/subsequences/utils/supplement_data.py
7
57.14
-33.33% kpi/fields/paginated_api.py
7
53.33
-46.67% kpi/maintenance_tasks.py
7
70.37
-25.93% kpi/models/extra_project_metadata_field.py
7
72.38
-6.67% kpi/utils/object_permission.py
7
71.43
-20.0% kpi/utils/project_views.py
6
57.58
-3.64% kobo/apps/organizations/models.py
6
43.75
-37.5% kobo/apps/project_ownership/filters.py
6
72.09
-13.95% kobo/apps/project_ownership/serializers/transfer.py
6
0.0
-100.0% kobo/apps/project_views/tasks.py
6
74.51
-11.76% kpi/context_processors.py
6
42.86
-42.86% kpi/mixins/asset.py
6
53.85
-46.15% kpi/utils/files.py
6
22.32
-5.36% kpi/utils/models.py
5
30.0
-7.14% kobo/apps/organizations/permissions.py
5
47.22
-13.89% kobo/apps/organizations/tasks.py
5
53.85
-6.41% kpi/deployment_backends/kc_access/utils.py
5
57.14
-23.81% kpi/fields/writable_json.py
5
75.0
-20.83% kpi/models/authorized_application.py
5
29.41
-14.71% kpi/utils/rename_xls_sheet.py
5
36.14
-6.02% kpi/utils/storage.py
4
53.85
-30.77% kobo/apps/accounts/templatetags/customize_email_content.py
4
77.5
-10.0% kobo/apps/kobo_auth/signals.py
4
50.0
-10.53% kobo/apps/organizations/utils.py
4
89.47
-7.02% kpi/deployment_backends/mixin.py
4
33.33
-66.67% kpi/utils/chunked_delete.py
4
80.0
-20.0% kpi/views/v1_api_gone.py
3
65.79
-7.89% kobo/apps/data_collectors/signals.py
3
89.19
-8.11% kobo/apps/project_views/models/project_view.py
3
76.47
-17.65% kpi/utils/pyxform_compatibility.py
3
61.54
-23.08% kpi/versioning.py
2
64.21
-2.11% kobo/apps/openrosa/libs/utils/middleware.py
2
91.3
-8.7% kobo/apps/project_ownership/views/invite.py
2
83.33
-6.67% kobo/urls.py
2
86.09
-1.74% kpi/exceptions.py
2
64.52
-6.45% kpi/mixins/standardize_searchable_field.py
2
96.3
-3.7% kpi/mixins/xls_exportable.py
2
82.61
-8.7% kpi/serializers/v2/asset_version.py
2
90.0
-10.0% kpi/utils/absolute_paths.py
2
47.06
-2.94% kpi/utils/hash.py
1
44.44
-5.56% kobo/apps/accounts/templatetags/get_provider_appname.py
1
86.11
-2.78% kobo/apps/openrosa/apps/logger/fields.py
1
67.95
-1.28% kpi/authentication.py
1
57.89
-2.63% kpi/db_routers.py
Jobs
ID Job ID Ran Files Coverage
1 30450583610.1 29 Jul 2026 12:16PM UTC 889
49.43
2 30450583610.2 29 Jul 2026 12:16PM UTC 889
44.17
3 30450583610.3 29 Jul 2026 12:18PM UTC 889
52.74
4 30450583610.4 29 Jul 2026 12:18PM UTC 891
45.52
5 30450583610.5 29 Jul 2026 12:19PM UTC 889
58.92
6 30450583610.6 29 Jul 2026 12:19PM UTC 891
50.75
7 30450583610.7 29 Jul 2026 12:20PM UTC 891
53.99
8 30450583610.8 29 Jul 2026 12:20PM UTC 891
67.07
9 30450583610.9 29 Jul 2026 12:21PM UTC 891
60.07
10 30450583610.10 29 Jul 2026 12:22PM UTC 893
69.39
Source Files on build 30450583610
  • Tree
  • List 893
  • Changed 1
  • Source Changed 0
  • Coverage Changed 1
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • e4c14af5 on github
  • Prev Build on release/2.026.30 (#30306931713)
  • Next Build on release/2.026.30 (#30487945096)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc