From 999ebb7b7b11d102bb22af9098de81302bad7b35 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 4 Sep 2026 11:05:28 +0200 Subject: [PATCH] feat: Optimize extensionQuery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - Root cause found: LocalVSCodeService.extensionQuery (backing /vscode/gallery/extensionquery) unconditionally fetched every active version — all pre-releases included, unbounded by default — for every matched extension, solely to derive the single "latest version" for display, even when the client's query flags requested no version details at all. The code even carried a comment acknowledging "extensionquery is really slow." - Fix: Added a target-platform-aware bulk "latest version per extension" query (ExtensionVersionJooqRepository.findLatest(ids, targetPlatform) / RepositoryService.findLatestVersions(ids, targetPlatform)), mirroring the efficient CROSS APPLY pattern already used by the REST search path (LocalRegistryService). LocalVSCodeService.extensionQuery now: (1) only fetches the full active-version list when a flag actually needs it (FLAG_INCLUDE_VERSIONS/FLAG_INCLUDE_VERSION_PROPERTIES/FLAG_INCLUDE_LATEST_VERSION_ONLY), and (2) always computes the "latest" metadata via the new direct DB query instead of pulling the full history into Java and sorting it. - Verified: full ./gradlew test suite passes (had to update mocks in VSCodeAPITest, LocalVSCodeServiceTest, and add coverage in RepositoryServiceSmokeTest for the new repository method). --- .../openvsx/adapter/LocalVSCodeService.java | 20 ++++++++++++------- .../ExtensionVersionJooqRepository.java | 6 +++++- .../repositories/RepositoryService.java | 4 ++++ .../adapter/LocalVSCodeServiceTest.java | 1 + .../openvsx/adapter/VSCodeAPITest.java | 5 +++++ .../RepositoryServiceSmokeTest.java | 1 + 6 files changed, 29 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/eclipse/openvsx/adapter/LocalVSCodeService.java b/server/src/main/java/org/eclipse/openvsx/adapter/LocalVSCodeService.java index 487010a52..ed9818bdd 100644 --- a/server/src/main/java/org/eclipse/openvsx/adapter/LocalVSCodeService.java +++ b/server/src/main/java/org/eclipse/openvsx/adapter/LocalVSCodeService.java @@ -213,8 +213,18 @@ public ExtensionQueryResult extensionQuery(ExtensionQueryParam param, int defaul // see https://github.com/eclipse/openvsx/issues/1394 var extensionsMap = extensionsList.stream() .collect(Collectors.toMap(Extension::getId, Function.identity(), (a, b) -> a)); - List allActiveExtensionVersions = repositories - .findActiveExtensionVersions(extensionsMap.keySet(), targetPlatform, maxPreReleaseVersions); + + // The full active version list (all pre-releases included, unless capped) is only needed to populate + // the response's per-extension version list, which itself is only included for these flags. Skipping + // the fetch otherwise avoids pulling an extension's entire (potentially unbounded) version history + // just to compute "latest", which repositories.findLatestVersions below does directly in the database. + var needsVersionList = test(flags, FLAG_INCLUDE_LATEST_VERSION_ONLY) + || test(flags, FLAG_INCLUDE_VERSIONS) + || test(flags, FLAG_INCLUDE_VERSION_PROPERTIES); + List allActiveExtensionVersions = needsVersionList + ? repositories + .findActiveExtensionVersions(extensionsMap.keySet(), targetPlatform, maxPreReleaseVersions) + : Collections.emptyList(); List extensionVersions; if (test(flags, FLAG_INCLUDE_LATEST_VERSION_ONLY)) { @@ -262,11 +272,7 @@ public ExtensionQueryResult extensionQuery(ExtensionQueryParam param, int defaul fileResources = Collections.emptyMap(); } - var latestVersions = allActiveExtensionVersions.stream() - .collect(Collectors.groupingBy(ev -> ev.getExtension().getId())) - .values() - .stream() - .map(list -> versions.getLatest(list, false)) + var latestVersions = repositories.findLatestVersions(extensionsMap.keySet(), targetPlatform).stream() .collect(Collectors.toMap(ev -> ev.getExtension().getId(), ev -> ev)); var extensionQueryResults = new ArrayList(); diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java b/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java index a6a1adc75..33b4717d8 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java @@ -1014,7 +1014,11 @@ public Map findLatestIsPreview(Collection extensionIds) { } public List findLatest(Collection extensionIds) { - var latestQuery = findLatestQuery(null, false, true); + return findLatest(extensionIds, null); + } + + public List findLatest(Collection extensionIds, String targetPlatform) { + var latestQuery = findLatestQuery(targetPlatform, false, true); latestQuery.addSelect( EXTENSION_VERSION.ID, EXTENSION_VERSION.VERSION, diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java b/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java index 933d1a776..a0a109e02 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java @@ -970,6 +970,10 @@ public List findLatestVersions(Collection extensionIds) return extensionVersionJooqRepo.findLatest(extensionIds); } + public List findLatestVersions(Collection extensionIds, String targetPlatform) { + return extensionVersionJooqRepo.findLatest(extensionIds, targetPlatform); + } + public Map findLatestVersionsIsPreview(Collection extensionIds) { return extensionVersionJooqRepo.findLatestIsPreview(extensionIds); } diff --git a/server/src/test/java/org/eclipse/openvsx/adapter/LocalVSCodeServiceTest.java b/server/src/test/java/org/eclipse/openvsx/adapter/LocalVSCodeServiceTest.java index 8ffe03717..529819770 100644 --- a/server/src/test/java/org/eclipse/openvsx/adapter/LocalVSCodeServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/adapter/LocalVSCodeServiceTest.java @@ -74,6 +74,7 @@ void testDuplicateExtensionsInSearch() { .thenReturn(List.of(extension, extension)); Mockito.when(repositories.findActiveExtensionVersions(any(), any(), anyInt())) .thenReturn(List.of(extensionVersion)); + Mockito.when(repositories.findLatestVersions(any(), any())).thenReturn(List.of(extensionVersion)); Mockito.when(versions.getLatest(anyList(), anyBoolean())).thenReturn(extensionVersion); var result = vsCodeService.extensionQuery(param, 10); diff --git a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java index 448644a7a..0fa121926 100644 --- a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java @@ -1120,6 +1120,11 @@ private void mockExtensionVersions( anyInt())) .thenReturn(extVersions); + var latest = extVersions.stream().min(ExtensionVersion.SORT_COMPARATOR).orElse(null); + Mockito + .when(repositories.findLatestVersions(eq(Set.of(extension.getId())), eq(queryTargetPlatform))) + .thenReturn(latest != null ? List.of(latest) : Collections.emptyList()); + mockFileResources(extVersions); } diff --git a/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java b/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java index 3ac51042a..e9fadd89a 100644 --- a/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java +++ b/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java @@ -339,6 +339,7 @@ void testExecuteQueries() { () -> repositories.findActivePersonalAccessTokensAndType(userData, PersonalAccessTokenType.LLT), () -> repositories.findAllPersonalAccessTokensByVersion(0), () -> repositories.findLatestVersions(List.of(1L)), + () -> repositories.findLatestVersions(List.of(1L), "targetPlatform"), () -> repositories.hasSameVersion(extVersion), () -> repositories.hasActiveReview(extension, userData), () -> repositories.findLatestVersionsIsPreview(List.of(1L)),