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
11 changes: 11 additions & 0 deletions app_dart/lib/src/request_handlers/scheduler/backfill_grid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@ final class BackfillTask {
/// The LUCI scheduling priority of backfilling this task.
final int priority;

/// Creates a copy of `this` with the provided properties replaced.
@useResult
BackfillTask copyWith({int? priority}) {
return BackfillTask._from(
task,
target: target,
commit: commit,
priority: priority ?? this.priority,
);
}

@override
String toString() {
return 'BackfillTask ${const JsonEncoder.withIndent(' ').convert({
Expand Down
31 changes: 29 additions & 2 deletions app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,26 @@ final class BatchBackfiller extends RequestHandler {
Future<Response> get(Request request) async {
await _backfillReleaseBranch(Config.flutterSlug);
await Future.forEach(config.supportedRepos, _backfillDefaultBranch);
await _backfillExperimentalBranch(Config.flutterSlug);
return Response.emptyOk;
}

// TODO(matanlurey): Remove or make a formal supported feature.
//
// Hardcodes running a branch named `ios-experimental`, which is being tried
// by the iOS team to do MacOS 15.5 validation. It will appear similar to
// any other branch, but use lower-priority scheduling.
//
// See https://github.com/flutter/flutter/issues/168738.
Future<void> _backfillExperimentalBranch(RepositorySlug slug) async {
final fsGrid = await _firestore.queryRecentCommitsAndTasks(
slug,
commitLimit: config.backfillerCommitLimit,
branch: 'ios-experimental',
);
await _doBackfillFrom(slug, fsGrid, forceLowPriority: true);
}

Future<void> _backfillReleaseBranch(RepositorySlug slug) async {
log.debug('Running release branch backfiller for "$slug"');
final branches = await _branchService.getReleaseBranches(slug: slug);
Expand Down Expand Up @@ -82,8 +99,9 @@ final class BatchBackfiller extends RequestHandler {

Future<void> _doBackfillFrom(
RepositorySlug slug,
List<CommitAndTasks> fsGrid,
) async {
List<CommitAndTasks> fsGrid, {
bool forceLowPriority = false,
}) async {
if (fsGrid.isEmpty) {
log.warn('No commits to backfill');
return;
Expand Down Expand Up @@ -141,6 +159,15 @@ final class BatchBackfiller extends RequestHandler {
'Backfilling ${toBackfillTasks.length} tasks (pruned from $beforePruning)',
);

if (forceLowPriority) {
log.debug('Overriding ${toBackfillTasks.length} tasks to low priority');
for (var i = 0; i < toBackfillTasks.length; i++) {
toBackfillTasks[i] = toBackfillTasks[i].copyWith(
priority: LuciBuildService.kBackfillPriority,
);
}
}

// Update the database first before we schedule builds.
await _updateFirestore(toBackfillTasks, grid.skippableTasks);
log.info('Wrote updates to ${toBackfillTasks.length} tasks for backfill');
Expand Down
22 changes: 22 additions & 0 deletions app_dart/test/request_handlers/scheduler/backfill_grid_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,26 @@ void main() {
]),
);
});

test('can override BackfillTask priority after creation', () {
final commit = generateFirestoreCommit(1).toRef();
final task = generateFirestoreTask(2, commitSha: commit.sha).toRef();
final target = generateTarget(1, name: task.name);

final grid = BackfillGrid.from(
[
(commit, [task]),
],
tipOfTreeTargets: [target],
);

expect(
grid.createBackfillTask(task, priority: 1001).copyWith(priority: 1002),
isBackfillTask
.hasTask(task)
.hasTarget(target)
.hasCommit(commit)
.hasPriority(1002),
);
});
}
85 changes: 71 additions & 14 deletions app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
import 'package:cocoon_common/rpc_model.dart' as rpc;
import 'package:cocoon_common/task_status.dart';
import 'package:cocoon_server_test/test_logging.dart';
import 'package:cocoon_service/src/model/commit_ref.dart';
import 'package:cocoon_service/src/request_handlers/scheduler/backfill_grid.dart';
import 'package:cocoon_service/src/request_handlers/scheduler/backfill_strategy.dart';
import 'package:cocoon_service/src/request_handlers/scheduler/batch_backfiller.dart';
import 'package:cocoon_service/src/service/config.dart';
import 'package:cocoon_service/src/service/luci_build_service.dart';
import 'package:cocoon_service/src/service/luci_build_service/pending_task.dart';
import 'package:collection/collection.dart';
import 'package:github/github.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

import '../../src/fake_config.dart';
import '../../src/request_handling/fake_pubsub.dart';
import '../../src/request_handling/request_handler_tester.dart';
import '../../src/service/fake_ci_yaml_fetcher.dart';
import '../../src/service/fake_firestore_service.dart';
import '../../src/service/fake_luci_build_service.dart';
import '../../src/utilities/entity_generators.dart';
import '../../src/utilities/mocks.mocks.dart';

Expand All @@ -31,11 +31,10 @@ void main() {
late BatchBackfiller handler;

// Dependencies.
late FakePubSub pubSub;
late MockGithubChecksUtil mockGithubChecksUtil;
late FakeConfig config;
late FakeFirestoreService firestore;
late FakeCiYamlFetcher ciYamlFetcher;
late _FakeLuciBuildService fakeLuciBuildService;

// Used to implement BranchService.getBranches.
late List<rpc.Branch>? branchesForRepository;
Expand All @@ -44,22 +43,14 @@ void main() {
late RequestHandlerTester tester;

setUp(() {
pubSub = FakePubSub();
mockGithubChecksUtil = MockGithubChecksUtil();
firestore = FakeFirestoreService();
config = FakeConfig(
backfillerCommitLimitValue: 10,
backfillerTargetLimitValue: 100,
supportedReposValue: {Config.flutterSlug},
);
ciYamlFetcher = FakeCiYamlFetcher();

final luciBuildService = FakeLuciBuildService(
config: config,
pubsub: pubSub,
githubChecksUtil: mockGithubChecksUtil,
firestore: firestore,
);
fakeLuciBuildService = _FakeLuciBuildService();

final branchService = MockBranchService();
branchesForRepository = [];
Expand All @@ -79,7 +70,7 @@ void main() {
handler = BatchBackfiller(
config: config,
ciYamlFetcher: ciYamlFetcher,
luciBuildService: luciBuildService,
luciBuildService: fakeLuciBuildService,
backfillerStrategy: const _NaiveBackfillStrategy(),
firestore: firestore,
branchService: branchService,
Expand Down Expand Up @@ -362,6 +353,59 @@ void main() {
]);
// dart format on
});

// https://github.com/flutter/flutter/issues/168738
test('schedules low-priority targets for "ios-experimental"', () async {
branchesForRepository = [
// Intentionally left blank.
];

// dart format off
await fillStorageAndSetCiYaml([
[$N, $I, $F, $S],
[$N, $N, $N, $N],
], branch: 'ios-experimental');
// dart format on

// BEFORE:
expect(
await visualizeFirestoreGrid(commits: 2, branch: 'ios-experimental'),
// dart format off
[
'🧑‍💼 ⬜ 🟨 🟥 🟩',
'🧑‍💼 ⬜ ⬜ ⬜ ⬜',
],
// dart format on
);

await tester.get(handler);

// AFTER:
expect(
await visualizeFirestoreGrid(commits: 2, branch: 'ios-experimental'),
// dart format off
[
'🧑‍💼 🟨 🟨 🟥 🟩',
'🧑‍💼 ⬜ 🟨 🟨 🟨',
],
// dart format on
);

expect(
fakeLuciBuildService.scheduledPostsubmitBuilds,
allOf(
hasLength(4),
everyElement(
isA<PendingTask>().having(
(t) => t.priority,
'priority',
LuciBuildService.kBackfillPriority,
),
),
),
reason: 'Should use a low priority for all executions',
);
});
}

/// A very hermetic but dumb backfilling algorithm.
Expand All @@ -385,3 +429,16 @@ final class _NaiveBackfillStrategy extends BackfillStrategy {
];
}
}

final class _FakeLuciBuildService extends Fake implements LuciBuildService {
final scheduledPostsubmitBuilds = <PendingTask>[];

@override
Future<List<PendingTask>> schedulePostsubmitBuilds({
required CommitRef commit,
required List<PendingTask> toBeScheduled,
}) async {
scheduledPostsubmitBuilds.addAll(toBeScheduled);
return [];
}
}