Skip to content

Commit

Permalink
perf: move version downloads to version queries
Browse files Browse the repository at this point in the history
  • Loading branch information
MiniDigger committed Feb 2, 2025
1 parent 9448e2b commit b0c1b1b
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@ public interface ProjectVersionsDAO {
@SqlQuery("SELECT * FROM project_versions WHERE project_id = :projectId AND version_string = :versionString")
ProjectVersionTable getProjectVersion(long projectId, String versionString);

@SqlQuery("SELECT pv.*" +
" FROM project_versions pv" +
" JOIN project_channels pc ON pv.channel_id = pc.id" +
" WHERE pc.id = :channelId" +
" ORDER BY pv.created_at DESC")
ProjectVersionTable getLastVersionOnChannel(long channelId);

@SqlQuery("SELECT pv.* FROM project_versions pv" +
" JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id" +
" JOIN platform_versions v ON pvpd.platform_version_id = v.id" +
Expand All @@ -68,13 +61,4 @@ public interface ProjectVersionsDAO {
" pv.version_string = :versionString" +
" LIMIT 1")
ProjectVersionTable getProjectVersionTableWithProjectSlug(String slug, String versionString);

@SingleValue
@UseEnumStrategy(EnumStrategy.BY_ORDINAL)
@SqlQuery("SELECT array_agg(DISTINCT plv.platform)" +
" FROM project_versions pv" +
" JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id" +
" JOIN platform_versions plv ON pvpd.platform_version_id = plv.id" +
" WHERE pv.id = :versionId GROUP BY pv.id")
List<Platform> getVersionPlatforms(long versionId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public interface VersionsApiDAO {
pv.description,
coalesce((SELECT sum(pvd.downloads) FROM project_versions_downloads pvd WHERE pv.id = pvd.version_id), 0) vs_totalDownloads,
(select array_agg(d) from (SELECT pvd.platform, sum(pvd.downloads) FROM project_versions_downloads pvd WHERE pv.id = pvd.version_id GROUP BY pvd.platform) d) vs_platformDownloads,
u.name author,
(SELECT ARRAY[p.owner_name, p.slug] FROM projects p WHERE p.id = pv.project_id limit 1) AS project_namespace,
(select u.name from users u where u.id = pv.author_id) as author,
pv.review_state,
pc.created_at pc_created_at,
pc.name pc_name,
Expand All @@ -51,10 +52,18 @@ public interface VersionsApiDAO {
WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'channel') THEN 'CHANNEL'
WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'
ELSE 'NONE'
END AS pinnedStatus
END AS pinnedStatus,
(SELECT json_agg(json_build_object('file_size', file_size,
'hash', hash,
'file_name', file_name,
'external_url', external_url,
'platforms', platforms,
'download_platform', download_platform)) AS value
FROM project_version_downloads
WHERE version_id = pv.id
GROUP BY version_id) AS downloads
FROM project_versions pv
JOIN project_channels pc ON pv.channel_id = pc.id
LEFT JOIN users u ON pv.author_id = u.id
WHERE
<if(!canSeeHidden)>
(pv.visibility = 0
Expand Down Expand Up @@ -84,6 +93,7 @@ WITH sq AS (SELECT array_agg(DISTINCT plv.platform) platforms, array_agg(DISTINC
pv.description,
coalesce((SELECT sum(pvd.downloads) FROM project_versions_downloads pvd WHERE pv.id = pvd.version_id), 0) vs_totalDownloads,
(select array_agg(d) from (SELECT pvd.platform, sum(pvd.downloads) FROM project_versions_downloads pvd WHERE pv.id = pvd.version_id GROUP BY pvd.platform) d) vs_platformDownloads,
(SELECT ARRAY[p.owner_name, p.slug] FROM projects p WHERE p.id = pv.project_id limit 1) AS project_namespace,
(select u.name from users u where u.id = pv.author_id) as author,
pv.review_state,
pc.created_at pc_created_at,
Expand All @@ -95,7 +105,16 @@ WITH sq AS (SELECT array_agg(DISTINCT plv.platform) platforms, array_agg(DISTINC
WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'channel') THEN 'CHANNEL'
WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'
ELSE 'NONE'
END AS pinnedStatus
END AS pinnedStatus,
(SELECT json_agg(json_build_object('file_size', file_size,
'hash', hash,
'file_name', file_name,
'external_url', external_url,
'platforms', platforms,
'download_platform', download_platform)) AS value
FROM project_version_downloads
WHERE version_id = pv.id
GROUP BY version_id) AS downloads
FROM project_versions pv
JOIN project_channels pc ON pv.channel_id = pc.id
<if(platformfilter)>INNER JOIN sq ON pv.id = sq.version_id<endif>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.papermc.hangar.db.mappers;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.papermc.hangar.model.api.project.version.FileInfo;
import io.papermc.hangar.model.api.project.version.PlatformVersionDownload;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.service.internal.file.FileService;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.EnumMap;
import java.util.Map;
import org.jdbi.v3.core.mapper.ColumnMapper;
import org.jdbi.v3.core.statement.StatementContext;
import org.springframework.stereotype.Component;

@Component
public class VersionDownloadsMapper implements ColumnMapper<Map<Platform, PlatformVersionDownload>> {

private final ObjectMapper objectMapper;
private final FileService fileService;

public VersionDownloadsMapper(final ObjectMapper objectMapper, final FileService fileService) {
this.objectMapper = objectMapper;
this.fileService = fileService;
}

@Override
public Map<Platform, PlatformVersionDownload> map(final ResultSet r, final int columnNumber, final StatementContext ctx) throws SQLException {
final String raw = r.getString(columnNumber);
final Map<Platform, PlatformVersionDownload> result = new EnumMap<>(Platform.class);
final String[] projectNamespace = (String[]) r.getArray("project_namespace").getArray();
final String version = r.getString("version_string");
try {
final JsonNode parsedDownloads = this.objectMapper.readTree(raw);
for (final JsonNode download : parsedDownloads) {
final JsonNode platforms = download.get("platforms");
final String fileName = download.get("file_name").asText();
final long fileSize = download.get("file_size").asLong();
final String hash = download.get("hash").asText();
final String externalUrl = download.get("external_url").asText();
final FileInfo fileInfo = "null".equals(fileName) ? null : new FileInfo(fileName, fileSize, hash);
final Platform downloadPlatform = Platform.values()[download.get("download_platform").asInt()];
final String downloadUrl = fileInfo != null ? this.fileService.getVersionDownloadUrl(projectNamespace[0], projectNamespace[1], version, downloadPlatform, fileInfo.getName()) : null;
final PlatformVersionDownload pvd = new PlatformVersionDownload(fileInfo, "null".equals(externalUrl) ? null : externalUrl, downloadUrl);
for (final JsonNode platformId : platforms) {
result.put(Platform.values()[platformId.asInt()], pvd);
}
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,19 @@ public class Version extends VersionCompact {
private final Map<Platform, Set<String>> platformDependencies = new EnumMap<>(Platform.class);
private final Map<Platform, List<String>> platformDependenciesFormatted = new EnumMap<>(Platform.class);

public Version(final OffsetDateTime createdAt, final long id, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus) {
super(createdAt, id, name, visibility, description, stats, author, reviewState, channel, pinnedStatus);
public Version(final OffsetDateTime createdAt,
final long id,
@ColumnName("version_string") final String name,
final Visibility visibility,
final String description,
@Nested("vs") final VersionStats stats,
final String author,
@EnumByOrdinal final ReviewState reviewState,
@Nested("pc") final ProjectChannel channel,
final PinnedStatus pinnedStatus,
final Map<Platform, PlatformVersionDownload> downloads
) {
super(createdAt, id, name, visibility, description, stats, author, reviewState, channel, pinnedStatus, downloads);
}

public Map<Platform, Set<PluginDependency>> getPluginDependencies() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import io.papermc.hangar.model.common.projects.ReviewState;
import io.papermc.hangar.model.common.projects.Visibility;
import java.time.OffsetDateTime;
import java.util.EnumMap;
import java.util.Map;
import org.jdbi.v3.core.enums.EnumByName;
import org.jdbi.v3.core.enums.EnumByOrdinal;
Expand All @@ -28,9 +27,19 @@ public class VersionCompact extends Model implements Named, Visible, Identified
private final ReviewState reviewState;
private final ProjectChannel channel;
private final PinnedStatus pinnedStatus;
private final Map<Platform, PlatformVersionDownload> downloads = new EnumMap<>(Platform.class);

protected VersionCompact(final OffsetDateTime createdAt, final long id, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus) {
private final Map<Platform, PlatformVersionDownload> downloads;

protected VersionCompact(final OffsetDateTime createdAt,
final long id,
@ColumnName("version_string") final String name,
final Visibility visibility,
final String description,
@Nested("vs") final VersionStats stats,
final String author,
@EnumByOrdinal final ReviewState reviewState,
@Nested("pc") final ProjectChannel channel,
final PinnedStatus pinnedStatus,
final Map<Platform, PlatformVersionDownload> downloads) {
super(createdAt);
this.id = id;
this.name = name;
Expand All @@ -41,6 +50,7 @@ protected VersionCompact(final OffsetDateTime createdAt, final long id, @ColumnN
this.reviewState = reviewState;
this.channel = channel;
this.pinnedStatus = pinnedStatus;
this.downloads = downloads;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ private SitemapGenerator addUser(UserTable userTable, SitemapGenerator generator

// add all versions of said projects
projects.forEach(p -> {
// TODO version compact, we dont need downloads here
final SortedMap<Long, Version> projectVersions = this.versionsApiDAO.getVersions(p.getId(), false, null, new RequestPagination(100L, 0L));
projectVersions.values().stream()
.filter(pv -> pv.getVisibility() == Visibility.PUBLIC || pv.getVisibility() == Visibility.NEEDSAPPROVAL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -172,9 +173,9 @@ private <T> CompletableFuture<T> supply(final Supplier<T> supplier) {
}

final Long userId = this.getHangarUserId();
final Long versionId = this.versionsApiDAO.getVersions(projectId, false, userId, pagination).keySet().stream().findAny().orElse(null);
if (versionId != null) {
return this.versionsApiDAO.getVersion(versionId, this.getGlobalPermissions().has(Permission.SeeHidden), userId);
final SortedMap<Long, Version> versions = this.versionsApiDAO.getVersions(projectId, this.getGlobalPermissions().has(Permission.SeeHidden), userId, pagination);
if (!versions.isEmpty()) {
return versions.values().iterator().next();
}

// Try again with any channel, else empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import io.papermc.hangar.HangarComponent;
import io.papermc.hangar.db.dao.internal.table.versions.downloads.ProjectVersionDownloadsDAO;
import io.papermc.hangar.exceptions.HangarApiException;
import io.papermc.hangar.model.api.project.version.FileInfo;
import io.papermc.hangar.model.api.project.version.PlatformVersionDownload;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.db.projects.ProjectTable;
import io.papermc.hangar.model.db.versions.ProjectVersionTable;
Expand All @@ -13,9 +11,6 @@
import io.papermc.hangar.service.internal.file.S3FileService;
import io.papermc.hangar.service.internal.projects.ProjectService;
import io.papermc.hangar.service.internal.uploads.ProjectFiles;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
Expand All @@ -39,19 +34,6 @@ public DownloadService(final ProjectFiles projectFiles, final ProjectVersionDown
this.projectService = projectService;
}

public Map<Platform, PlatformVersionDownload> getDownloads(final String user, final String project, final String version, final long versionId) {
final Map<Platform, PlatformVersionDownload> versionDownloadsMap = new EnumMap<>(Platform.class);
final List<ProjectVersionDownloadTable> downloads = this.downloadsDAO.getDownloads(versionId);
for (final ProjectVersionDownloadTable download : downloads) {
final FileInfo fileInfo = download.getFileName() != null ? new FileInfo(download.getFileName(), download.getFileSize(), download.getHash()) : null;
final String downloadUrl = fileInfo != null ? this.fileService.getVersionDownloadUrl(user, project, version, download.getDownloadPlatform(), fileInfo.getName()) : null;
for (final Platform platform : download.getPlatforms()) {
versionDownloadsMap.put(platform, new PlatformVersionDownload(fileInfo, download.getExternalUrl(), downloadUrl));
}
}
return versionDownloadsMap;
}

public ResponseEntity<?> downloadVersion(final ProjectTable project, final ProjectVersionTable version, final Platform platform) {
final ProjectVersionDownloadTable download = this.downloadsDAO.getDownloadByPlatform(version.getVersionId(), platform);
if (download == null) {
Expand Down
Loading

0 comments on commit b0c1b1b

Please sign in to comment.