Skip to content

Commit 140cc4b

Browse files
authored
New isolate for the SDK index in the search instance. (#8795)
1 parent e276217 commit 140cc4b

File tree

10 files changed

+86
-36
lines changed

10 files changed

+86
-36
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Important changes to data models, configuration, and migrations between each
22
AppEngine version, listed here to ease deployment and troubleshooting.
33

44
## Next Release (replace with git tag when deployed)
5+
* Note: search instance uses separate isolate for the SDK index.
56

67
## `20250522t102600-all`
78
* Bump runtimeVersion to `2025.05.21`.

app/lib/fake/server/fake_search_service.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class FakeSearchService {
3939
storage: _storage,
4040
cloudCompute: _cloudCompute,
4141
fn: () async {
42-
registerSdkMemIndex(await createSdkMemIndex());
42+
registerSdkIndex(await createSdkMemIndex());
4343
final handler = wrapHandler(_logger, searchServiceHandler);
4444
final server = await IOServer.bind('localhost', port);
4545
serveRequests(server.server, (request) async {

app/lib/search/backend.dart

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -619,18 +619,34 @@ class _CombinedSearchIndex implements SearchIndex {
619619
IndexInfo indexInfo() => _packageIndexHolder._index.indexInfo();
620620

621621
@override
622-
PackageSearchResult search(ServiceSearchQuery query) {
622+
Future<PackageSearchResult> search(ServiceSearchQuery query) async {
623623
final combiner = SearchResultCombiner(
624-
primaryIndex: _packageIndexHolder._index,
625-
sdkMemIndex: sdkMemIndex,
624+
primaryIndex: _packageIndexHolder,
625+
sdkIndex: sdkIndex,
626626
);
627-
return combiner.search(query);
627+
return await combiner.search(query);
628628
}
629629
}
630630

631631
/// Holds an immutable [InMemoryPackageIndex] that is the actual active search index.
632-
class PackageIndexHolder {
632+
class PackageIndexHolder implements SearchIndex {
633633
var _index = InMemoryPackageIndex(documents: const []);
634+
635+
@override
636+
bool isReady() => indexInfo().isReady;
637+
638+
@override
639+
IndexInfo indexInfo() => _index.indexInfo();
640+
641+
@override
642+
PackageSearchResult search(ServiceSearchQuery query) {
643+
return _index.search(query);
644+
}
645+
646+
/// Updates the active package index with [newIndex].
647+
void updatePackageIndex(InMemoryPackageIndex newIndex) {
648+
_index = newIndex;
649+
}
634650
}
635651

636652
/// Updates the active package index with [newIndex].

app/lib/search/result_combiner.dart

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,51 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async';
6+
57
import 'package:_pub_shared/search/tags.dart';
68
import 'package:collection/collection.dart';
79

8-
import 'mem_index.dart';
910
import 'sdk_mem_index.dart';
1011
import 'search_service.dart';
1112

1213
/// Combines the results from the primary package index and the optional Dart
1314
/// SDK index.
14-
class SearchResultCombiner {
15-
final InMemoryPackageIndex primaryIndex;
16-
final SdkMemIndex? sdkMemIndex;
15+
class SearchResultCombiner implements SearchIndex {
16+
final SearchIndex primaryIndex;
17+
final SdkIndex? sdkIndex;
1718

1819
SearchResultCombiner({
1920
required this.primaryIndex,
20-
required this.sdkMemIndex,
21+
required this.sdkIndex,
2122
});
2223

23-
PackageSearchResult search(ServiceSearchQuery query) {
24-
final primaryResult = primaryIndex.search(query);
25-
if (!query.includeSdkResults) {
26-
return primaryResult;
24+
@override
25+
FutureOr<IndexInfo> indexInfo() async {
26+
return await primaryIndex.indexInfo();
27+
}
28+
29+
@override
30+
FutureOr<bool> isReady() async {
31+
return await primaryIndex.isReady();
32+
}
33+
34+
@override
35+
Future<PackageSearchResult> search(ServiceSearchQuery query) async {
36+
if (sdkIndex == null || !query.includeSdkResults) {
37+
return await primaryIndex.search(query);
2738
}
2839

2940
final queryFlutterSdk = query.tagsPredicate.hasNoTagPrefix('sdk:') ||
3041
query.tagsPredicate.hasTag(SdkTag.sdkFlutter);
31-
final sdkLibraryHits = sdkMemIndex
32-
?.search(query.query!, limit: 2, skipFlutter: !queryFlutterSdk)
33-
.toList() ??
34-
<SdkLibraryHit>[];
42+
final results = await Future.wait([
43+
Future(() => primaryIndex.search(query)),
44+
Future(() => sdkIndex!
45+
.search(query.query!, limit: 2, skipFlutter: !queryFlutterSdk)),
46+
]);
47+
final primaryResult = results[0] as PackageSearchResult;
48+
final sdkLibraryHits = results[1] as List<SdkLibraryHit>;
49+
3550
if (sdkLibraryHits.isNotEmpty) {
3651
// Do not display low SDK scores if the package hits are more relevant on the page.
3752
//

app/lib/search/sdk_mem_index.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ import 'token_index.dart';
1717

1818
export 'package:pana/src/dartdoc/dartdoc_index.dart';
1919

20-
/// Sets the SDK in-memory index.
21-
void registerSdkMemIndex(SdkMemIndex? index) {
20+
/// Sets the SDK index.
21+
void registerSdkIndex(SdkIndex? index) {
2222
if (index != null) {
23-
ss.register(#_sdkMemIndex, index);
23+
ss.register(#_sdkIndex, index);
2424
}
2525
}
2626

2727
/// The active SDK in-memory index.
28-
SdkMemIndex? get sdkMemIndex => ss.lookup(#_sdkMemIndex) as SdkMemIndex?;
28+
SdkIndex? get sdkIndex => ss.lookup(#_sdkIndex) as SdkIndex?;
2929

3030
/// Results from these libraries are ranked with lower score and
3131
/// will be displayed only if the query has the library name, or

app/lib/service/entrypoint/_isolate.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,11 @@ Future<IsolateRunner> startWorkerIsolate({
176176
Future<IsolateRunner> startQueryIsolate({
177177
required Logger logger,
178178
required Uri spawnUri,
179+
required String kind,
179180
}) async {
180181
final worker = IsolateRunner.uri(
181182
logger: logger,
182-
kind: 'query',
183+
kind: kind,
183184
spawnUri: spawnUri,
184185
);
185186
await worker.start(1);

app/lib/service/entrypoint/search.dart

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'dart:math';
88
import 'package:args/command_runner.dart';
99
import 'package:gcloud/service_scope.dart';
1010
import 'package:logging/logging.dart';
11+
import 'package:pub_dev/search/result_combiner.dart';
12+
import 'package:pub_dev/service/entrypoint/sdk_isolate_index.dart';
1113
import 'package:pub_dev/service/entrypoint/search_index.dart';
1214

1315
import '../../search/backend.dart';
@@ -36,14 +38,29 @@ class SearchCommand extends Command {
3638

3739
envConfig.checkServiceEnvironment(name);
3840
await withServices(() async {
39-
final index = await startQueryIsolate(
41+
final packageIsolate = await startQueryIsolate(
4042
logger: _logger,
43+
kind: 'package',
4144
spawnUri:
4245
Uri.parse('package:pub_dev/service/entrypoint/search_index.dart'),
4346
);
44-
registerScopeExitCallback(index.close);
47+
registerScopeExitCallback(packageIsolate.close);
4548

46-
registerSearchIndex(LatencyAwareSearchIndex(IsolateSearchIndex(index)));
49+
final sdkIsolate = await startQueryIsolate(
50+
logger: _logger,
51+
kind: 'sdk',
52+
spawnUri: Uri.parse(
53+
'package:pub_dev/service/entrypoint/sdk_isolate_index.dart'),
54+
);
55+
registerScopeExitCallback(sdkIsolate.close);
56+
57+
registerSearchIndex(
58+
SearchResultCombiner(
59+
primaryIndex:
60+
LatencyAwareSearchIndex(IsolateSearchIndex(packageIsolate)),
61+
sdkIndex: SdkIsolateIndex(sdkIsolate),
62+
),
63+
);
4764

4865
void scheduleRenew() {
4966
scheduleMicrotask(() async {
@@ -53,7 +70,7 @@ class SearchCommand extends Command {
5370
await Future.delayed(delay);
5471

5572
// create a new index and handover with a 2-minute maximum wait
56-
await index.renew(count: 1, wait: Duration(minutes: 2));
73+
await packageIsolate.renew(count: 1, wait: Duration(minutes: 2));
5774

5875
// schedule the renewal again
5976
scheduleRenew();

app/lib/service/entrypoint/search_index.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'dart:convert';
88
import 'package:gcloud/service_scope.dart';
99
import 'package:logging/logging.dart';
1010
import 'package:pub_dev/search/backend.dart';
11-
import 'package:pub_dev/search/sdk_mem_index.dart';
1211
import 'package:pub_dev/search/search_service.dart';
1312
import 'package:pub_dev/search/updater.dart';
1413
import 'package:pub_dev/service/entrypoint/_isolate.dart';
@@ -35,7 +34,6 @@ Future<void> main(List<String> args, var message) async {
3534
}
3635
await fork(() async {
3736
await servicesWrapperFn(() async {
38-
registerSdkMemIndex(await createSdkMemIndex());
3937
await indexUpdater.init();
4038

4139
await runIsolateFunctions(

app/test/search/result_combiner_test.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:convert';
66

77
import 'package:_pub_shared/search/search_form.dart';
8+
import 'package:pub_dev/search/backend.dart';
89
import 'package:pub_dev/search/mem_index.dart';
910
import 'package:pub_dev/search/result_combiner.dart';
1011
import 'package:pub_dev/search/sdk_mem_index.dart';
@@ -13,7 +14,7 @@ import 'package:test/test.dart';
1314

1415
void main() {
1516
group('ResultCombiner', () {
16-
final primaryIndex = InMemoryPackageIndex(
17+
final packageIndex = InMemoryPackageIndex(
1718
documents: [
1819
PackageDocument(
1920
package: 'stringutils',
@@ -27,9 +28,10 @@ void main() {
2728
),
2829
],
2930
);
31+
final primaryIndex = PackageIndexHolder()..updatePackageIndex(packageIndex);
3032
final combiner = SearchResultCombiner(
3133
primaryIndex: primaryIndex,
32-
sdkMemIndex: SdkMemIndex(
34+
sdkIndex: SdkMemIndex(
3335
dartIndex: DartdocIndex.fromJsonList([
3436
{
3537
'name': 'dart:core',
@@ -73,7 +75,7 @@ void main() {
7375
);
7476

7577
test('non-text ranking', () async {
76-
final results = combiner
78+
final results = await combiner
7779
.search(ServiceSearchQuery.parse(order: SearchOrder.downloads));
7880
expect(json.decode(json.encode(results.toJson())), {
7981
'timestamp': isNotNull,
@@ -87,7 +89,7 @@ void main() {
8789

8890
test('no actual text query', () async {
8991
final results =
90-
combiner.search(ServiceSearchQuery.parse(query: 'package:s'));
92+
await combiner.search(ServiceSearchQuery.parse(query: 'package:s'));
9193
expect(json.decode(json.encode(results.toJson())), {
9294
'timestamp': isNotNull,
9395
'totalCount': 1,
@@ -100,7 +102,7 @@ void main() {
100102

101103
test('search: substring', () async {
102104
final results =
103-
combiner.search(ServiceSearchQuery.parse(query: 'substring'));
105+
await combiner.search(ServiceSearchQuery.parse(query: 'substring'));
104106
expect(json.decode(json.encode(results.toJson())), {
105107
'timestamp': isNotNull,
106108
'totalCount': 1,

app/test/service/entrypoint/search_index_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void main() {
3434
final rs =
3535
await searchIndex.search(ServiceSearchQuery.parse(query: 'json'));
3636
expect(rs.errorMessage, isNull);
37-
expect(rs.sdkLibraryHits, isNotEmpty);
37+
expect(rs.sdkLibraryHits, isEmpty);
3838
expect(rs.packageHits, isEmpty);
3939
}, timeout: Timeout(Duration(minutes: 5)));
4040
});

0 commit comments

Comments
 (0)