[stable] [das] allow interleaved requests in the message scheduler
Issue description: disallowing interleaved message handling requests was causing major performance issues.
What is the fix: stop awaiting handlers and allow interleaving.
Why cherry-pick: this performance issue is heavily impacting a number of users.
Risk: Low, but not none. Some issues have been identified (in the linked issue) and are being worked on concurrently.
Bug: https://github.com/dart-lang/sdk/issues/60440
Change-Id: I176371f5609138eeb8a81a231f4a51a3a00dd37d
Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/419340
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421341
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Kevin Chisholm <kevinjchisholm@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3b9f8e..e417012 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 3.7.3
+
+This is a patch release that:
+
+- Fixes a performance regression in the analysis server (issue [#60335]).
+
+[#60335]: https://github.com/dart-lang/sdk/issues/60335
+
## 3.7.2
**Released on:** 2025-03-12
diff --git a/pkg/analysis_server/lib/src/server/message_scheduler.dart b/pkg/analysis_server/lib/src/server/message_scheduler.dart
index a15b33b..662b7d4 100644
--- a/pkg/analysis_server/lib/src/server/message_scheduler.dart
+++ b/pkg/analysis_server/lib/src/server/message_scheduler.dart
@@ -75,6 +75,9 @@
/// and the Diagnostic server. The [MessageScheduler] acts as a hub for all
/// incoming messages and forwards the messages to the appropriate handlers.
final class MessageScheduler {
+ /// A flag to allow disabling overlapping message handlers.
+ static bool allowOverlappingHandlers = true;
+
/// The [AnalysisServer] associated with the scheduler.
late final AnalysisServer server;
@@ -278,7 +281,20 @@
completer,
);
}
- await completer.future;
+
+ // Blocking here with an await on the future was intended to prevent unwanted
+ // interleaving but was found to cause a significant performance regression.
+ // For more context see: https://github.com/dart-lang/sdk/issues/60440.
+ // To re-disable interleaving, set [allowOverlappingHandlers] to `false`.
+ if (!allowOverlappingHandlers) {
+ await completer.future;
+ }
+
+ // NOTE that this message is not accurate if [allowOverlappingHandlers] is `true`
+ // as in that case we're not blocking anymore and the future may not be complete.
+ // TODO(pq): if not awaited, consider adding a `then` so we can track when the future completes
+ // but note that we may see some flakiness in tests as message handling gets
+ // non-deterministically interleaved.
testView?.messageLog.add(
' Complete ${message.runtimeType}: ${message.toString()}',
);
diff --git a/pkg/analysis_server/test/integration/server/message_scheduler_test.dart b/pkg/analysis_server/test/integration/server/message_scheduler_test.dart
index 2e50de8..6a9d7d7 100644
--- a/pkg/analysis_server/test/integration/server/message_scheduler_test.dart
+++ b/pkg/analysis_server/test/integration/server/message_scheduler_test.dart
@@ -67,6 +67,8 @@
}
Future<void> test_multipleRequests() async {
+ if (MessageScheduler.allowOverlappingHandlers) return;
+
var futures = <Future<void>>[];
futures.add(setRoots(included: [workspaceRootPath], excluded: []));
var request = ExecutionCreateContextParams(
@@ -103,6 +105,8 @@
}
Future<void> test_documentChange() async {
+ if (MessageScheduler.allowOverlappingHandlers) return;
+
const content = '''
void f() {
print('Test!');
@@ -176,6 +180,8 @@
}
Future<void> test_duplicateRequests() async {
+ if (MessageScheduler.allowOverlappingHandlers) return;
+
const content = '''
class B {
@^
@@ -252,6 +258,8 @@
}
Future<void> test_multipleRequests() async {
+ if (MessageScheduler.allowOverlappingHandlers) return;
+
const content = '''
void main() {
print('Hello world!!');
@@ -288,6 +296,8 @@
}
Future<void> test_response() async {
+ if (MessageScheduler.allowOverlappingHandlers) return;
+
const content = '''
void f() {
print('Test!');