[current_results] Implement data store for current results in memory

Change-Id: I4d49c36ccadf76cccc756aad30bda453aac4f6b2
Reviewed-on: https://dart-review.googlesource.com/c/dart_ci/+/150483
Commit-Queue: William Hesse <whesse@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/current_results/bin/server.dart b/current_results/bin/server.dart
index 85f58cd..15afb63 100644
--- a/current_results/bin/server.dart
+++ b/current_results/bin/server.dart
@@ -5,28 +5,23 @@
 import 'dart:convert';
 import 'dart:io';
 
-import 'package:shelf/shelf.dart' as shelf;
-import 'package:shelf/shelf_io.dart' as io;
 import 'package:gcloud/storage.dart';
 import 'package:googleapis_auth/auth_io.dart';
 import 'package:http/http.dart';
+import 'package:pool/pool.dart';
 
-import '../gen/result.pbserver.dart';
+import 'package:current_results/src/slice.dart';
 
 // For Google Cloud Run, set _hostname to '0.0.0.0'.
 var _hostname = '0.0.0.0';
 
 Future initialized;
 
+final current = Slice();
+
 void main(List<String> args) async {
   initialized = loadData();
-  var port = int.tryParse(Platform.environment['PORT'] ?? '8080');
-  var handler = const shelf.Pipeline()
-      .addMiddleware(shelf.logRequests())
-      .addHandler(_echoRequest);
-
-  var server = await io.serve(handler, _hostname, port);
-  print('Serving at http://${server.address.host}:${server.port}');
+  var port = int.tryParse(Platform.environment['PORT'] ?? '8081');
 }
 
 Future<void> loadData() async {
@@ -35,78 +30,33 @@
   final storage = Storage(client, 'dart-ci-staging');
   final bucket = storage.bucket('dart-test-results');
 
-  await for (final configuration
-      in bucket.list(prefix: 'configuration/master/')) {
-    if (configuration.isDirectory) {
-      try {
-        final revision = await bucket
-            .read('${configuration.name}latest')
-            .transform(ascii.decoder)
-            .transform(LineSplitter())
-            .single;
-        final lines = await bucket
-            .read('${configuration.name}$revision/results.json')
-            .transform(utf8.decoder)
-            .transform(LineSplitter());
-        await for (final line in lines) {
-          final result = Result()
-            ..mergeFromProto3Json(json.decode(line),
-                supportNamesWithUnderscores: true);
-          data.add(CurrentResult.fromResult(result));
-        }
-      } catch (e) {
-        print('Error reading results from ${configuration.name}:');
-        print(e);
-      }
+  final configurationDirectories = await bucket
+      .list(prefix: 'configuration/master/')
+      .where((entry) => entry.isDirectory)
+      .toList();
+  await Pool(10).forEach(configurationDirectories, (configuration) async {
+    print(configuration.name);
+    try {
+      final revision = await bucket
+          .read('${configuration.name}latest')
+          .transform(ascii.decoder)
+          .transform(LineSplitter())
+          .single;
+      final results = await bucket
+          .read('${configuration.name}$revision/results.json')
+          .transform(utf8.decoder)
+          .transform(LineSplitter())
+          .toList();
+      current.add(results);
+    } catch (e) {
+      print('Error reading results from ${configuration.name}:');
+      print(e);
     }
-  }
-  print("Records ingested: ${data.length}");
-}
-
-class CurrentResult {
-  final String name;
-  final String configuration;
-  final String commitHash;
-  final String result;
-  final bool flaky;
-  final String expected;
-  final Duration time;
-
-  CurrentResult(this.name, this.configuration, this.commitHash, this.result,
-      this.flaky, this.expected, this.time);
-
-  CurrentResult.fromResult(Result other)
-      : this(
-            unique(other.name),
-            unique(other.configuration),
-            unique(other.commitHash),
-            unique(other.result),
-            other.flaky,
-            unique(other.expected),
-            Duration(milliseconds: other.timeMs));
-}
-
-final set = Set<String>();
-final data = <CurrentResult>[];
-
-String unique(String string) =>
-    set.lookup(string) ?? (set.add(string) ? string : string);
-
-Map<String, dynamic> canonicalize(Map<String, dynamic> map) => {
-      for (final entry in map.entries)
-        unique(entry.key):
-            (entry.value is String) ? unique(entry.value) : entry.value
-    };
-
-Future<shelf.Response> _echoRequest(shelf.Request request) async {
-  print("Request ${request.url} received");
-  await initialized;
-  return shelf.Response.ok('Number of results: ${data.length}\n'
-      'Number of unique strings ${set.length}');
+  }).drain();
+  print("Records ingested: ${current.size}");
 }
 
 Future<AuthClient> getAuthenticatedClient() async {
-  AuthClient result;
   final localCredentials =
       Platform.environment['GOOGLE_APPLICATION_CREDENTIALS'];
   if (localCredentials == null) {
diff --git a/current_results/gen/result.pb.dart b/current_results/gen/result.pb.dart
deleted file mode 100644
index 09efbf4..0000000
--- a/current_results/gen/result.pb.dart
+++ /dev/null
@@ -1,150 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: result.proto
-///
-// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name
-
-import 'dart:core' as $core show bool, Deprecated, double, int, List, Map, override, pragma, String;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class Result extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo('Result', package: const $pb.PackageName('dart_ci'))
-    ..aOS(1, 'name')
-    ..aOS(2, 'configuration')
-    ..a<$core.int>(3, 'timeMs', $pb.PbFieldType.O3)
-    ..aOS(4, 'result')
-    ..aOS(5, 'expected')
-    ..aOB(6, 'matches')
-    ..aOS(7, 'botName')
-    ..aOS(8, 'commitHash')
-    ..aOS(9, 'buildNumber')
-    ..aOS(10, 'builderName')
-    ..aOB(11, 'flaky')
-    ..aOB(12, 'previousFlaky')
-    ..aOS(13, 'previousCommitHash')
-    ..a<$core.int>(14, 'previousCommitTime', $pb.PbFieldType.O3)
-    ..aOS(15, 'previousBuildNumber')
-    ..aOS(16, 'previousResult')
-    ..aOB(17, 'changed')
-    ..aOS(100, 'suite')
-    ..aOS(101, 'testName')
-    ..a<$core.int>(102, 'commitTime', $pb.PbFieldType.O3)
-    ..hasRequiredFields = false
-  ;
-
-  Result._() : super();
-  factory Result() => create();
-  factory Result.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory Result.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  Result clone() => Result()..mergeFromMessage(this);
-  Result copyWith(void Function(Result) updates) => super.copyWith((message) => updates(message as Result));
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static Result create() => Result._();
-  Result createEmptyInstance() => create();
-  static $pb.PbList<Result> createRepeated() => $pb.PbList<Result>();
-  static Result getDefault() => _defaultInstance ??= create()..freeze();
-  static Result _defaultInstance;
-
-  $core.String get name => $_getS(0, '');
-  set name($core.String v) { $_setString(0, v); }
-  $core.bool hasName() => $_has(0);
-  void clearName() => clearField(1);
-
-  $core.String get configuration => $_getS(1, '');
-  set configuration($core.String v) { $_setString(1, v); }
-  $core.bool hasConfiguration() => $_has(1);
-  void clearConfiguration() => clearField(2);
-
-  $core.int get timeMs => $_get(2, 0);
-  set timeMs($core.int v) { $_setSignedInt32(2, v); }
-  $core.bool hasTimeMs() => $_has(2);
-  void clearTimeMs() => clearField(3);
-
-  $core.String get result => $_getS(3, '');
-  set result($core.String v) { $_setString(3, v); }
-  $core.bool hasResult() => $_has(3);
-  void clearResult() => clearField(4);
-
-  $core.String get expected => $_getS(4, '');
-  set expected($core.String v) { $_setString(4, v); }
-  $core.bool hasExpected() => $_has(4);
-  void clearExpected() => clearField(5);
-
-  $core.bool get matches => $_get(5, false);
-  set matches($core.bool v) { $_setBool(5, v); }
-  $core.bool hasMatches() => $_has(5);
-  void clearMatches() => clearField(6);
-
-  $core.String get botName => $_getS(6, '');
-  set botName($core.String v) { $_setString(6, v); }
-  $core.bool hasBotName() => $_has(6);
-  void clearBotName() => clearField(7);
-
-  $core.String get commitHash => $_getS(7, '');
-  set commitHash($core.String v) { $_setString(7, v); }
-  $core.bool hasCommitHash() => $_has(7);
-  void clearCommitHash() => clearField(8);
-
-  $core.String get buildNumber => $_getS(8, '');
-  set buildNumber($core.String v) { $_setString(8, v); }
-  $core.bool hasBuildNumber() => $_has(8);
-  void clearBuildNumber() => clearField(9);
-
-  $core.String get builderName => $_getS(9, '');
-  set builderName($core.String v) { $_setString(9, v); }
-  $core.bool hasBuilderName() => $_has(9);
-  void clearBuilderName() => clearField(10);
-
-  $core.bool get flaky => $_get(10, false);
-  set flaky($core.bool v) { $_setBool(10, v); }
-  $core.bool hasFlaky() => $_has(10);
-  void clearFlaky() => clearField(11);
-
-  $core.bool get previousFlaky => $_get(11, false);
-  set previousFlaky($core.bool v) { $_setBool(11, v); }
-  $core.bool hasPreviousFlaky() => $_has(11);
-  void clearPreviousFlaky() => clearField(12);
-
-  $core.String get previousCommitHash => $_getS(12, '');
-  set previousCommitHash($core.String v) { $_setString(12, v); }
-  $core.bool hasPreviousCommitHash() => $_has(12);
-  void clearPreviousCommitHash() => clearField(13);
-
-  $core.int get previousCommitTime => $_get(13, 0);
-  set previousCommitTime($core.int v) { $_setSignedInt32(13, v); }
-  $core.bool hasPreviousCommitTime() => $_has(13);
-  void clearPreviousCommitTime() => clearField(14);
-
-  $core.String get previousBuildNumber => $_getS(14, '');
-  set previousBuildNumber($core.String v) { $_setString(14, v); }
-  $core.bool hasPreviousBuildNumber() => $_has(14);
-  void clearPreviousBuildNumber() => clearField(15);
-
-  $core.String get previousResult => $_getS(15, '');
-  set previousResult($core.String v) { $_setString(15, v); }
-  $core.bool hasPreviousResult() => $_has(15);
-  void clearPreviousResult() => clearField(16);
-
-  $core.bool get changed => $_get(16, false);
-  set changed($core.bool v) { $_setBool(16, v); }
-  $core.bool hasChanged() => $_has(16);
-  void clearChanged() => clearField(17);
-
-  $core.String get suite => $_getS(17, '');
-  set suite($core.String v) { $_setString(17, v); }
-  $core.bool hasSuite() => $_has(17);
-  void clearSuite() => clearField(100);
-
-  $core.String get testName => $_getS(18, '');
-  set testName($core.String v) { $_setString(18, v); }
-  $core.bool hasTestName() => $_has(18);
-  void clearTestName() => clearField(101);
-
-  $core.int get commitTime => $_get(19, 0);
-  set commitTime($core.int v) { $_setSignedInt32(19, v); }
-  $core.bool hasCommitTime() => $_has(19);
-  void clearCommitTime() => clearField(102);
-}
-
diff --git a/current_results/lib/src/generated/result.pb.dart b/current_results/lib/src/generated/result.pb.dart
new file mode 100644
index 0000000..7e3ec61
--- /dev/null
+++ b/current_results/lib/src/generated/result.pb.dart
@@ -0,0 +1,215 @@
+///
+//  Generated code. Do not modify.
+//  source: result.proto
+///
+// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name
+
+import 'dart:core' as $core
+    show bool, Deprecated, double, int, List, Map, override, pragma, String;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class Result extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i =
+      $pb.BuilderInfo('Result', package: const $pb.PackageName('dart_ci'))
+        ..aOS(1, 'name')
+        ..aOS(2, 'configuration')
+        ..a<$core.int>(3, 'timeMs', $pb.PbFieldType.O3)
+        ..aOS(4, 'result')
+        ..aOS(5, 'expected')
+        ..aOB(6, 'matches')
+        ..aOS(7, 'botName')
+        ..aOS(8, 'commitHash')
+        ..aOS(9, 'buildNumber')
+        ..aOS(10, 'builderName')
+        ..aOB(11, 'flaky')
+        ..aOB(12, 'previousFlaky')
+        ..aOS(13, 'previousCommitHash')
+        ..a<$core.int>(14, 'previousCommitTime', $pb.PbFieldType.O3)
+        ..aOS(15, 'previousBuildNumber')
+        ..aOS(16, 'previousResult')
+        ..aOB(17, 'changed')
+        ..aOS(100, 'suite')
+        ..aOS(101, 'testName')
+        ..a<$core.int>(102, 'commitTime', $pb.PbFieldType.O3)
+        ..hasRequiredFields = false;
+
+  Result._() : super();
+  factory Result() => create();
+  factory Result.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Result.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+  Result clone() => Result()..mergeFromMessage(this);
+  Result copyWith(void Function(Result) updates) =>
+      super.copyWith((message) => updates(message as Result));
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static Result create() => Result._();
+  Result createEmptyInstance() => create();
+  static $pb.PbList<Result> createRepeated() => $pb.PbList<Result>();
+  static Result getDefault() => _defaultInstance ??= create()..freeze();
+  static Result _defaultInstance;
+
+  $core.String get name => $_getS(0, '');
+  set name($core.String v) {
+    $_setString(0, v);
+  }
+
+  $core.bool hasName() => $_has(0);
+  void clearName() => clearField(1);
+
+  $core.String get configuration => $_getS(1, '');
+  set configuration($core.String v) {
+    $_setString(1, v);
+  }
+
+  $core.bool hasConfiguration() => $_has(1);
+  void clearConfiguration() => clearField(2);
+
+  $core.int get timeMs => $_get(2, 0);
+  set timeMs($core.int v) {
+    $_setSignedInt32(2, v);
+  }
+
+  $core.bool hasTimeMs() => $_has(2);
+  void clearTimeMs() => clearField(3);
+
+  $core.String get result => $_getS(3, '');
+  set result($core.String v) {
+    $_setString(3, v);
+  }
+
+  $core.bool hasResult() => $_has(3);
+  void clearResult() => clearField(4);
+
+  $core.String get expected => $_getS(4, '');
+  set expected($core.String v) {
+    $_setString(4, v);
+  }
+
+  $core.bool hasExpected() => $_has(4);
+  void clearExpected() => clearField(5);
+
+  $core.bool get matches => $_get(5, false);
+  set matches($core.bool v) {
+    $_setBool(5, v);
+  }
+
+  $core.bool hasMatches() => $_has(5);
+  void clearMatches() => clearField(6);
+
+  $core.String get botName => $_getS(6, '');
+  set botName($core.String v) {
+    $_setString(6, v);
+  }
+
+  $core.bool hasBotName() => $_has(6);
+  void clearBotName() => clearField(7);
+
+  $core.String get commitHash => $_getS(7, '');
+  set commitHash($core.String v) {
+    $_setString(7, v);
+  }
+
+  $core.bool hasCommitHash() => $_has(7);
+  void clearCommitHash() => clearField(8);
+
+  $core.String get buildNumber => $_getS(8, '');
+  set buildNumber($core.String v) {
+    $_setString(8, v);
+  }
+
+  $core.bool hasBuildNumber() => $_has(8);
+  void clearBuildNumber() => clearField(9);
+
+  $core.String get builderName => $_getS(9, '');
+  set builderName($core.String v) {
+    $_setString(9, v);
+  }
+
+  $core.bool hasBuilderName() => $_has(9);
+  void clearBuilderName() => clearField(10);
+
+  $core.bool get flaky => $_get(10, false);
+  set flaky($core.bool v) {
+    $_setBool(10, v);
+  }
+
+  $core.bool hasFlaky() => $_has(10);
+  void clearFlaky() => clearField(11);
+
+  $core.bool get previousFlaky => $_get(11, false);
+  set previousFlaky($core.bool v) {
+    $_setBool(11, v);
+  }
+
+  $core.bool hasPreviousFlaky() => $_has(11);
+  void clearPreviousFlaky() => clearField(12);
+
+  $core.String get previousCommitHash => $_getS(12, '');
+  set previousCommitHash($core.String v) {
+    $_setString(12, v);
+  }
+
+  $core.bool hasPreviousCommitHash() => $_has(12);
+  void clearPreviousCommitHash() => clearField(13);
+
+  $core.int get previousCommitTime => $_get(13, 0);
+  set previousCommitTime($core.int v) {
+    $_setSignedInt32(13, v);
+  }
+
+  $core.bool hasPreviousCommitTime() => $_has(13);
+  void clearPreviousCommitTime() => clearField(14);
+
+  $core.String get previousBuildNumber => $_getS(14, '');
+  set previousBuildNumber($core.String v) {
+    $_setString(14, v);
+  }
+
+  $core.bool hasPreviousBuildNumber() => $_has(14);
+  void clearPreviousBuildNumber() => clearField(15);
+
+  $core.String get previousResult => $_getS(15, '');
+  set previousResult($core.String v) {
+    $_setString(15, v);
+  }
+
+  $core.bool hasPreviousResult() => $_has(15);
+  void clearPreviousResult() => clearField(16);
+
+  $core.bool get changed => $_get(16, false);
+  set changed($core.bool v) {
+    $_setBool(16, v);
+  }
+
+  $core.bool hasChanged() => $_has(16);
+  void clearChanged() => clearField(17);
+
+  $core.String get suite => $_getS(17, '');
+  set suite($core.String v) {
+    $_setString(17, v);
+  }
+
+  $core.bool hasSuite() => $_has(17);
+  void clearSuite() => clearField(100);
+
+  $core.String get testName => $_getS(18, '');
+  set testName($core.String v) {
+    $_setString(18, v);
+  }
+
+  $core.bool hasTestName() => $_has(18);
+  void clearTestName() => clearField(101);
+
+  $core.int get commitTime => $_get(19, 0);
+  set commitTime($core.int v) {
+    $_setSignedInt32(19, v);
+  }
+
+  $core.bool hasCommitTime() => $_has(19);
+  void clearCommitTime() => clearField(102);
+}
diff --git a/current_results/gen/result.pbenum.dart b/current_results/lib/src/generated/result.pbenum.dart
similarity index 99%
rename from current_results/gen/result.pbenum.dart
rename to current_results/lib/src/generated/result.pbenum.dart
index 969d1be..2c06c6b 100644
--- a/current_results/gen/result.pbenum.dart
+++ b/current_results/lib/src/generated/result.pbenum.dart
@@ -3,4 +3,3 @@
 //  source: result.proto
 ///
 // ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name
-
diff --git a/current_results/gen/result.pbjson.dart b/current_results/lib/src/generated/result.pbjson.dart
similarity index 67%
rename from current_results/gen/result.pbjson.dart
rename to current_results/lib/src/generated/result.pbjson.dart
index 8493cd1..34cbe82 100644
--- a/current_results/gen/result.pbjson.dart
+++ b/current_results/lib/src/generated/result.pbjson.dart
@@ -21,12 +21,41 @@
     const {'1': 'build_number', '3': 9, '4': 1, '5': 9, '10': 'buildNumber'},
     const {'1': 'builder_name', '3': 10, '4': 1, '5': 9, '10': 'builderName'},
     const {'1': 'flaky', '3': 11, '4': 1, '5': 8, '10': 'flaky'},
-    const {'1': 'previous_flaky', '3': 12, '4': 1, '5': 8, '10': 'previousFlaky'},
-    const {'1': 'previous_commit_hash', '3': 13, '4': 1, '5': 9, '10': 'previousCommitHash'},
-    const {'1': 'previous_commit_time', '3': 14, '4': 1, '5': 5, '10': 'previousCommitTime'},
-    const {'1': 'previous_build_number', '3': 15, '4': 1, '5': 9, '10': 'previousBuildNumber'},
-    const {'1': 'previous_result', '3': 16, '4': 1, '5': 9, '10': 'previousResult'},
+    const {
+      '1': 'previous_flaky',
+      '3': 12,
+      '4': 1,
+      '5': 8,
+      '10': 'previousFlaky'
+    },
+    const {
+      '1': 'previous_commit_hash',
+      '3': 13,
+      '4': 1,
+      '5': 9,
+      '10': 'previousCommitHash'
+    },
+    const {
+      '1': 'previous_commit_time',
+      '3': 14,
+      '4': 1,
+      '5': 5,
+      '10': 'previousCommitTime'
+    },
+    const {
+      '1': 'previous_build_number',
+      '3': 15,
+      '4': 1,
+      '5': 9,
+      '10': 'previousBuildNumber'
+    },
+    const {
+      '1': 'previous_result',
+      '3': 16,
+      '4': 1,
+      '5': 9,
+      '10': 'previousResult'
+    },
     const {'1': 'changed', '3': 17, '4': 1, '5': 8, '10': 'changed'},
   ],
 };
