schultek / jaspr

Modern web framework for building websites in Dart. Supports SPAs and SSR.
https://jasprpad.schultek.de
MIT License
996 stars 59 forks source link

Too many routes causes race condition during static generation #229

Closed nmfisher closed 1 month ago

nmfisher commented 2 months ago

Description

When you have too many routes, jaspr build will exit before building most of them.

I dug into the code briefly this afternoon and it looks like jaspr build sets up a proxy/listener, runs the underlying app, the app then (via RouteRegistry) will call requestRouteGeneration for each route, which dispatches a request back to the listener. The listener then fetches the route and writes out to a file. Since requestRouteGeneration is async but never awaited, it will spin up N concurrent requests (where N == the number of routes you have) but this will then delay the request processing on the proxy side long enough so that the route doesn't get added to the loop in time.

An ugly workaround is to add an arbitrary delay to the while (queuedRoutes.isNotEmpty) {...} loop in build_command.dart (e.g. await Future.delayed(Duration(milliseconds: 500)); was enough to do the trick on my machine.

A better fix would be to dispatch multiple routes back at once via requestRouteGeneration, e.g.

server_app.dart

static Future requestRouteGeneration(List<String> routes) async {
    if (kGenerateMode) {
      _sendDebugMessage({'routes': routes});
    }
  }

  static Future<void> _sendDebugMessage(Object message) async {
    await http.post(
      Uri.parse('http://localhost:$jasprProxyPort/\$jasprMessageHandler'),
      body: jsonEncode(message),
    );
  }

platform_server.dart

...
    for (var route in routes) {
      registerRoute(route, '');
    }

    await ServerApp.requestRouteGeneration(paths.toList());
...

Steps To Reproduce

git clone https://github.com/nmfisher/jaspr_route_race_condition_example
cd jaspr_route_race_condition_example
jaspr build

You will see that only build/jaspr/index.html is generated. The About page and 100 dummy pages should also have been generated.

(note it's possible this is dependent on the speed of the machine, if you can't reproduce, try increasing the number of dummy routes generated to 1000).

Doctor Output

[✓] Jaspr CLI (Version 0.11.1) • Dart Version 3.4.0-282.1.beta (beta) (Tue Apr 2 19:37:49 2024 +0000) on "macos_arm64" at /Users/nickfisher/Downloads/flutter/bin/cache/dart-sdk/bin/dart • Running on macos Version 13.5.1 (Build 22G90) - Locale en-SG • Analytics: Enabled

[✓] Current Project • Dependencies on core packages: • jaspr: ^0.12.0 • jaspr_builder: ^0.12.0 (dev) • jaspr_router: ^0.4.0 • Rendering mode: static • Uses jaspr compilers: false • Uses flutter embedding: false

Expected Behavior

All routes are successfully generated.

Additional Context

N/A

schultek commented 2 months ago

Thanks I will look into it. It should indeed wait for all requests.

schultek commented 1 month ago

Fixed in 0.13.0