Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/dart-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
PUB_CACHE_PATH: ~/.pub-cache
LOWEST_DART_SDK: "3.5.0"
LOWEST_DART_SDK: "3.7.0"

jobs:
build:
Expand All @@ -23,14 +23,14 @@ jobs:
dart_sdk:
# ${{ env.LOWEST_DART_SDK }} won't work at job level as env context not available for strategy ¯\_(ツ)_/¯
# (see https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#context-availability)
- "3.5.0"
- "3.7.0"
- stable
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Setup Dart
uses: dart-lang/setup-dart@v1.3
uses: dart-lang/setup-dart@v1
with:
sdk: ${{ matrix.dart_sdk }}

Expand Down Expand Up @@ -82,12 +82,12 @@ jobs:
unit_tests:
name: Run Unit Tests
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.dart_sdk != '3.3.0' }} # env context not available for continue-on-error
continue-on-error: ${{ matrix.dart_sdk != '3.7.0' }} # env context not available for continue-on-error
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
dart_sdk: ["3.5.0", stable, beta] # env context not available for strategy
dart_sdk: ["3.7.0", stable, beta] # env context not available for strategy
deps: [downgrade, upgrade]
steps:
- name: Checkout Code
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Future<void> main() async {
final router = RelicRouter()
..get('/user/:name/age/:age', hello)
..use('/', logRequests())
..fallback = respondWith((final _) => Response.notFound(
..fallback = respondWith((_) => Response.notFound(
body: Body.fromString("Sorry, that doesn't compute")));

// RelicRouter can be used directly as a handler via the call() extension
Expand Down
167 changes: 93 additions & 74 deletions benchmark/benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ late final List<String> dynamicRoutesToLookup;
void setupBenchmarkData(final int routeCount) {
logger.info('Setting up benchmark data with $routeCount routes...');
indexes = List.generate(routeCount, (final i) => i);
final permutedIndexes = indexes.toList()
..shuffle(Random(123)); // Use fixed seed for reproducibility
final permutedIndexes =
indexes.toList()
..shuffle(Random(123)); // Use fixed seed for reproducibility

// Pre-generate lookup paths
staticRoutesToLookup = permutedIndexes.map((final i) => '/path$i').toList();
dynamicRoutesToLookup = permutedIndexes
.map(
(final i) =>
// Fixed seed for reproducibility
'/users/user_${Random(i).nextInt(1000)}'
'/items/item_${Random(i + 1).nextInt(5000)}'
'/profile$i',
)
.toList();
dynamicRoutesToLookup =
permutedIndexes
.map(
(final i) =>
// Fixed seed for reproducibility
'/users/user_${Random(i).nextInt(1000)}'
'/items/item_${Random(i + 1).nextInt(5000)}'
'/profile$i',
)
.toList();
logger.info('Setup complete.');
}

Expand Down Expand Up @@ -65,7 +67,7 @@ class Emitter extends ScoreEmitterV2 {

abstract class RouterBenchmark extends PerfBenchmarkBase {
RouterBenchmark(final Iterable<String> grouping, final Emitter emitter)
: super(grouping.join(';'), emitter: emitter);
: super(grouping.join(';'), emitter: emitter);

@override
void exercise() => run();
Expand All @@ -74,7 +76,7 @@ abstract class RouterBenchmark extends PerfBenchmarkBase {
// Benchmark for adding static routes
class StaticAddBenchmark extends RouterBenchmark {
StaticAddBenchmark(final Emitter emitter)
: super(['Add', 'Static', 'x$routeCount', 'Router'], emitter);
: super(['Add', 'Static', 'x$routeCount', 'Router'], emitter);

@override
void run() {
Expand All @@ -88,7 +90,7 @@ class StaticAddBenchmark extends RouterBenchmark {
// Benchmark for looking up static routes
class StaticLookupBenchmark extends RouterBenchmark {
StaticLookupBenchmark(final Emitter emitter)
: super(['Lookup', 'Static', 'x$routeCount', 'Router'], emitter);
: super(['Lookup', 'Static', 'x$routeCount', 'Router'], emitter);

late final Router<int> router;

Expand All @@ -112,7 +114,7 @@ class StaticLookupBenchmark extends RouterBenchmark {
// Benchmark for adding dynamic routes
class DynamicAddBenchmark extends RouterBenchmark {
DynamicAddBenchmark(final Emitter emitter)
: super(['Add', 'Dynamic', 'x$routeCount', 'Router'], emitter);
: super(['Add', 'Dynamic', 'x$routeCount', 'Router'], emitter);

@override
void run() {
Expand All @@ -126,7 +128,7 @@ class DynamicAddBenchmark extends RouterBenchmark {
// Benchmark for looking up dynamic routes
class DynamicLookupBenchmark extends RouterBenchmark {
DynamicLookupBenchmark(final Emitter emitter)
: super(['Lookup', 'Dynamic', 'x$routeCount', 'Router'], emitter);
: super(['Lookup', 'Dynamic', 'x$routeCount', 'Router'], emitter);

late final Router<int> router;

Expand All @@ -150,7 +152,7 @@ class DynamicLookupBenchmark extends RouterBenchmark {

class StaticAddRoutingkitBenchmark extends RouterBenchmark {
StaticAddRoutingkitBenchmark(final Emitter emitter)
: super(['Add', 'Static', 'x$routeCount', 'Routingkit'], emitter);
: super(['Add', 'Static', 'x$routeCount', 'Routingkit'], emitter);

@override
void run() {
Expand All @@ -163,7 +165,7 @@ class StaticAddRoutingkitBenchmark extends RouterBenchmark {

class StaticLookupRoutingkitBenchmark extends RouterBenchmark {
StaticLookupRoutingkitBenchmark(final Emitter emitter)
: super(['Lookup', 'Static', 'x$routeCount', 'Routingkit'], emitter);
: super(['Lookup', 'Static', 'x$routeCount', 'Routingkit'], emitter);

late final routingkit.Router<int> router;

Expand All @@ -186,7 +188,7 @@ class StaticLookupRoutingkitBenchmark extends RouterBenchmark {

class DynamicAddRoutingkitBenchmark extends RouterBenchmark {
DynamicAddRoutingkitBenchmark(final Emitter emitter)
: super(['Add', 'Dynamic', 'x$routeCount', 'Routingkit'], emitter);
: super(['Add', 'Dynamic', 'x$routeCount', 'Routingkit'], emitter);

@override
void run() {
Expand All @@ -199,7 +201,7 @@ class DynamicAddRoutingkitBenchmark extends RouterBenchmark {

class DynamicLookupRoutingkitBenchmark extends RouterBenchmark {
DynamicLookupRoutingkitBenchmark(final Emitter emitter)
: super(['Lookup', 'Dynamic', 'x$routeCount', 'Routingkit'], emitter);
: super(['Lookup', 'Dynamic', 'x$routeCount', 'Routingkit'], emitter);

late final routingkit.Router<int> router;

Expand All @@ -223,7 +225,7 @@ class DynamicLookupRoutingkitBenchmark extends RouterBenchmark {

class StaticAddSpannerBenchmark extends RouterBenchmark {
StaticAddSpannerBenchmark(final Emitter emitter)
: super(['Add', 'Static', 'x$routeCount', 'Spanner'], emitter);
: super(['Add', 'Static', 'x$routeCount', 'Spanner'], emitter);

@override
void run() {
Expand All @@ -236,7 +238,7 @@ class StaticAddSpannerBenchmark extends RouterBenchmark {

class StaticLookupSpannerBenchmark extends RouterBenchmark {
StaticLookupSpannerBenchmark(final Emitter emitter)
: super(['Lookup', 'Static', 'x$routeCount', 'Spanner'], emitter);
: super(['Lookup', 'Static', 'x$routeCount', 'Spanner'], emitter);

late final spanner.Spanner router;

Expand All @@ -259,21 +261,24 @@ class StaticLookupSpannerBenchmark extends RouterBenchmark {

class DynamicAddSpannerBenchmark extends RouterBenchmark {
DynamicAddSpannerBenchmark(final Emitter emitter)
: super(['Add', 'Dynamic', 'x$routeCount', 'Spanner'], emitter);
: super(['Add', 'Dynamic', 'x$routeCount', 'Spanner'], emitter);

@override
void run() {
final router = spanner.Spanner();
for (final i in indexes) {
router.addRoute(
spanner.HTTPMethod.GET, '/users/<id>/items/<itemId>/profile$i', i);
spanner.HTTPMethod.GET,
'/users/<id>/items/<itemId>/profile$i',
i,
);
}
}
}

class DynamicLookupSpannerBenchmark extends RouterBenchmark {
DynamicLookupSpannerBenchmark(final Emitter emitter)
: super(['Lookup', 'Dynamic', 'x$routeCount', 'Spanner'], emitter);
: super(['Lookup', 'Dynamic', 'x$routeCount', 'Spanner'], emitter);

late final spanner.Spanner router;

Expand All @@ -283,7 +288,10 @@ class DynamicLookupSpannerBenchmark extends RouterBenchmark {
router = spanner.Spanner();
for (final i in indexes) {
router.addRoute(
spanner.HTTPMethod.GET, '/users/<id>/items/<itemId>/profile$i', i);
spanner.HTTPMethod.GET,
'/users/<id>/items/<itemId>/profile$i',
i,
);
}
}

Expand All @@ -297,36 +305,44 @@ class DynamicLookupSpannerBenchmark extends RouterBenchmark {
}

enum RunOption<V> implements OptionDefinition<V> {
file(FileOption(
argName: 'output',
argAbbrev: 'o',
helpText: 'The file to write benchmark results to',
fromDefault: _defaultFile,
mode: PathExistMode.mustNotExist,
)),

iterations(IntOption(
argName: 'iterations',
argAbbrev: 'i',
helpText: 'Something to do with scale',
defaultsTo: 1000,
min: 1,
)),

storeInNotes(FlagOption(
argName: 'store-in-git-notes',
argAbbrev: 's',
helpText: 'Store benchmark result with git notes',
defaultsTo: false,
)),

pause(FlagOption(
argName: 'pause-on-startup',
argAbbrev: 'p',
helpText: 'Pause on startup to allow devtools to attach',
defaultsTo: false,
hideNegatedUsage: true,
));
file(
FileOption(
argName: 'output',
argAbbrev: 'o',
helpText: 'The file to write benchmark results to',
fromDefault: _defaultFile,
mode: PathExistMode.mustNotExist,
),
),

iterations(
IntOption(
argName: 'iterations',
argAbbrev: 'i',
helpText: 'Something to do with scale',
defaultsTo: 1000,
min: 1,
),
),

storeInNotes(
FlagOption(
argName: 'store-in-git-notes',
argAbbrev: 's',
helpText: 'Store benchmark result with git notes',
defaultsTo: false,
),
),

pause(
FlagOption(
argName: 'pause-on-startup',
argAbbrev: 'p',
helpText: 'Pause on startup to allow devtools to attach',
defaultsTo: false,
hideNegatedUsage: true,
),
);

const RunOption(this.option);

Expand Down Expand Up @@ -384,10 +400,14 @@ class RunCommand extends BetterCommand<RunOption<dynamic>, void> {
if (storeInNotes) {
final head = await git.commitFromRevision('HEAD');
logger.info('Appending benchmark results to: ${head.treeSha} (tree)');
await git.runCommand(
['notes', '--ref=benchmarks', 'append', '-F', file.path, head.treeSha],
echoOutput: logger.shouldLog(LogLevel.debug),
);
await git.runCommand([
'notes',
'--ref=benchmarks',
'append',
'-F',
file.path,
head.treeSha,
], echoOutput: logger.shouldLog(LogLevel.debug));
}
}
}
Expand Down Expand Up @@ -419,9 +439,11 @@ class ExtractCommand extends BetterCommand<ExtractOption<dynamic>, void> {
final to = commandConfig.value(ExtractOption.to);

final git = await GitDir.fromExisting(p.current, allowSubdirectory: true);
final result = await git.runCommand(
['log', '--format=%aI %H %T', '$from..$to'],
);
final result = await git.runCommand([
'log',
'--format=%aI %H %T',
'$from..$to',
]);

final sb = StringBuffer();
for (final line in (result.stdout as String).split('\n')) {
Expand All @@ -433,10 +455,12 @@ class ExtractCommand extends BetterCommand<ExtractOption<dynamic>, void> {
final treeSha = hashes[2];
logger.debug('$commitSha $treeSha $authorTime');

final result = await git.runCommand(
['notes', '--ref=benchmarks', 'show', treeSha],
throwOnError: false,
);
final result = await git.runCommand([
'notes',
'--ref=benchmarks',
'show',
treeSha,
], throwOnError: false);
if (result.exitCode == 0) sb.writeln(result.stdout);
}
logger.info(sb.toString());
Expand All @@ -463,13 +487,8 @@ Future<int> main(final List<String> args) async {
'Relic Benchmark Tool',
setLogLevel: setLogLevel,
enableCompletionCommand: true,
embeddedCompletions: [
completionScriptCarapace,
],
)..addCommands([
RunCommand(),
ExtractCommand(),
]);
embeddedCompletions: [completionScriptCarapace],
)..addCommands([RunCommand(), ExtractCommand()]);
try {
await runner.run(args);
} on UsageException catch (ex) {
Expand Down
14 changes: 9 additions & 5 deletions example/advanced/multi_isolate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ void main() async {

// Wait for all the isolates to spawn
log('Starting $isolateCount isolates');
final isolates = await Future.wait(List.generate(
final isolates = await Future.wait(
List.generate(
isolateCount,
(final index) =>
Isolate.spawn((final _) => _serve(), null, debugName: '$index')));
Isolate.spawn((final _) => _serve(), null, debugName: '$index'),
),
);

// Wait for Ctrl-C before proceeding
await ProcessSignal.sigint.watch().first;
Expand All @@ -29,9 +32,10 @@ void main() async {
/// [_serve] is called in each spawned isolate.
Future<void> _serve() async {
// A router with no routes but a fallback
final app = RelicApp()
..use('/', logRequests())
..put('/echo', respondWith(_echoRequest));
final app =
RelicApp()
..use('/', logRequests())
..put('/echo', respondWith(_echoRequest));

// start the server
await app.serve(shared: true);
Expand Down
Loading
Loading