Repository navigation
Replies: 1 comment
|
Both are in v0.10.15-alpha: an index on |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Description
TranscriptChunk.recording_idhas no index, so every query that loads, counts or deletes the chunks of one recording scans the wholetranscript_chunktable, which also stores the embedding BLOBs. In the live schema the table only hasidx_user_speaker_nameandix_transcript_chunk_speaker_name; neither can serve arecording_idlookup.SQLite and PostgreSQL do not index foreign key columns automatically, and the model declares the column as a plain foreign key (
src/models/recording.py:510) with only those two indexes (src/models/recording.py:516,src/models/recording.py:521-522). This makes transcript editing, reprocessing, speaker rename and merge, chunk deletion and per-recording chunk counts scale with the total number of chunks in the whole installation instead of with the number of chunks in the recording.The paginated recordings list also pays an unnecessary sort. The endpoint orders the statement by
created_at(src/api/recordings.py:1745) and then builds the pagination count from that already ordered statement (src/api/recordings.py:1748-1749). The count only needs a number, so the database sorts the full accessible set twice per page request even though one of the two uses discards the order. On a library of 2,004 accessible recordings the count takes about 55 ms with the inherited sort and about 2.6 ms without it.Affected Version
v0.10.14-alpha
Requested Feature
Add an index on
transcript_chunk(recording_id)for both fresh and existing databases, and build the pagination count from an unordered statement. Both changes are additive, do not alter any API response, and can ship independently of any other performance work.Proposed Implementation
db.create_all(): adddb.Index('ix_transcript_chunk_recording', 'recording_id')to the__table_args__tuple ofTranscriptChunkinsrc/models/recording.py(next toidx_user_speaker_nameatsrc/models/recording.py:521-522), or attach adb.Indexto therecording_idcolumn (src/models/recording.py:510).db.create_all()does not alter tables that already exist (src/init_db.py:114). Inside_run_migrations(src/init_db.py:138), callcreate_index_if_not_exists(engine, 'ix_transcript_chunk_recording', 'transcript_chunk', 'recording_id'), the same helper used for the other recording indexes (src/init_db.py:246-247). The helper quotes table names per dialect, so the call works on SQLite and PostgreSQL alike (src/utils/database.py:272-322,src/utils/database.py:302-320).src/api/recordings.py: count from an unordered copy of the statement, for exampleunordered = stmt.order_by(None)and thenselect(db.func.count()).select_from(unordered.subquery()), in place ofselect(db.func.count()).select_from(stmt.subquery())(src/api/recordings.py:1748). Keep the orderedstmtfor the page query itself (src/api/recordings.py:1753, executed atsrc/api/recordings.py:1756).initialize_databaseon a database created with the older schema, and a test that asserts the count statement carries noORDER BY(for example by checkingstr(count_stmt)or by capturing the emitted SQL).Expected Behavior
TranscriptChunkrows of one recording usesix_transcript_chunk_recordinginstead of scanningtranscript_chunk.initialize_databasehave the index; on SQLite,PRAGMA index_list('transcript_chunk')showsix_transcript_chunk_recordingafter the upgrade.Root Cause Analysis
TranscriptChunkdeclaresrecording_idas a foreign key without an index (src/models/recording.py:510). The only indexes on the table areix_transcript_chunk_speaker_name(src/models/recording.py:516) and the compositeidx_user_speaker_name(src/models/recording.py:521-522), because neither SQLite nor PostgreSQL indexes foreign key columns automatically. Point queries that filter byrecording_idtherefore fall back to a full table scan.Examples are the per-recording chunk delete and count paths in
src/services/embeddings.py:561andsrc/services/embeddings.py:584, the chunk count before deletion atsrc/api/recordings.py:3274, the admin per-recording chunk count atsrc/api/admin.py:1120, and the migration backfill count atsrc/init_db.py:1176.The same filter is bound in the chunk queries at
src/services/embeddings.py:623-624andsrc/services/embeddings.py:791-792, where it is one condition among several.For the count, the endpoint builds the ordered list statement first (
src/api/recordings.py:1745) and then wraps it for the count (src/api/recordings.py:1748-1749). SQLAlchemy preserves theORDER BYinside the subquery, so SQLite reportsUSE TEMP B-TREE FOR ORDER BYfor a query that returns a single scalar.All reactions