From 5f7aabc6e76056aa08d935282e777c0ae01aacb9 Mon Sep 17 00:00:00 2001 From: Vladyslav Fedoriuk Date: Wed, 16 Aug 2023 23:06:55 +0200 Subject: [PATCH] Optimize dependencies parsing - Do not parse dependencies for a repo when revision did not change --- app/database.py | 3 ++ app/dependencies.py | 41 ++++++++++++++---- app/scrape.py | 13 +++++- db.sqlite3 | Bin 770048 -> 782336 bytes ...9d70_add_a_last_checked_revision_column.py | 29 +++++++++++++ 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 migrations/versions/ac7c35039d70_add_a_last_checked_revision_column.py diff --git a/app/database.py b/app/database.py index 0eec57d..c555d35 100644 --- a/app/database.py +++ b/app/database.py @@ -78,6 +78,9 @@ class Repo(Base): dependencies: Mapped[list["Dependency"]] = relationship( "Dependency", secondary="repo_dependency", back_populates="repos" ) + last_checked_revision: Mapped[str | None] = mapped_column( + String(255), nullable=True + ) __table_args__ = (UniqueConstraint("url", "source_graph_repo_id"),) diff --git a/app/dependencies.py b/app/dependencies.py index 9f2deba..478d245 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -2,6 +2,7 @@ import asyncio import subprocess from collections.abc import Sequence +from typing import NewType import aiofiles.tempfile import stamina @@ -10,17 +11,19 @@ from app.database import Repo from app.models import DependencyCreateData -async def run_command(*cmd: str) -> str: +async def run_command(*cmd: str, cwd: str | None = None) -> str: """ Run the given command in a subprocess and return the stdout as plain text. :param cmd: The command to run. + :param cwd: The working directory to run the command in. :return: The stdout result """ process = await asyncio.create_subprocess_exec( *cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cwd=cwd, ) stdout, stderr = await process.communicate() @@ -35,9 +38,12 @@ async def run_command(*cmd: str) -> str: return stdout.decode() +RevisionHash = NewType("RevisionHash", str) + + async def acquire_dependencies_data_for_repository( repo: Repo, -) -> list[DependencyCreateData]: +) -> tuple[RevisionHash, list[DependencyCreateData]]: """ Acquire dependencies for the given repository. @@ -61,6 +67,20 @@ async def acquire_dependencies_data_for_repository( directory, ) + # Get the latest commit hash + revision: str = await run_command( + "git", + "rev-parse", + "HEAD", + cwd=directory, + ) + + if repo.last_checked_revision == revision: + # Assume there are no new dependencies to return + # since all the repo dependencies have already + # been parsed. + return RevisionHash(revision), [] + # Parse the dependencies async for attempt in stamina.retry_context(on=RuntimeError, attempts=3): with attempt: @@ -75,10 +95,13 @@ async def acquire_dependencies_data_for_repository( dependencies_list = ( dependencies_list[2:] if len(dependencies_list) > 2 else [] ) - return [ - DependencyCreateData( - name=dependency.strip(), - ) - for dependency in dependencies_list - if dependency.strip() - ] + return ( + RevisionHash(revision), + [ + DependencyCreateData( + name=dependency.strip(), + ) + for dependency in dependencies_list + if dependency.strip() + ], + ) diff --git a/app/scrape.py b/app/scrape.py index 45311d0..9a86315 100644 --- a/app/scrape.py +++ b/app/scrape.py @@ -25,13 +25,24 @@ async def _create_dependencies_for_repo(session: AsyncSession, repo: Repo) -> No :param repo: A repo for which to create and assign the dependencies """ try: - dependencies_create_data = await acquire_dependencies_data_for_repository(repo) + ( + revision, + dependencies_create_data, + ) = await acquire_dependencies_data_for_repository(repo) except RuntimeError: # If the parsing fails, just skip creating the dependencies return if not dependencies_create_data: # If there are no dependencies, just skip creating the dependencies return + # Update the repo with the revision hash + if repo.last_checked_revision != revision: + update_repo_statement = ( + sqlalchemy.update(Repo) + .where(Repo.id == repo.id) + .values(last_checked_revision=revision) + ) + await session.execute(update_repo_statement) # Create dependencies - on conflict do nothing. insert_statement = sqlalchemy.dialects.sqlite.insert( Dependency diff --git a/db.sqlite3 b/db.sqlite3 index efd8fa9af1cbb7d37fd6baab12fe0a38b0c1a016..7ba1bd6a2afe3b3a9f1ed027bf2c4d89dad51edc 100644 GIT binary patch delta 20981 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 diff --git a/migrations/versions/ac7c35039d70_add_a_last_checked_revision_column.py b/migrations/versions/ac7c35039d70_add_a_last_checked_revision_column.py new file mode 100644 index 0000000..977996a --- /dev/null +++ b/migrations/versions/ac7c35039d70_add_a_last_checked_revision_column.py @@ -0,0 +1,29 @@ +"""Add a last_checked_revision column + +Revision ID: ac7c35039d70 +Revises: 90eb9d1f9267 +Create Date: 2023-08-16 22:35:25.314490 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "ac7c35039d70" +down_revision = "90eb9d1f9267" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "repo", sa.Column("last_checked_revision", sa.String(length=255), nullable=True) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("repo", "last_checked_revision") + # ### end Alembic commands ###