perf(server): drop three unused indexes on the sessions table - #6884
Open
otavio wants to merge 1 commit into
Open
perf(server): drop three unused indexes on the sessions table#6884otavio wants to merge 1 commit into
otavio wants to merge 1 commit into
Conversation
This was referenced Aug 11, 2026
sessions_username_idx, sessions_type_idx and sessions_closed_started_idx back no filter, no sort and no constraint, while costing an index insert on every session row written. Over 66 days of production counters they served 2, 0 and 0 scans, against 204,400 on sessions_started_at_idx. username and type are unreachable by construction rather than merely unused: the session list accepts exactly three filter fields (device_uid, closed, active) and rejects anything else at the route, its sort is hardcoded to started_at with no user-selectable alternative, and cloud adds no session filters of its own. The type predicates that do exist are all on session_events, a different table, already served by its session_id and seat indexes. sessions_closed_started_idx is redundant rather than unused, which is why its 0 scans needed explaining before trusting them: the recording-conversion worker runs WHERE closed AND recorded AND NOT converted ORDER BY started_at DESC, which looks tailor-made for (closed, started_at). But closed is a near-constant — a session is closed for all but the minutes it is live — so the index is a strictly fatter duplicate of sessions_started_at_idx, 42 MB against 26 MB, and loses to it on cost for every shape that exists. If that worker ever shows up as a cost, what it wants is a partial index over the unconverted backlog alone. sessions_namespace_id_idx stays despite its 63 scans: deleting a namespace cascades to sessions, and without it that is a sequential scan over 1.19 M rows. Unlike the devices pair in 017 there is no read-path trade here, so nothing gets slower. Fixes: shellhub-io/team#199
gustavosbarreto
force-pushed
the
perf/drop-unused-session-indexes
branch
from
August 12, 2026 13:42
f9be886 to
84ff059
Compare
gustavosbarreto
approved these changes
Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migration
020drops three indexes onsessionsthat back no filter, no sort and no constraint,while costing an index insert on every session row written. Over 66 days of production counters
they served 2, 0 and 0 scans, against 204,400 on
sessions_started_at_idx.sessions_username_idxsessions_type_idxsessions_closed_started_idxUnlike the
devicespair in #6883 there is no read-path trade to weigh here: nothing queriesthese columns, so nothing gets slower. Dropping an index reclaims its space immediately, so no
repack is needed either.
usernameandtypeare unreachable, not merely unusedVerified against the source in both
shellhubandcloudrather than trusted from counters:device_uid,closed,active(
SessionFilterFields) — and rejects anything else at the route before it reaches SQL.ListSessionshardcodesSorter{By: "started_at", Order: desc, Tiebreak: "id"}. There is noSessionSortFields.cloudadds no session filters or sorts of its own.WHERE/ORDER BYon either column exists in either repo. Thetype = ?predicates that doexist are all on
session_events, a different table, already served by itssession_id/seatindexes.
sessions_closed_started_idxis redundant, which is why 0 scans needed explainingThis one does have a consumer, so the counter alone would have been misleading. The
recording-conversion worker runs
WHERE recorded AND closed AND converted = ? ORDER BY started_at DESC LIMIT 10— close to atextbook match for
(closed, started_at). The query runs on every cron tick; the index is simplynever chosen.
The reason is that its leading column is a near-constant: a session is
closedfor all but theminutes it is live, so
(closed, started_at)is a strictly fatter duplicate ofsessions_started_at_idx— 42 MB against 26 MB — offering the planner nothing extra for eitherthe filter or the
ORDER BY. The smaller index wins on cost every time, which is exactly what the204,400-vs-0 split shows.
If that worker ever shows up as a cost, the index it wants is a partial one over the backlog
alone, not this:
A few pages instead of 42 MB. Not included here — that would be speculation.
sessions_namespace_id_idxstaysDespite only 63 scans. Deleting a namespace cascades to
sessions, and without this index that isa sequential scan over a 1.19 M-row table. The cascade argument does not depend on
idx_scanatall, so it holds regardless of how shellhub-io/team#200 resolves.
Testing
Full
pgstore suite green against a schema built from001through020, and verifiedend-to-end through bun's runner on the dev stack:
020applies at boot and leavessessionswithsessions_pkey,sessions_namespace_id_idx,sessions_device_id_idxandsessions_started_at_idx.Fixes shellhub-io/team#199.