-
diff --git a/current_results/gen/result.pbserver.dart b/current_results/lib/src/generated/result.pbserver.dart
similarity index 99%
rename from current_results/gen/result.pbserver.dart
rename to current_results/lib/src/generated/result.pbserver.dart
index 41edfc1..08420c3 100644
--- a/current_results/gen/result.pbserver.dart
+++ b/current_results/lib/src/generated/result.pbserver.dart
@@ -5,4 +5,3 @@
 // ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name
 
 export 'result.pb.dart';
-
diff --git a/current_results/lib/src/result.dart b/current_results/lib/src/result.dart
new file mode 100644
index 0000000..eaf8be7
--- /dev/null
+++ b/current_results/lib/src/result.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:current_results/src/generated/result.pb.dart' as api;
+
+class Result {
+  final String name;
+  final String configuration;
+  final String commitHash;
+  final String result;
+  final bool flaky;
+  final String expected;
+  final Duration time;
+
+  Result(this.name, this.configuration, this.commitHash, this.result,
+      this.flaky, this.expected, this.time);
+
+  Result.fromApi(api.Result other)
+      : this(
+            unique(other.name),
+            unique(other.configuration),
+            unique(other.commitHash),
+            unique(other.result),
+            other.flaky,
+            unique(other.expected),
+            Duration(milliseconds: other.timeMs));
+
+  static final uniqueStrings = Set<String>();
+
+  static String unique(String string) =>
+      uniqueStrings.lookup(string) ??
+      (uniqueStrings.add(string) ? string : string);
+
+  Map<String, dynamic> toMap() => {
+        'name': name,
+        'configuration': configuration,
+        'commitHash': commitHash,
+        'result': result,
+        'flaky': flaky,
+        'expected': expected,
+        'time': time,
+      };
+
+  String toString() => toMap().toString();
+}
diff --git a/current_results/lib/src/slice.dart b/current_results/lib/src/slice.dart
new file mode 100644
index 0000000..1174465
--- /dev/null
+++ b/current_results/lib/src/slice.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+import 'package:collection/collection.dart';
+
+import 'package:current_results/src/result.dart';
+import 'package:current_results/src/generated/result.pb.dart' as api;
+
+class Slice {
+  /// The current results, stored separately for each configuration in a
+  /// list sorted by test name.
+  final _stored = <String, List<Result>>{};
+
+  /// A sorted list of all test names seen. Names are not removed from this list.
+  List<String> testNames = [];
+  int _size = 0;
+
+  int get size => _size;
+
+  void add(List<String> lines) {
+    if (lines.isEmpty) return;
+    final results = lines.map((line) => Result.fromApi(api.Result()
+      ..mergeFromProto3Json(json.decode(line),
+          supportNamesWithUnderscores: true)));
+    final configuration = results.first.configuration;
+    if (results.any((result) => result.configuration != configuration)) {
+      print('Loaded results list with multiple configurations: '
+          'first result: ${results.first}');
+      return;
+    }
+    int compareNames(Result a, Result b) => a.name.compareTo(b.name);
+    final sorted = results.toList()..sort(compareNames);
+    _size -= _stored[configuration]?.length ?? 0;
+    _stored[configuration] = sorted;
+    _size += sorted.length;
+    if (!sorted.every(Scanner(testNames).found)) {
+      testNames = merge(testNames, sorted);
+    }
+  }
+
+  List<String> merge(List<String> names, List<Result> sorted) {
+    final result = <String>[];
+    var i = 0;
+    var j = 0;
+    while (i < names.length && j < sorted.length) {
+      final compare = names[i].compareTo(sorted[j].name);
+      if (compare <= 0) {
+        result.add(names[i++]);
+        if (compare == 0) j++;
+      } else {
+        result.add(sorted[j++].name);
+      }
+    }
+    while (i < names.length) {
+      result.add(names[i++]);
+    }
+    while (j < sorted.length) {
+      result.add(sorted[j++].name);
+    }
+    return result;
+  }
+}
+
+class Scanner {
+  List<String> names;
+  int position = 0;
+
+  Scanner(this.names);
+
+  bool found(Result a) {
+    while (position < names.length) {
+      if (names[position].compareTo(a.name) < 0) {
+        position++;
+      } else {
+        return names[position] == a.name;
+      }
+    }
+    return false;
+  }
+}
diff --git a/current_results/pubspec.lock b/current_results/pubspec.lock
index 2e1a23a..2fb970e 100644
--- a/current_results/pubspec.lock
+++ b/current_results/pubspec.lock
@@ -7,21 +7,21 @@
       name: _discoveryapis_commons
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.1.9"
+    version: "0.2.0"
   _fe_analyzer_shared:
     dependency: transitive
     description:
       name: _fe_analyzer_shared
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.0.0"
+    version: "4.0.0"
   analyzer:
     dependency: transitive
     description:
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.39.8"
+    version: "0.39.10"
   args:
     dependency: transitive
     description:
@@ -63,7 +63,7 @@
       name: crypto
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.4"
+    version: "2.1.5"
   csslib:
     dependency: transitive
     description:
@@ -91,7 +91,7 @@
       name: gcloud
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.7.0+1"
+    version: "0.7.2"
   glob:
     dependency: transitive
     description:
@@ -112,7 +112,7 @@
       name: googleapis_auth
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.11+1"
+    version: "0.2.12"
   grpc:
     dependency: "direct main"
     description:
@@ -154,7 +154,7 @@
       name: js
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.6.1+1"
+    version: "0.6.2"
   meta:
     dependency: transitive
     description:
@@ -197,6 +197,13 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.9.0"
+  pool:
+    dependency: "direct main"
+    description:
+      name: pool
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.4.0"
   protobuf:
     dependency: "direct main"
     description:
diff --git a/current_results/pubspec.yaml b/current_results/pubspec.yaml
index 8acc9e9..df22a24 100644
--- a/current_results/pubspec.yaml
+++ b/current_results/pubspec.yaml
@@ -9,6 +9,7 @@
   gcloud: ^0.7.0
   googleapis_auth: ^0.2.11
   grpc: ^2.1.3
+  pool: ^1.4.0
   protobuf: ^1.0.0
 
 dev_dependencies: