mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04:00
Archive page: load history and tags eagerly (#809)
GET /api/archive/<id> loaded each history Archive's FileGroup and Tags with their own queries while serializing: two queries per history Archive. An Archive with 51 history entries ran 107 statements per request. Both the Archive and its history now use Archive.json_options(), so the count no longer grows with the history (5 statements for the same Archive). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
dab333124e
commit
a8dde44d4f
3 changed files with 40 additions and 1 deletions
|
|
@ -1243,7 +1243,8 @@ def get_archive(session: Session, archive_id: int, skip_viewed: bool = False) ->
|
|||
|
||||
def get_archive_by_file_group_id(session: Session, file_group_id: int, skip_viewed: bool = False) -> Archive:
|
||||
"""Get an Archive by its FileGroup ID."""
|
||||
archive = session.query(Archive).filter_by(file_group_id=file_group_id).one_or_none()
|
||||
archive = session.query(Archive).options(*Archive.json_options()) \
|
||||
.filter_by(file_group_id=file_group_id).one_or_none()
|
||||
if not archive:
|
||||
raise UnknownArchive(f'Unknown Archive with FileGroup ID: {file_group_id}')
|
||||
if not skip_viewed:
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ class Archive(Base, ModelHelper):
|
|||
FileGroup.url != None, # noqa
|
||||
) \
|
||||
.join(FileGroup, FileGroup.id == Archive.file_group_id) \
|
||||
.options(*Archive.json_options()) \
|
||||
.order_by(FileGroup.published_datetime))
|
||||
return history
|
||||
|
||||
|
|
|
|||
|
|
@ -590,3 +590,40 @@ async def test_delete_archive_atomic_when_some_tagged(async_client, test_session
|
|||
assert all(p.is_file() for p in untagged_paths)
|
||||
assert all(p.is_file() for p in tagged_paths)
|
||||
assert test_session.query(Archive).count() == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_archive_loads_history_eagerly(test_session, archive_factory, async_client, tag_factory):
|
||||
"""An Archive's page returns its history, with each Archive's Tags, without a query per history Archive."""
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
tag = await tag_factory()
|
||||
short = [archive_factory('example.com', 'https://example.com/short', tag_names=[tag.name]) for _ in range(2)]
|
||||
long = [archive_factory('example.com', 'https://example.com/long', tag_names=[tag.name]) for _ in range(6)]
|
||||
test_session.commit()
|
||||
|
||||
async def get(archive):
|
||||
statements = []
|
||||
|
||||
def count(conn, cursor, statement, *_):
|
||||
statements.append(statement)
|
||||
|
||||
event.listen(Engine, 'before_cursor_execute', count)
|
||||
try:
|
||||
request, response = await async_client.get(f'/api/archive/{archive.file_group_id}?skip_viewed=true')
|
||||
finally:
|
||||
event.remove(Engine, 'before_cursor_execute', count)
|
||||
assert response.status_code == HTTPStatus.OK, response.json
|
||||
return response.json, len(statements)
|
||||
|
||||
await get(short[-1]) # The first request does one-time work; measure the next ones.
|
||||
short_json, short_statements = await get(short[-1])
|
||||
long_json, long_statements = await get(long[-1])
|
||||
|
||||
assert len(short_json['history']) == 1
|
||||
assert len(long_json['history']) == 5
|
||||
assert long_json['file_group']['tags'] == [tag.name]
|
||||
assert all(i['tags'] == [tag.name] for i in long_json['history'])
|
||||
# Five history Archives cost no more queries than one.
|
||||
assert long_statements == short_statements, (short_statements, long_statements)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue