Skip to content

Commit

Permalink
new: Add mutex option to task configuration (#1416)
Browse files Browse the repository at this point in the history
* feat: add mutex option to task configuration

Signed-off-by: Andres Correa Casablanca <[email protected]>

* docs: add mutex section to task.options docs

Signed-off-by: Andres Correa Casablanca <[email protected]>

* refactor: remove useless impls

Signed-off-by: Andres Correa Casablanca <[email protected]>

* refactor: use dashmap

Signed-off-by: Andres Correa Casablanca <[email protected]>

* perf: remove allocation

Signed-off-by: Andres Correa Casablanca <[email protected]>

* refactor: fix code style issues

Signed-off-by: Andres Correa Casablanca <[email protected]>

---------

Signed-off-by: Andres Correa Casablanca <[email protected]>
  • Loading branch information
castarco authored and milesj committed Apr 18, 2024
1 parent 97cbfbb commit b636610
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 15 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ clap = { version = "4.5.4", default-features = false, features = [
] }
clap_complete = "4.5.2"
console = "0.15.8"
dashmap = "5.5.3"
dirs = "5.0.1"
miette = "7.2.0"
once_cell = "1.19.0"
Expand Down
2 changes: 2 additions & 0 deletions crates/core/action-context/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ publish = false
moon_common = { path = "../../../nextgen/common" }
moon_target = { path = "../../../nextgen/target" }
clap = { workspace = true }
dashmap = { workspace = true }
relative-path = { workspace = true, features = ["serde"] }
rustc-hash = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
6 changes: 5 additions & 1 deletion crates/core/action-context/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use clap::ValueEnum;
use dashmap::DashMap;
use moon_common::path::WorkspaceRelativePathBuf;
use moon_target::{Target, TargetLocator};
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::{path::PathBuf, sync::Arc};

#[derive(ValueEnum, Clone, Debug, Deserialize, Serialize)]
pub enum ProfileType {
Expand Down Expand Up @@ -33,6 +34,9 @@ pub struct ActionContext {

pub initial_targets: FxHashSet<TargetLocator>,

#[serde(skip)]
pub named_mutexes: DashMap<String, Arc<tokio::sync::Mutex<()>>>,

pub passthrough_args: Vec<String>,

pub primary_targets: FxHashSet<Target>,
Expand Down
39 changes: 27 additions & 12 deletions crates/core/action-pipeline/src/actions/run_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,33 @@ pub async fn run_task(
.insert(target.clone(), TargetState::Passthrough);
}

let attempts_result = if is_cache_enabled {
let context = context.read().await;

runner.create_and_run_command(&context, runtime).await
} else {
// Concurrent long-running tasks will cause a deadlock, as some threads will
// attempt to write to context while others are reading from it, and long-running
// tasks may never release the lock. Unfortuantely we have to clone here to work
// around it, so revisit in the future.
let context = (context.read().await).clone();

runner.create_and_run_command(&context, runtime).await
let attempts_result = {
let _ctx: RwLock<ActionContext>;
let ctx = if is_cache_enabled {
context.read().await
} else {
// Concurrent long-running tasks will cause a deadlock, as some threads will
// attempt to write to context while others are reading from it, and long-running
// tasks may never release the lock. Unfortuantely we have to clone here to work
// around it, so revisit in the future.
_ctx = RwLock::new(context.read().await.clone());
_ctx.read().await
};

if let Some(mutex_name) = &task.options.mutex {
if let Some(named_mutex) = ctx.named_mutexes.get(mutex_name) {
let _guard = named_mutex.lock().await;

runner.create_and_run_command(&ctx, runtime).await
} else {
Result::Err(miette::Report::msg(format!(
"Unable to acquire named mutex \"{}\"",
mutex_name
)))
}
} else {
runner.create_and_run_command(&ctx, runtime).await
}
};

match attempts_result {
Expand Down
4 changes: 4 additions & 0 deletions nextgen/config/src/project/task_options_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ cacheable!(
/// The strategy to use when merging `outputs` with an inherited task.
pub merge_outputs: Option<TaskMergeStrategy>,

/// Creates an exclusive lock on a virtual resource, preventing other
/// tasks using the same resource from running concurrently.
pub mutex: Option<String>,

/// The style in which task output will be printed to the console.
#[setting(env = "MOON_OUTPUT_STYLE")]
pub output_style: Option<TaskOutputStyle>,
Expand Down
4 changes: 4 additions & 0 deletions nextgen/task-builder/src/tasks_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,10 @@ impl<'proj> TasksBuilder<'proj> {
options.merge_outputs = *merge_outputs;
}

if let Some(mutex) = &config.mutex {
options.mutex = Some(mutex.clone());
}

if let Some(output_style) = &config.output_style {
options.output_style = Some(*output_style);
}
Expand Down
3 changes: 3 additions & 0 deletions nextgen/task/src/task_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ cacheable!(

pub merge_outputs: TaskMergeStrategy,

pub mutex: Option<String>,

pub output_style: Option<TaskOutputStyle>,

pub persistent: bool,
Expand Down Expand Up @@ -67,6 +69,7 @@ impl Default for TaskOptions {
merge_env: TaskMergeStrategy::Append,
merge_inputs: TaskMergeStrategy::Append,
merge_outputs: TaskMergeStrategy::Append,
mutex: None,
output_style: None,
persistent: false,
retry_count: 0,
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface TaskOptions {
mergeInputs: TaskMergeStrategy;
mergeOutputs: TaskMergeStrategy;
outputStyle: TaskOutputStyle | null;
mutex: string | null;
persistent: boolean;
retryCount: number;
runDepsInParallel: boolean;
Expand Down
10 changes: 10 additions & 0 deletions packages/types/src/tasks-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ export interface TaskOptionsConfig {
* @default 'append'
*/
mergeOutputs: TaskMergeStrategy | null;
/**
* Creates an exclusive lock on a virtual resource, preventing other tasks
* using the same resource from running concurrently.
*/
mutex: string | null;
/**
* The style in which task output will be printed to the console.
*
Expand Down Expand Up @@ -302,6 +307,11 @@ export interface PartialTaskOptionsConfig {
* @default 'append'
*/
mergeOutputs?: TaskMergeStrategy | null;
/**
* Creates an exclusive lock on a virtual resource, preventing other tasks
* using the same resource from running concurrently.
*/
mutex?: string | null;
/**
* The style in which task output will be printed to the console.
*
Expand Down
25 changes: 25 additions & 0 deletions website/docs/config/project.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,31 @@ The [strategy](../concepts/task-inheritance#merge-strategies) to use when mergin
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`outputs`](#outputs) list with an inherited task. Defaults to "append".

#### `mutex`

<HeadingApiLink to="/api/types/interface/TaskOptionsConfig#mutex" />

Creates an exclusive lock on a "virtual resource", preventing other tasks using the same "virtual
resource" from running concurrently.

If you have many tasks that require exclusive access to a resource that can't be tracked by Moon
(like a database, an ignored file, a file that's not part of the project, or a remote resource) you
can use the `mutex` option to prevent them from running at the same time.

```yaml title="moon.yml" {5}
tasks:
task_a:
# ...
options:
mutex: 'virtual_resource_name'
# task_b doesn't necessarily have to be in the same project
task_b:
# ...
options:
mutex: 'virtual_resource_name'
```

#### `outputStyle`

<HeadingApiLink to="/api/types/interface/TaskOptionsConfig#outputStyle" />
Expand Down
13 changes: 13 additions & 0 deletions website/static/schemas/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,19 @@
],
"markdownDescription": "The strategy to use when merging `outputs` with an inherited task."
},
"mutex": {
"title": "mutex",
"description": "Creates an exclusive lock on a virtual resource, preventing other tasks using the same resource from running concurrently.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"markdownDescription": "Creates an exclusive lock on a virtual resource, preventing other tasks using the same resource from running concurrently."
},
"outputStyle": {
"title": "outputStyle",
"description": "The style in which task output will be printed to the console.",
Expand Down
13 changes: 13 additions & 0 deletions website/static/schemas/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,19 @@
],
"markdownDescription": "The strategy to use when merging `outputs` with an inherited task."
},
"mutex": {
"title": "mutex",
"description": "Creates an exclusive lock on a virtual resource, preventing other tasks using the same resource from running concurrently.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"markdownDescription": "Creates an exclusive lock on a virtual resource, preventing other tasks using the same resource from running concurrently."
},
"outputStyle": {
"title": "outputStyle",
"description": "The style in which task output will be printed to the console.",
Expand Down
9 changes: 7 additions & 2 deletions website/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
{
"extends": ["../tsconfig.options.json", "@docusaurus/tsconfig"],
"extends": [
"../tsconfig.options.json",
"@docusaurus/tsconfig"
],
"compilerOptions": {
"emitDeclarationOnly": true,
"noEmit": false,
"outDir": "../.moon/cache/types/website",
"verbatimModuleSyntax": false,
"baseUrl": ".",
"paths": {
"@site/*": ["./*"]
"@site/*": [
"./*"
]
}
},
"include": [
Expand Down

0 comments on commit b636610

Please sign in to comment.