Skip to content

feat: improve templates list sorting#2983

Open
drankou wants to merge 2 commits into
mainfrom
template-list-pagination-eng-4207-perf
Open

feat: improve templates list sorting#2983
drankou wants to merge 2 commits into
mainfrom
template-list-pagination-eng-4207-perf

Conversation

@drankou

@drankou drankou commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Closes ENG-4207

Problem

Templates list took very long in prod for any team when sorting by name/cpu/memory: a non-sargable OR across envs/env_defaults forced a seq scan of all envs rows, and build-field sorts resolved every template's default build through env_build_assignments/env_builds laterals — O(team size) per page.

Changes

  • Drop cpu/memory sort + filter from the endpoint. With multiple tags per template, a single build value per row is an arbitrary pick anyway (team decision: resource columns belong on the builds page). Build fields in the response stay, display-only, resolved per returned page.

  • Drop name sort. Sort key lives in env_aliases → per-template lateral, taking too long on the largest teams. Search covers find-by-name UX.

  • Rewrite created_at/updated_at sorts as keyset-paginated UNION of two CTE branches (team templates + shared defaults), each index-driven.

  • New migration: partial index (team_id, updated_at DESC, id DESC) WHERE source = 'template' — cover updated_at sort

@linear-code

linear-code Bot commented Jun 11, 2026

Copy link
Copy Markdown

ENG-4207

@cla-bot cla-bot Bot added the cla-signed label Jun 11, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

I have reviewed the code changes and have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
2913 2 2911 6
View the top 3 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildRUN
Stack Traces | 0s run time
=== RUN   TestTemplateBuildRUN
=== PAUSE TestTemplateBuildRUN
=== CONT  TestTemplateBuildRUN
--- FAIL: TestTemplateBuildRUN (0.00s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/metrics::TestSandboxMetrics
Stack Traces | 4.84s run time
=== RUN   TestSandboxMetrics
=== PAUSE TestSandboxMetrics
=== CONT  TestSandboxMetrics
    sandbox_metrics_test.go:45: 
        	Error Trace:	.../api/metrics/sandbox_metrics_test.go:45
        	Error:      	Should NOT be empty, but was 0
        	Test:       	TestSandboxMetrics
--- FAIL: TestSandboxMetrics (4.84s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildRUN/Single_RUN_command
Stack Traces | 153s run time
=== RUN   TestTemplateBuildRUN/Single_RUN_command
=== PAUSE TestTemplateBuildRUN/Single_RUN_command
=== CONT  TestTemplateBuildRUN/Single_RUN_command
    build_template_test.go:133: test-ubuntu-run: [info] Building template c4bfece320gz9whxzqt2/a014aa1d-13c0-4721-9c13-bc7290fe5f70
    build_template_test.go:133: test-ubuntu-run: [info] [base] FROM ubuntu:22.04 [853db866ee97500d93b96c12372d9b297568ba646efed473c099998ba016e737]
    build_template_test.go:133: test-ubuntu-run: [info] Base Docker image size: 30 MB
    build_template_test.go:133: test-ubuntu-run: [info] Creating file system and pulling Docker image
    build_template_test.go:133: test-ubuntu-run: [info] Uncompressing layer sha256:40d16f30db405106ef8074779bdf41f012465c2a785bbeaa2eab9f2081099b47 30 MB
    build_template_test.go:133: test-ubuntu-run: [info] Uncompressing layer sha256:d64ba8c881f0dd5be1beb064d280123ac157d8daebb607e5f0bbb10cc36233a3 13 MB
    build_template_test.go:133: test-ubuntu-run: [info] Uncompressing layer sha256:8c4b1b28875140ed3abacaf16ad0d696f6bef912f52d2148f261a23e3349465b 168 B
    build_template_test.go:133: test-ubuntu-run: [info] Layers extracted
    build_template_test.go:133: test-ubuntu-run: [info] Root filesystem structure: bin, boot, dev, etc, home, lib, lib32, lib64, libx32, media, mnt, opt, proc, root, run, sbin, srv, sys, tmp, usr, var
    build_template_test.go:133: test-ubuntu-run: [info] Provisioning sandbox template
    build_template_test.go:133: test-ubuntu-run: [info] Provisioning was successful, cleaning up
    build_template_test.go:133: test-ubuntu-run: [info] Sandbox template provisioned
    build_template_test.go:133: test-ubuntu-run: [info] [base] DEFAULT USER user [b97b5fcc78b0f792b4eba66f81f8a39fd3a6d714ef9c470af8d9c70526254cc5]
    build_template_test.go:133: test-ubuntu-run: [info] [builder 1/1] RUN echo 'Hello, World!' [fee4392ea8b74715fa4dc3ee06a32c86fad386a78b32dd7036ea57170ab70553]
    build_template_test.go:133: test-ubuntu-run: [info] [builder 1/1] [stdout]: Hello, World!
    build_template_test.go:133: test-ubuntu-run: [info] [finalize] Finalizing template build [22385bb4f7feb0fd660dd4f18c4a00f87af47c4039c0186f9fe9e340faa018b6]
    build_template_test.go:133: test-ubuntu-run: [error] Build failed: build was cancelled
    build_template_test.go:166: Build failed: {<nil> build was cancelled <nil>}
--- FAIL: TestTemplateBuildRUN/Single_RUN_command (153.06s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@drankou drankou force-pushed the template-list-pagination-eng-4207-perf branch from fbe564f to 0eecb69 Compare June 12, 2026 14:58
…sort and filter

The name/cpu/memory sort variants resolved each template's default build
through per-template laterals with an OR spanning envs and env_defaults,
which is not sargable - Postgres seq-scanned all of envs (~96M rows, only
~1M templates) on every page, taking ~70s regardless of team size.

A template can have many tagged builds, each with its own latest build, so
a single cpu/memory value per template row is ambiguous anyway. Sorting and
filtering by build fields is dropped from the endpoint entirely; build
columns remain display-only, resolved for the returned page via a lateral.

The remaining sorts (name, created_at, updated_at) use two keyset-paginated
CTE branches (team templates + shared default templates), each with its own
ORDER BY and LIMIT, merged with UNION. updated_at gets a partial index
(team_id, updated_at DESC, id DESC) WHERE source = 'template' (~118 MB vs
~12 GB unfiltered, hypopg-estimated), making it index-driven like
created_at: 9s cold / 0.4s warm -> ms on the largest (370k-template) team.
@drankou drankou force-pushed the template-list-pagination-eng-4207-perf branch from 0eecb69 to 15b62ba Compare June 12, 2026 15:36
The name sort key lives in env_aliases, so each page pays a per-template
lateral over the whole team: ~2.1s per page on the largest (370k-template)
team even with all pages cached, since the cost is the ~370k aggregate
probes rather than I/O. Finding a template by name is served by the search
filter; alphabetical browsing does not justify keeping the one remaining
O(team size) query in the endpoint.

With this, every supported sort (created_at, updated_at) is an index-driven
top-N scan.
@drankou drankou force-pushed the template-list-pagination-eng-4207-perf branch from 564de0d to e891cbf Compare June 12, 2026 16:09
@drankou drankou marked this pull request as ready for review June 12, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant