Compare commits

...

3 Commits

Author SHA1 Message Date
FrozenPandaz 2677d5b0db chore(core): test 2024-09-04 17:07:57 -04:00
FrozenPandaz fdc7ee5d63 fix(core): fix estimate task timing typings 2024-09-04 16:13:09 -04:00
FrozenPandaz 19577a3a02 feat(core): use durations from task history to schedule tasks 2024-09-03 14:41:43 -04:00
11 changed files with 142 additions and 32 deletions
Generated
+27 -24
View File
@@ -177,7 +177,7 @@ version = "0.69.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cexpr",
"clang-sys",
"itertools",
@@ -202,9 +202,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bitvec"
@@ -268,9 +268,12 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.90"
version = "1.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b"
dependencies = [
"shlex",
]
[[package]]
name = "cexpr"
@@ -369,7 +372,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"crossterm_winapi",
"libc",
"mio",
@@ -475,9 +478,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
@@ -710,7 +713,7 @@ version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbd06203b1a9b33a78c88252a625031b094d9e1b647260070c25b09910c0a804"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"bstr",
"gix-path",
"libc",
@@ -758,7 +761,7 @@ version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"bstr",
"gix-features",
"gix-path",
@@ -844,7 +847,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fddc27984a643b20dd03e97790555804f98cf07404e0e552c0ad8133266a79a1"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"gix-path",
"libc",
"windows-sys 0.52.0",
@@ -914,7 +917,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"ignore",
"walkdir",
]
@@ -942,9 +945,9 @@ dependencies = [
[[package]]
name = "hashlink"
version = "0.8.4"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown 0.14.5",
]
@@ -1133,9 +1136,9 @@ dependencies = [
[[package]]
name = "libsqlite3-sys"
version = "0.26.0"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
@@ -1266,7 +1269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a63d0570e4c3e0daf7a8d380563610e159f538e20448d6c911337246f40e84"
dependencies = [
"anyhow",
"bitflags 2.5.0",
"bitflags 2.6.0",
"ctor",
"napi-derive",
"napi-sys",
@@ -1353,7 +1356,7 @@ version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"libc",
]
@@ -1380,7 +1383,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@@ -1890,11 +1893,11 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.29.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
@@ -1920,7 +1923,7 @@ version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -2236,7 +2239,7 @@ version = "0.107.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6528f3dd33e11eae9d7fe9fee4a79d5bbd211c74426ab2eec64dc82bd2eb74d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"is-macro",
"num-bigint",
"scoped-tls",
+1 -1
View File
@@ -34,7 +34,7 @@ nom = '7.1.3'
regex = "1.9.1"
rayon = "1.7.0"
rkyv = { version = "0.7", features = ["validation"] }
rusqlite = { version = "0.29.0", features = ["bundled", "array", "vtab", "wasm32-wasi-vfs"] }
rusqlite = { version = "0.32.1", features = ["bundled", "array", "vtab", "wasm32-wasi-vfs"] }
thiserror = "1.0.40"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+12 -1
View File
@@ -28,7 +28,7 @@ import {
DaemonProjectGraphError,
ProjectGraphError,
} from '../../project-graph/error-types';
import { IS_WASM, NxWorkspaceFiles, TaskRun } from '../../native';
import { IS_WASM, NxWorkspaceFiles, TaskRun, TaskTarget } from '../../native';
import { HandleGlobMessage } from '../message-types/glob';
import {
GET_NX_WORKSPACE_FILES,
@@ -44,7 +44,9 @@ import {
} from '../message-types/get-files-in-directory';
import { HASH_GLOB, HandleHashGlobMessage } from '../message-types/hash-glob';
import {
GET_ESTIMATED_TASK_TIMINGS,
GET_FLAKY_TASKS,
HandleGetEstimatedTaskTimings,
HandleGetFlakyTasks,
HandleRecordTaskRunsMessage,
RECORD_TASK_RUNS,
@@ -354,6 +356,15 @@ export class DaemonClient {
return this.sendToDaemonViaQueue(message);
}
async getEstimatedTaskTimings(targets: TaskTarget[]) {
const message: HandleGetEstimatedTaskTimings = {
type: GET_ESTIMATED_TASK_TIMINGS,
targets,
};
return this.sendToDaemonViaQueue(message);
}
recordTaskRuns(taskRuns: TaskRun[]): Promise<void> {
const message: HandleRecordTaskRunsMessage = {
type: RECORD_TASK_RUNS,
@@ -1,12 +1,18 @@
import type { TaskRun } from '../../native';
import { TaskRun, TaskTarget } from '../../native';
export const GET_FLAKY_TASKS = 'GET_FLAKY_TASKS' as const;
export const GET_ESTIMATED_TASK_TIMINGS = 'GET_ESTIMATED_TASK_TIMINGS' as const;
export type HandleGetFlakyTasks = {
type: typeof GET_FLAKY_TASKS;
hashes: string[];
};
export type HandleGetEstimatedTaskTimings = {
type: typeof GET_ESTIMATED_TASK_TIMINGS;
targets: TaskTarget[];
};
export function isHandleGetFlakyTasksMessage(
message: unknown
): message is HandleGetFlakyTasks {
+1
View File
@@ -41,6 +41,7 @@ export declare class NxTaskHistory {
constructor(db: ExternalObject<Connection>)
recordTaskRuns(taskRuns: Array<TaskRun>): void
getFlakyTasks(hashes: Array<string>): Array<string>
getEstimatedTaskTimings(targets: Array<TaskTarget>): Record<string, number>
}
export declare class RustPseudoTerminal {
-1
View File
@@ -16,5 +16,4 @@ mod machine_id;
pub mod pseudo_terminal;
#[cfg(not(target_arch = "wasm32"))]
pub mod watch;
#[cfg(not(target_arch = "wasm32"))]
pub mod db;
+37 -1
View File
@@ -1,9 +1,12 @@
use std::rc::Rc;
use std::collections::HashMap;
use itertools::Itertools;
use napi::bindgen_prelude::*;
use rusqlite::vtab::array;
use rusqlite::{params, types::Value, Connection};
use crate::native::tasks::types::TaskTarget;
#[napi(object)]
pub struct TaskRun {
pub hash: String,
@@ -94,4 +97,37 @@ impl NxTaskHistory {
.map(|r| r.map_err(anyhow::Error::from))
.collect()
}
#[napi]
pub fn get_estimated_task_timings(&self, targets: Vec<TaskTarget>) -> anyhow::Result<HashMap<String, f64>> {
let values = Rc::new(
targets
.iter()
.map(|t| Value::from(
match &t.configuration {
Some(configuration) => format!("{}:{}:{}", t.project, t.target, configuration),
_ => format!("{}:{}", t.project, t.target)
}
))
.collect::<Vec<Value>>(),
);
self.db
.prepare(
"
SELECT CONCAT_WS(':', project, target, configuration) AS target_string, AVG(end - start) AS duration
FROM task_history
JOIN task_details ON task_history.hash = task_details.hash
WHERE target_string in rarray(?1)
GROUP BY target_string
",
)?
.query_map([values], |row| {
let target_string: String = row.get(0)?;
let duration: f64 = row.get(1)?;
Ok((target_string, duration))
})?
.map(|r| r.map_err(anyhow::Error::from))
.collect()
}
}
@@ -63,7 +63,7 @@ describe('NxTaskHistory', () => {
hash: '123',
code: 0,
status: 'success',
start: Date.now() - 1000 * 60 * 30,
start: Date.now() - 1000 * 60 * 60,
end: Date.now(),
},
{
@@ -82,4 +82,38 @@ describe('NxTaskHistory', () => {
expect(r2).not.toContain('123');
expect(r2).not.toContain('234');
});
it('should get estimated task timings', () => {
taskHistory.recordTaskRuns([
{
hash: '123',
code: 1,
status: 'failure',
start: Date.now() - 1000 * 60 * 60,
end: Date.now(),
},
{
hash: '123',
code: 0,
status: 'success',
start: Date.now() - 1000 * 60 * 60,
end: Date.now(),
},
{
hash: '234',
code: 0,
status: 'success',
start: Date.now() - 1000 * 60 * 60,
end: Date.now(),
},
]);
const r = taskHistory.getEstimatedTaskTimings([
{
project: 'proj',
target: 'build',
configuration: 'production',
},
]);
expect(r['proj:build:production']).toEqual(60 * 60 * 1000);
});
});
@@ -49,6 +49,7 @@ export class TaskOrchestrator {
private tasksSchedule = new TasksSchedule(
this.projectGraph,
this.taskGraph,
null,
this.options
);
@@ -86,7 +87,10 @@ export class TaskOrchestrator {
async run() {
// Init the ForkedProcessTaskRunner
await this.forkedProcessTaskRunner.init();
await Promise.all([
this.forkedProcessTaskRunner.init(),
this.tasksSchedule.init(),
]);
// initial scheduling
await this.scheduleNextTasks();
@@ -9,6 +9,7 @@ import { Task, TaskGraph } from '../config/task-graph';
import { ProjectGraph } from '../config/project-graph';
import { findAllProjectNodeDependencies } from '../utils/project-graph-utils';
import { reverse } from '../project-graph/operators';
import { TaskHistory } from '../utils/task-history';
export interface Batch {
executorName: string;
@@ -24,13 +25,21 @@ export class TasksSchedule {
private runningTasks = new Set<string>();
private completedTasks = new Set<string>();
private scheduleRequestsExecutionChain = Promise.resolve();
private estimatedTaskTimings: Record<string, number>;
constructor(
private readonly projectGraph: ProjectGraph,
private readonly taskGraph: TaskGraph,
private readonly taskHistory: TaskHistory,
private readonly options: DefaultTasksRunnerOptions
) {}
public async init() {
this.estimatedTaskTimings = await this.taskHistory.getEstimatedTaskTimings(
Object.values(this.taskGraph.tasks).map((t) => t.target)
);
}
public async scheduleNextTasks() {
this.scheduleRequestsExecutionChain =
this.scheduleRequestsExecutionChain.then(() => this.scheduleTasks());
+8 -1
View File
@@ -1,11 +1,18 @@
import { daemonClient } from '../daemon/client/client';
import { isOnDaemon } from '../daemon/is-on-daemon';
import { NxTaskHistory, TaskRun } from '../native';
import { NxTaskHistory, TaskRun, TaskTarget } from '../native';
import { getDbConnection } from './db-connection';
export class TaskHistory {
taskHistory = new NxTaskHistory(getDbConnection());
async getEstimatedTaskTimings(targets: TaskTarget[]) {
if (isOnDaemon() || !daemonClient.enabled()) {
return this.taskHistory.getEstimatedTaskTimings(targets);
}
return await daemonClient.getEstimatedTaskTimings(targets);
}
async getFlakyTasks(hashes: string[]) {
if (isOnDaemon() || !daemonClient.enabled()) {
return this.taskHistory.getFlakyTasks(hashes);