Skip to content

Conversation

@nielsenko
Copy link
Collaborator

@nielsenko nielsenko commented Oct 31, 2025

Description

Exposes the noOfIsolates parameter on RelicApp.serve() to allow multi-isolate servers. Previously, noOfIsolates was only available when constructing a RelicServer directly, or via RelicApp.run, but not through the RelicApp.serve() extension method.

Changes:

  • Added noOfIsolates parameter to RelicAppIOServeEx.serve() extension method
  • Added v6Only parameter to RelicAppIOServeEx.serve()
  • Automatically enables shared when noOfIsolates > 1 (required for multi-isolate binding)
  • Pass noOfIsolates through to run() method
  • Added assertion in _MultiIsolateRelicServer constructor to ensure noOfIsolates > 1
  • Updated hot-reload test to use noOfIsolates: 2

Related Issues

  • Fixes: #?

Pre-Launch Checklist

  • This update focuses on a single feature or bug fix.
  • I have read and followed the Dart Style Guide and formatted the code using dart format.
  • I have referenced at least one issue this PR fixes or is related to.
  • I have updated/added relevant documentation (doc comments with ///), ensuring consistency with existing project documentation.
  • I have added new tests to verify the changes.
  • All existing and new tests pass successfully.
  • I have documented any breaking changes below.

Breaking Changes

  • Includes breaking changes.
  • No breaking changes.

Additional Notes

None.

Summary by CodeRabbit

  • New Features
    • Server configuration now supports IPv6-only binding mode
    • Server can operate in multi-isolate mode for concurrent workload processing
    • Enhanced server initialization to support new configuration parameters
    • Improved shared connection handling for multi-isolate deployments

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Added v6Only and noOfIsolates parameters to the serve method in the IO adapter extension. The v6Only parameter is passed through to IOAdapter.bind, while noOfIsolates is passed to the run() call. The shared parameter is computed as shared || (noOfIsolates > 1). An assertion was added to _MultiIsolateRelicServer to validate that noOfIsolates exceeds 1.

Changes

Cohort / File(s) Summary
IO serve extension
lib/src/adapter/io/io_serve.dart
Added v6Only and noOfIsolates parameters to serve method; v6Only passed to IOAdapter.bind; shared computed as shared || (noOfIsolates > 1); noOfIsolates passed to run() call
Multi-isolate server
lib/src/relic_server.dart
Added assertion in _MultiIsolateRelicServer constructor requiring noOfIsolates > 1; reordered initializer list to include assertion and _children initialization with List.generate
Test updates
test/router/relic_app_test.dart
Updated app.serve() invocation to pass noOfIsolates: 2

Sequence Diagram

sequenceDiagram
    participant Test as test/router
    participant Serve as RelicAppIOServeEx.serve()
    participant Run as run()
    participant IOAdapter as IOAdapter.bind()
    participant MultiIsolate as _MultiIsolateRelicServer

    Test->>Serve: serve(v6Only, noOfIsolates=2, shared=false)
    Serve->>Serve: shared = false || (2 > 1) = true
    Serve->>IOAdapter: bind(v6Only, shared=true)
    Serve->>Run: run(noOfIsolates=2)
    Run->>MultiIsolate: constructor(noOfIsolates=2)
    MultiIsolate->>MultiIsolate: assert(2 > 1) ✓
    MultiIsolate->>MultiIsolate: _children = List.generate(2, ...)
Loading

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Changes follow a consistent pattern of parameter threading through method calls
  • Assertion logic is straightforward validation
  • Test update is a minimal one-line change

Possibly Related PRs

  • feat!: Introduce RelicApp #212: Introduced the RelicAppIOServeEx extension and IOAdapter.bind flow that this PR extends with multi-isolate and IPv6-only support options.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "feat: Forgot to expose noOfIsolates on serve extension on RelicApp" accurately describes the main change in the changeset. The title clearly identifies that the primary objective is to expose the noOfIsolates parameter on the serve extension method, which aligns with the substantial modifications to RelicAppIOServeEx.serve() and the supporting changes in related files. While the phrasing "Forgot to expose" is somewhat informal, it remains specific and understandable to reviewers scanning the commit history.
Description Check ✅ Passed The pull request description is well-structured and covers the primary required sections of the template. The Description section provides comprehensive details about the changes, explaining why noOfIsolates is being exposed and documenting all modifications. The Pre-Launch Checklist is mostly complete with most items checked, and Breaking Changes are clearly addressed as "No breaking changes." However, the Related Issues section contains a placeholder "#?" instead of an actual issue reference, and the corresponding checklist item "I have referenced at least one issue" is left unchecked, indicating this requirement has not been fulfilled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Oct 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.50%. Comparing base (a7bba28) to head (d28de35).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #260      +/-   ##
==========================================
+ Coverage   91.48%   91.50%   +0.01%     
==========================================
  Files          89       89              
  Lines        3475     3483       +8     
  Branches     1769     1771       +2     
==========================================
+ Hits         3179     3187       +8     
  Misses        296      296              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nielsenko
Copy link
Collaborator Author

@CodeRabbit review

@nielsenko nielsenko self-assigned this Oct 31, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

2 similar comments
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
lib/src/adapter/io/io_serve.dart (1)

17-25: Document the new parameters.

The v6Only and noOfIsolates parameters are not documented in the method docstring. Consider adding documentation explaining:

  • v6Only: Controls whether an IPv6 socket should only accept IPv6 connections (when using IPv6 addresses)
  • noOfIsolates: Number of isolates to spawn for handling requests (defaults to 1 for single-isolate mode)

Apply this diff to add parameter documentation:

 extension RelicAppIOServeEx on RelicApp {
   /// Starts a [HttpServer] that listens on the specified [address] and
   /// [port] and sends requests to [handler].
   ///
   /// If [securityContext] is provided, a secure HTTPS server will be started
   /// using [HttpServer.bindSecure]. Otherwise, an HTTP server will be started
   /// using [HttpServer.bind].
   ///
   /// If not specified [address] will default to [InternetAddress.loopbackIPv4],
   /// and [port] to 8080.
+  ///
+  /// The [v6Only] parameter controls whether an IPv6 socket accepts only IPv6
+  /// connections (ignored for IPv4 addresses).
+  ///
+  /// The [noOfIsolates] parameter specifies the number of isolates to spawn
+  /// for handling requests. When greater than 1, [shared] is automatically
+  /// enabled to allow multiple isolates to bind to the same port.
   Future<RelicServer> serve({
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7bba28 and 97b1ee5.

📒 Files selected for processing (3)
  • lib/src/adapter/io/io_serve.dart (1 hunks)
  • lib/src/relic_server.dart (1 hunks)
  • test/router/relic_app_test.dart (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.dart

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.dart: All Dart code must pass static analysis using dart analyze --fatal-infos with no issues
All Dart files must be formatted with dart format (CI enforces dart format --set-exit-if-changed .)

Files:

  • test/router/relic_app_test.dart
  • lib/src/adapter/io/io_serve.dart
  • lib/src/relic_server.dart
test/**/*.dart

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

test/**/*.dart: Tests should follow the Given-When-Then pattern in descriptions (flexible structuring allowed)
Use Arrange-Act-Assert pattern within test bodies
Provide clear, descriptive test titles; prefer single responsibility per test unless related assertions improve clarity
Place tests in the test/ directory mirroring the lib/ structure

Files:

  • test/router/relic_app_test.dart
lib/**/*.dart

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

lib/**/*.dart: Use Uint8List for request/response bodies for performance; avoid List for body payloads
Use type-safe HTTP header parsing and validation when accessing headers
Use router with trie-based matching and symbol-based path parameters (e.g., #name, #age) for routing
Ensure WebSocket handling includes proper lifecycle management (e.g., ping/pong for connection health)

Files:

  • lib/src/adapter/io/io_serve.dart
  • lib/src/relic_server.dart
🧠 Learnings (6)
📓 Common learnings
Learnt from: nielsenko
Repo: serverpod/relic PR: 216
File: lib/src/router/relic_app.dart:47-49
Timestamp: 2025-10-22T11:25:39.264Z
Learning: In the serverpod/relic repository, validation of the `noOfIsolates` parameter should be handled in the `RelicServer` constructor (lib/src/relic_server.dart), not in `RelicApp.run` (lib/src/router/relic_app.dart).
Learnt from: nielsenko
Repo: serverpod/relic PR: 212
File: example/multi_isolate.dart:32-38
Timestamp: 2025-10-16T07:49:04.663Z
Learning: In the Relic framework, the default binding for RelicApp.serve() is intentionally set to InternetAddress.loopbackIPv4 (localhost only) rather than anyIPv4. This is a deliberate security-first design decision to require explicit consent (via the address parameter) before exposing endpoints outside the host.
Learnt from: nielsenko
Repo: serverpod/relic PR: 48
File: lib/src/handler/handler.dart:59-67
Timestamp: 2025-04-25T07:39:38.915Z
Learning: Nielsenko prefers using switch statements with pattern matching over if statements when working with sealed classes in Dart, as they provide exhaustiveness checking at compile time and can be more concise.
📚 Learning: 2025-10-22T11:25:39.264Z
Learnt from: nielsenko
Repo: serverpod/relic PR: 216
File: lib/src/router/relic_app.dart:47-49
Timestamp: 2025-10-22T11:25:39.264Z
Learning: In the serverpod/relic repository, validation of the `noOfIsolates` parameter should be handled in the `RelicServer` constructor (lib/src/relic_server.dart), not in `RelicApp.run` (lib/src/router/relic_app.dart).

Applied to files:

  • test/router/relic_app_test.dart
  • lib/src/adapter/io/io_serve.dart
  • lib/src/relic_server.dart
📚 Learning: 2025-10-09T16:21:09.310Z
Learnt from: CR
Repo: serverpod/relic PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-09T16:21:09.310Z
Learning: Applies to test/**/*.dart : Provide clear, descriptive test titles; prefer single responsibility per test unless related assertions improve clarity

Applied to files:

  • test/router/relic_app_test.dart
📚 Learning: 2025-10-20T14:05:34.503Z
Learnt from: nielsenko
Repo: serverpod/relic PR: 226
File: lib/src/router/relic_app.dart:147-156
Timestamp: 2025-10-20T14:05:34.503Z
Learning: In the relic codebase, hot-reload and other development-time features in lib/src/router/relic_app.dart should fail loudly (raise exceptions) rather than catch and suppress errors, as this helps developers diagnose issues during development.

Applied to files:

  • test/router/relic_app_test.dart
📚 Learning: 2025-04-24T14:06:32.810Z
Learnt from: nielsenko
Repo: serverpod/relic PR: 48
File: example/example.dart:31-36
Timestamp: 2025-04-24T14:06:32.810Z
Learning: In the example code, `sleep()` is intentionally used instead of `await Future.delayed()` to simulate CPU-bound work that benefits from multiple isolates/cores. Using a blocking call demonstrates why multiple isolates are necessary, while an async approach would allow a single isolate to handle multiple requests concurrently, defeating the purpose of the multi-isolate example.

Applied to files:

  • test/router/relic_app_test.dart
📚 Learning: 2025-10-16T07:49:04.663Z
Learnt from: nielsenko
Repo: serverpod/relic PR: 212
File: example/multi_isolate.dart:32-38
Timestamp: 2025-10-16T07:49:04.663Z
Learning: In the Relic framework, the default binding for RelicApp.serve() is intentionally set to InternetAddress.loopbackIPv4 (localhost only) rather than anyIPv4. This is a deliberate security-first design decision to require explicit consent (via the address parameter) before exposing endpoints outside the host.

Applied to files:

  • lib/src/adapter/io/io_serve.dart
🔇 Additional comments (3)
test/router/relic_app_test.dart (1)

95-95: LGTM! Multi-isolate hot-reload testing.

The addition of noOfIsolates: 2 appropriately exercises the newly exposed parameter and validates that hot-reload works correctly with multiple isolates.

lib/src/relic_server.dart (1)

233-234: LGTM! Defensive assertion for multi-isolate server.

The assertion ensures _MultiIsolateRelicServer is only instantiated when noOfIsolates > 1, catching any logic errors if this constructor were called directly instead of through the factory.

lib/src/adapter/io/io_serve.dart (1)

33-33: LGTM! Automatic shared mode for multi-isolate.

The logic shared || noOfIsolates > 1 correctly ensures that shared is enabled when using multiple isolates (required for port sharing). This overrides any explicit shared: false the user might pass when noOfIsolates > 1.

@nielsenko nielsenko marked this pull request as ready for review November 2, 2025 10:38
@nielsenko nielsenko requested a review from a team November 2, 2025 10:39
Copy link
Contributor

@SandPod SandPod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor comments, otherwise LGTM! 👍

@nielsenko nielsenko merged commit be3bf27 into serverpod:main Nov 2, 2025
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants