Skip to content

Commit b5461a9

Browse files
KapJIfacebook-github-bot
authored andcommitted
Add remote persistent worker support
Differential Revision: D65214397
1 parent c6b2244 commit b5461a9

File tree

40 files changed

+1086
-5
lines changed

40 files changed

+1086
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: build_example_persistent_worker
2+
inputs:
3+
buildbuddyApiKey:
4+
description: "The API key for BuildBuddy remote cache and execution."
5+
required: true
6+
runs:
7+
using: composite
8+
steps:
9+
- name: Build examples/persistent_worker directory
10+
env:
11+
BUILDBUDDY_API_KEY: ${{ inputs.buildbuddyApiKey }}
12+
run: |-
13+
cd examples/persistent_worker
14+
export PATH="$RUNNER_TEMP/artifacts:$PATH"
15+
./test.sh
16+
shell: bash

.github/workflows/build-and-test.yml

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Build and test
22
on:
33
push:
44
pull_request:
5+
workflow_dispatch: # allows manual triggering
56
jobs:
67
linux-build-and-test:
78
runs-on: 4-core-ubuntu
@@ -69,6 +70,9 @@ jobs:
6970
$RUNNER_TEMP/artifacts/buck2 test //... -v 2
7071
- uses: ./.github/actions/build_example_conan
7172
- uses: ./.github/actions/build_example_no_prelude
73+
- uses: ./.github/actions/build_example_persistent_worker
74+
with:
75+
buildbuddyApiKey: ${{ secrets.BUILDBUDDY_API_KEY }}
7276
- uses: ./.github/actions/setup_reindeer
7377
- uses: ./.github/actions/build_bootstrap
7478
windows-build-examples:

app/buck2_action_impl/src/actions/impls/run.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ struct UnpackedWorkerValues<'v> {
255255
exe: &'v dyn CommandLineArgLike,
256256
id: WorkerId,
257257
concurrency: Option<usize>,
258+
remote: bool,
258259
}
259260

260261
struct UnpackedRunActionValues<'v> {
@@ -310,6 +311,7 @@ impl RunAction {
310311
exe: worker.exe_command_line(),
311312
id: WorkerId(worker.id),
312313
concurrency: worker.concurrency(),
314+
remote: worker.remote(),
313315
});
314316

315317
Ok(UnpackedRunActionValues {
@@ -325,6 +327,7 @@ impl RunAction {
325327
&self,
326328
fs: &ExecutorFs,
327329
artifact_visitor: &mut impl CommandLineArtifactVisitor,
330+
actx: &dyn ActionExecutionCtx,
328331
) -> anyhow::Result<(ExpandedCommandLine, Option<WorkerSpec>)> {
329332
let mut ctx = DefaultCommandLineContext::new(fs);
330333
let values = Self::unpack(&self.starlark_values)?;
@@ -341,10 +344,27 @@ impl RunAction {
341344
.exe
342345
.add_to_command_line(&mut worker_rendered, &mut ctx)?;
343346
worker.exe.visit_artifacts(artifact_visitor)?;
347+
let worker_key = if worker.remote {
348+
let mut worker_visitor = SimpleCommandLineArtifactVisitor::new();
349+
worker.exe.visit_artifacts(&mut worker_visitor)?;
350+
if !worker_visitor.outputs.is_empty() {
351+
// TODO[AH] create appropriate error enum value.
352+
return Err(anyhow::anyhow!("remote persistent worker command should not produce an output"));
353+
}
354+
let worker_inputs: Vec<&ArtifactGroupValues> = worker_visitor
355+
.inputs()
356+
.map(|group| actx.artifact_values(group))
357+
.collect();
358+
let (_, worker_digest) = metadata_content(fs.fs(), &worker_inputs, actx.digest_config())?;
359+
Some(worker_digest)
360+
} else {
361+
None
362+
};
344363
Some(WorkerSpec {
345364
exe: worker_rendered,
346365
id: worker.id,
347366
concurrency: worker.concurrency,
367+
remote_key: worker_key,
348368
})
349369
} else {
350370
None
@@ -416,7 +436,7 @@ impl RunAction {
416436
let fs = executor_fs.fs();
417437

418438
let (expanded, worker) =
419-
self.expand_command_line_and_worker(&ctx.executor_fs(), visitor)?;
439+
self.expand_command_line_and_worker(&ctx.executor_fs(), visitor, ctx)?;
420440

421441
// TODO (@torozco): At this point, might as well just receive the list already. Finding
422442
// those things in a HashMap is just not very useful.

app/buck2_build_api/src/actions/execute/action_executor.rs

+1
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ mod tests {
766766
CommandGenerationOptions {
767767
path_separator: PathSeparatorKind::Unix,
768768
output_paths_behavior: Default::default(),
769+
use_remote_persistent_workers: false,
769770
},
770771
Default::default(),
771772
),

app/buck2_build_api/src/interpreter/rule_defs/command_executor_config.rs

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub fn register_command_executor_config(builder: &mut GlobalsBuilder) {
8282
/// * `allow_hybrid_fallbacks_on_failure`: Whether to allow fallbacks when the result is failure (i.e. the command failed on the primary, but the infra worked)
8383
/// * `use_windows_path_separators`: Whether to use Windows path separators in command line arguments
8484
/// * `use_persistent workers`: Whether to use persistent workers for local execution if they are available
85+
/// * `use_remote_persistent_workers`: Whether to use persistent workers for remote execution if they are available
8586
/// * `allow_cache_uploads`: Whether to upload local actions to the RE cache
8687
/// * `max_cache_upload_mebibytes`: Maximum size to upload in cache uploads
8788
/// * `experimental_low_pass_filter`: Whether to use the experimental low pass filter
@@ -106,6 +107,7 @@ pub fn register_command_executor_config(builder: &mut GlobalsBuilder) {
106107
#[starlark(default = false, require = named)] allow_hybrid_fallbacks_on_failure: bool,
107108
#[starlark(default = false, require = named)] use_windows_path_separators: bool,
108109
#[starlark(default = false, require = named)] use_persistent_workers: bool,
110+
#[starlark(default = false, require = named)] use_remote_persistent_workers: bool,
109111
#[starlark(default = false, require = named)] allow_cache_uploads: bool,
110112
#[starlark(default = NoneOr::None, require = named)] max_cache_upload_mebibytes: NoneOr<
111113
i32,
@@ -307,6 +309,7 @@ pub fn register_command_executor_config(builder: &mut GlobalsBuilder) {
307309
PathSeparatorKind::Unix
308310
},
309311
output_paths_behavior,
312+
use_remote_persistent_workers,
310313
},
311314
}
312315
};

app/buck2_build_api/src/interpreter/rule_defs/provider/builtin/worker_info.rs

+11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub struct WorkerInfoGen<V: ValueLifetimeless> {
4545
pub exe: ValueOfUncheckedGeneric<V, FrozenStarlarkCmdArgs>,
4646
// Maximum number of concurrent commands to execute on a worker instance without queuing
4747
pub concurrency: ValueOfUncheckedGeneric<V, NoneOr<usize>>,
48+
// Remote execution capable worker
49+
pub remote: ValueOfUncheckedGeneric<V, bool>,
4850

4951
pub id: u64,
5052
}
@@ -62,6 +64,7 @@ fn worker_info_creator(globals: &mut GlobalsBuilder) {
6264
#[starlark(require = named, default = NoneOr::None)] concurrency: NoneOr<
6365
ValueOf<'v, usize>,
6466
>,
67+
#[starlark(require = named, default = false)] remote: bool,
6568
eval: &mut Evaluator<'v, '_, '_>,
6669
) -> anyhow::Result<WorkerInfo<'v>> {
6770
let heap = eval.heap();
@@ -72,6 +75,7 @@ fn worker_info_creator(globals: &mut GlobalsBuilder) {
7275
exe,
7376
id,
7477
concurrency: heap.alloc_typed_unchecked(concurrency).cast(),
78+
remote: heap.alloc_typed_unchecked(remote).cast(),
7579
})
7680
}
7781
}
@@ -90,6 +94,13 @@ impl<'v, V: ValueLike<'v>> WorkerInfoGen<V> {
9094
.expect("validated at construction")
9195
.into_option()
9296
}
97+
98+
pub fn remote(&self) -> bool {
99+
self.remote
100+
.to_value()
101+
.unpack()
102+
.expect("validated at construction")
103+
}
93104
}
94105

95106
fn validate_worker_info<'v, V>(info: &WorkerInfoGen<V>) -> anyhow::Result<()>

app/buck2_build_api_tests/src/interpreter/rule_defs/provider/builtin/worker_info.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn run_display() {
2424
.run_starlark_bzl_test(
2525
r#"
2626
def test():
27-
assert_eq('WorkerInfo(exe=cmd_args("x"), concurrency=None)', str(WorkerInfo(exe="x")))
27+
assert_eq('WorkerInfo(exe=cmd_args("x"), concurrency=None, remote=False)', str(WorkerInfo(exe="x")))
2828
"#,
2929
)
3030
.unwrap();

app/buck2_core/src/execution_types/executor_config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ impl Default for CacheUploadBehavior {
263263
pub struct CommandGenerationOptions {
264264
pub path_separator: PathSeparatorKind,
265265
pub output_paths_behavior: OutputPathsBehavior,
266+
pub use_remote_persistent_workers: bool,
266267
}
267268

268269
#[derive(Debug, Eq, PartialEq, Hash, Allocative, Clone)]
@@ -294,6 +295,7 @@ impl CommandExecutorConfig {
294295
options: CommandGenerationOptions {
295296
path_separator: PathSeparatorKind::system_default(),
296297
output_paths_behavior: Default::default(),
298+
use_remote_persistent_workers: false,
297299
},
298300
})
299301
}

app/buck2_execute/src/execute/command_executor.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::ops::ControlFlow;
1111
use std::sync::Arc;
1212
use std::time::Duration;
1313

14+
use anyhow::anyhow;
1415
use anyhow::Context;
1516
use buck2_common::file_ops::TrackedFileDigest;
1617
use buck2_core::execution_types::executor_config::CommandGenerationOptions;
@@ -184,15 +185,31 @@ impl CommandExecutor {
184185
}
185186
CommandExecutionInput::ScratchPath(_) => None,
186187
});
188+
let mut platform = self.0.re_platform.clone();
189+
let args = if self.0.options.use_remote_persistent_workers && let Some(worker) = request.worker() && let Some(key) = worker.remote_key.as_ref() {
190+
platform.properties.push(RE::Property {
191+
name: "persistentWorkerKey".to_owned(),
192+
value: key.to_string(),
193+
});
194+
// TODO[AH] Ideally, Buck2 could generate an argfile on the fly.
195+
for arg in request.args() {
196+
if !(arg.starts_with("@") || arg.starts_with("-flagfile") || arg.starts_with("--flagfile")) {
197+
return Err(anyhow!("Remote persistent worker arguments must be passed as `@argfile`, `-flagfile=argfile`, or `--flagfile=argfile`."));
198+
}
199+
}
200+
worker.exe.iter().chain(request.args().iter()).cloned().collect()
201+
} else {
202+
request.all_args_vec()
203+
};
187204
let action = re_create_action(
188-
request.all_args_vec(),
205+
args,
189206
request.paths().output_paths(),
190207
request.working_directory(),
191208
request.env(),
192209
input_digest,
193210
action_metadata_blobs,
194211
request.timeout(),
195-
self.0.re_platform.clone(),
212+
platform,
196213
false,
197214
digest_config,
198215
self.0.options.output_paths_behavior,

app/buck2_execute/src/execute/request.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,12 @@ impl CommandExecutionPaths {
276276
#[derive(Copy, Clone, Dupe, Debug, Display, Allocative, Hash, PartialEq, Eq)]
277277
pub struct WorkerId(pub u64);
278278

279-
#[derive(Clone)]
279+
#[derive(Clone, Debug)]
280280
pub struct WorkerSpec {
281281
pub id: WorkerId,
282282
pub exe: Vec<String>,
283283
pub concurrency: Option<usize>,
284+
pub remote_key: Option<TrackedFileDigest>,
284285
}
285286

286287
/// The data contains the information about the command to be executed.

app/buck2_execute/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![feature(try_trait_v2)]
1515
#![feature(used_with_arg)]
1616
#![feature(trait_upcasting)]
17+
#![feature(let_chains)]
1718

1819
pub mod artifact;
1920
pub mod artifact_utils;

app/buck2_server/src/daemon/common.rs

+1
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ pub fn get_default_executor_config(host_platform: HostPlatformOverride) -> Comma
486486
options: CommandGenerationOptions {
487487
path_separator: get_default_path_separator(host_platform),
488488
output_paths_behavior: Default::default(),
489+
use_remote_persistent_workers: false,
489490
},
490491
}
491492
}

app/buck2_test/src/orchestrator.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,7 @@ impl<'b> BuckTestOrchestrator<'b> {
11711171
options: CommandGenerationOptions {
11721172
path_separator: PathSeparatorKind::system_default(),
11731173
output_paths_behavior: Default::default(),
1174+
use_remote_persistent_workers: false,
11741175
},
11751176
};
11761177
let CommandExecutorResponse {
@@ -1698,6 +1699,7 @@ impl<'a> Execute2RequestExpander<'a> {
16981699
exe: worker_rendered,
16991700
id: WorkerId(worker.id),
17001701
concurrency: worker.concurrency(),
1702+
remote_key: None,
17011703
})
17021704
}
17031705
_ => None,
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[cells]
2+
root = .
3+
prelude = prelude
4+
toolchains = toolchains
5+
none = none
6+
7+
[cell_aliases]
8+
config = prelude
9+
fbcode = none
10+
fbsource = none
11+
buck = none
12+
13+
[external_cells]
14+
prelude = bundled
15+
16+
[parser]
17+
target_platform_detector_spec = target:root//...->prelude//platforms:default
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[buck2]
2+
digest_algorithms = SHA256
3+
4+
[buck2_re_client]
5+
engine_address = grpc://remote.buildbuddy.io
6+
action_cache_address = grpc://remote.buildbuddy.io
7+
cas_address = grpc://remote.buildbuddy.io
8+
tls = true
9+
http_headers = \
10+
x-buildbuddy-api-key:$BUILDBUDDY_API_KEY
11+
12+
[build]
13+
execution_platforms = root//platforms:buildbuddy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[buck2]
2+
digest_algorithms = SHA256
3+
4+
[buck2_re_client]
5+
engine_address = grpc://remote.buildbuddy.io
6+
action_cache_address = grpc://remote.buildbuddy.io
7+
cas_address = grpc://remote.buildbuddy.io
8+
tls = true
9+
http_headers = \
10+
x-buildbuddy-api-key:$BUILDBUDDY_API_KEY
11+
12+
[build]
13+
execution_platforms = root//platforms:buildbuddy-persistent-workers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
execution_platforms = root//platforms:local-persistent-workers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
execution_platforms = root//platforms:local

examples/persistent_worker/.buckroot

Whitespace-only changes.

examples/persistent_worker/.envrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# specify the following:
2+
# - BUILDBUDDY_API_KEY
3+
source_env_if_exists .envrc.private

examples/persistent_worker/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.buckconfig.local
2+
.direnv
3+
.envrc.private
4+
prelude

examples/persistent_worker/BUCK

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
load("defs.bzl", "demo", "worker")
2+
3+
python_binary(
4+
name = "one_shot",
5+
main = "one_shot.py",
6+
)
7+
8+
python_binary(
9+
name = "worker_py",
10+
main = "persistent_worker.py",
11+
deps = [
12+
"//proto/bazel:worker_protocol_pb2",
13+
"//proto/buck2:worker_pb2",
14+
],
15+
)
16+
17+
worker(
18+
name = "worker",
19+
worker = ":worker_py",
20+
visibility = ["PUBLIC"],
21+
)
22+
23+
[
24+
demo(name = "demo-" + str(i))
25+
for i in range(4)
26+
]

0 commit comments

Comments
 (0)